commit 329e058e1b318e9133ac5a5aede6e92a74c3dddf Author: Tickbase Date: Sat May 17 21:08:01 2025 +0200 Initial commit diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dcaeeff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,43 @@ +--- +name: Bug Report +about: Create a report to help improve CreamLinux +title: '[BUG] ' +labels: bug +assignees: '' +--- + +## Bug Description +A clear and concise description of what the bug is. + +## Steps To Reproduce +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +## Expected Behavior +A clear and concise description of what you expected to happen. + +## Screenshots +If applicable, add screenshots to help explain your problem. + +## System Information + - OS: [e.g. Ubuntu 22.04, Arch Linux, etc.] + - Desktop Environment: [e.g. GNOME, KDE, etc.] + - CreamLinux Version: [e.g. 0.1.0] + - Steam Version: [e.g. latest] + +## Game Information +- Game name: +- Game ID (if known): +- Native Linux or Proton: +- Steam installation path: + +## Additional Context +Add any other context about the problem here. + +## Logs +If possible, include the contents of `~/.cache/creamlinux/creamlinux.log` or attach the file. +``` +Paste log content here +``` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..4bc4ab3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature Request +about: Suggest an idea for CreamLinux +title: '[FEATURE] ' +labels: enhancement +assignees: '' +--- + +## Feature Description +A clear and concise description of what you want to happen. + +## Problem This Feature Solves +Is your feature request related to a problem? Please describe. +Ex. I'm always frustrated when [...] + +## Alternatives You've Considered +A clear and concise description of any alternative solutions or features you've considered. + +## Additional Context +Add any other context or screenshots about the feature request here. + +## Implementation Ideas (Optional) +If you have any ideas on how this feature could be implemented, please share them here. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ac8e8b2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,55 @@ +name: "Build CreamLinux" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build-tauri: + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest] + + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 19 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.77.2 + override: true + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf + + - name: Install frontend dependencies + run: npm install + + - name: Run ESLint + run: npm run lint + + - name: Build the app + run: npm run tauri build + + - name: Upload binary artifacts + uses: actions/upload-artifact@v3 + with: + name: creamlinux-${{ runner.os }} + path: | + src-tauri/target/release/creamlinux + src-tauri/target/release/bundle/deb/*.deb + src-tauri/target/release/bundle/appimage/*.AppImage 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/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..43f4b47 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 [Your Name] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..41bb944 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# CreamLinux + +CreamLinux is a GUI application for Linux that simplifies the management of DLC in Steam games. It provides a user-friendly interface to install and configure CreamAPI (for native Linux games) and SmokeAPI (for Windows games running through Proton). + +![Screenshot](./src/assets/screenshot.png) + +## Features + +- **Auto-discovery**: Automatically finds Steam games installed on your system +- **Native support**: Installs CreamLinux for native Linux games +- **Proton support**: Installs SmokeAPI for Windows games running through Proton +- **DLC management**: Easily select which DLCs to enable +- **Modern UI**: Clean, responsive interface that's easy to use + +## Installation + +### AppImage (Recommended) + +1. Download the latest `CreamLinux.AppImage` from the [Releases](https://github.com/yourusername/creamlinux/releases) page +2. Make it executable: + ```bash + chmod +x CreamLinux.AppImage + ``` +3. Run it: + ```bash + ./CreamLinux.AppImage + ``` + +### Building from Source + +#### Prerequisites +- Rust 1.77.2 or later +- Node.js 18 or later +- npm or yarn + +#### Steps +1. Clone the repository: + ```bash + git clone https://github.com/yourusername/creamlinux.git + cd creamlinux + ``` + +2. Install dependencies: + ```bash + npm install # or yarn + ``` + +3. Build the application: + ```bash + NO_STRIP=true npm run tauri build + ``` + +4. The compiled binary will be available in `src-tauri/target/release/creamlinux` + +### Desktop Integration + +If you're using the AppImage version, you can integrate it into your desktop environment: + +1. Create a desktop entry file: + ```bash + mkdir -p ~/.local/share/applications + ``` + +2. Create `~/.local/share/applications/creamlinux.desktop` with the following content (adjust the path to your AppImage): + ``` + [Desktop Entry] + Name=Creamlinux + Exec=/absolute/path/to/CreamLinux.AppImage + Icon=/absolute/path/to/creamlinux-icon.png + Type=Application + Categories=Game;Utility; + Comment=DLC Manager for Steam games on Linux + ``` + +3. Update your desktop database so creamlinux appears in your app launcher: +```bash +update-desktop-database ~/.local/share/applications +``` + +## Troubleshooting + +### Common Issues + +- **Game doesn't load**: Make sure the launch options are correctly set in Steam +- **DLCs not showing up**: Try refreshing the game list and reinstalling +- **Cannot find Steam**: Ensure Steam is installed and you've launched it at least once + +### Debug Logs + +Logs are stored at: `~/.cache/creamlinux/creamlinux.log` + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +### Development Setup + +1. Clone this repository +2. Install dependencies: + ```bash + npm install + ``` +3. Start the development server: + ```bash + npm run tauri dev + ``` + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Credits + +- [Creamlinux](https://github.com/anticitizn/creamlinux) - Native DLC support +- [SmokeAPI](https://github.com/acidicoala/SmokeAPI) - Proton support +- [Tauri](https://tauri.app/) - Framework for building the desktop application +- [React](https://reactjs.org/) - UI library \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/index.html b/index.html new file mode 100644 index 0000000..69eb28e --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Creamlinux + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..379a198 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4044 @@ +{ + "name": "creamlinux", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "creamlinux", + "version": "0.1.0", + "dependencies": { + "@tauri-apps/api": "^2.5.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@tauri-apps/cli": "^2.5.0", + "@types/node": "^20.10.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "sass-embedded": "^1.86.3", + "typescript": "~5.7.2", + "typescript-eslint": "^8.26.1", + "vite": "^6.3.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "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.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.5.tgz", + "integrity": "sha512-/g5EzJifw5GF8aren8wZ/G5oMuPoGeS6MQD3ca8ddcvdXR5UELUfdTZITCGNhNXynY/AYl3Z4plmxdj/tRl/hQ==", + "dev": true, + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "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/eslint-utils/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/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "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/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tauri-apps/api": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.5.0.tgz", + "integrity": "sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.5.0.tgz", + "integrity": "sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.5.0", + "@tauri-apps/cli-darwin-x64": "2.5.0", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", + "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", + "@tauri-apps/cli-linux-arm64-musl": "2.5.0", + "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-gnu": "2.5.0", + "@tauri-apps/cli-linux-x64-musl": "2.5.0", + "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", + "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", + "@tauri-apps/cli-win32-x64-msvc": "2.5.0" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz", + "integrity": "sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.5.0.tgz", + "integrity": "sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.5.0.tgz", + "integrity": "sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.5.0.tgz", + "integrity": "sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.5.0.tgz", + "integrity": "sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.5.0.tgz", + "integrity": "sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.5.0.tgz", + "integrity": "sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.5.0.tgz", + "integrity": "sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "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.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.17.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", + "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", + "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@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" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "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/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-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/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/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/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "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.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "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": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-builder": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz", + "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==", + "dev": true, + "license": "MIT/X11" + }, + "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/caniuse-lite": { + "version": "1.0.30001714", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz", + "integrity": "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==", + "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/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/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/colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "dev": true, + "license": "MIT" + }, + "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/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/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "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/electron-to-chromium": { + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "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": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "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 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "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.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.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": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "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/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/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/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": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-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/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/immutable": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz", + "integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==", + "dev": true, + "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/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-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-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/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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "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/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/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/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/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/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.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "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/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/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-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/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/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "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.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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/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": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "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/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/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.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/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sass-embedded": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.86.3.tgz", + "integrity": "sha512-3pZSp24ibO1hdopj+W9DuiWsZOb2YY6AFRo/jjutKLBkqJGM1nJjXzhAYfzRV+Xn5BX1eTI4bBTE09P0XNHOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bufbuild/protobuf": "^2.0.0", + "buffer-builder": "^0.2.0", + "colorjs.io": "^0.5.0", + "immutable": "^5.0.2", + "rxjs": "^7.4.0", + "supports-color": "^8.1.1", + "sync-child-process": "^1.0.2", + "varint": "^6.0.0" + }, + "bin": { + "sass": "dist/bin/sass.js" + }, + "engines": { + "node": ">=16.0.0" + }, + "optionalDependencies": { + "sass-embedded-android-arm": "1.86.3", + "sass-embedded-android-arm64": "1.86.3", + "sass-embedded-android-ia32": "1.86.3", + "sass-embedded-android-riscv64": "1.86.3", + "sass-embedded-android-x64": "1.86.3", + "sass-embedded-darwin-arm64": "1.86.3", + "sass-embedded-darwin-x64": "1.86.3", + "sass-embedded-linux-arm": "1.86.3", + "sass-embedded-linux-arm64": "1.86.3", + "sass-embedded-linux-ia32": "1.86.3", + "sass-embedded-linux-musl-arm": "1.86.3", + "sass-embedded-linux-musl-arm64": "1.86.3", + "sass-embedded-linux-musl-ia32": "1.86.3", + "sass-embedded-linux-musl-riscv64": "1.86.3", + "sass-embedded-linux-musl-x64": "1.86.3", + "sass-embedded-linux-riscv64": "1.86.3", + "sass-embedded-linux-x64": "1.86.3", + "sass-embedded-win32-arm64": "1.86.3", + "sass-embedded-win32-ia32": "1.86.3", + "sass-embedded-win32-x64": "1.86.3" + } + }, + "node_modules/sass-embedded-android-arm": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.86.3.tgz", + "integrity": "sha512-UyeXrFzZSvrGbvrWUBcspbsbivGgAgebLGJdSqJulgSyGbA6no3DWQ5Qpdd6+OAUC39BlpPu74Wx9s4RrVuaFw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-arm64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.86.3.tgz", + "integrity": "sha512-q+XwFp6WgAv+UgnQhsB8KQ95kppvWAB7DSoJp+8Vino8b9ND+1ai3cUUZPE5u4SnLZrgo5NtrbPvN5KLc4Pfyg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-ia32": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.86.3.tgz", + "integrity": "sha512-gTJjVh2cRzvGujXj5ApPk/owUTL5SiO7rDtNLrzYAzi1N5HRuLYXqk3h1IQY3+eCOBjGl7mQ9XyySbJs/3hDvg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-riscv64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.86.3.tgz", + "integrity": "sha512-Po3JnyiCS16kd6REo1IMUbFGYtvL9O0rmKaXx5vOuBaJD1LPy2LiSSp7TU7wkJ9IxsTDGzFaSeP1I9qb6D8VVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-android-x64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.86.3.tgz", + "integrity": "sha512-+7h3jdDv/0kUFx0BvxYlq2fa7CcHiDPlta6k5OxO5K6jyqJwo9hc0Z052BoYEauWTqZ+vK6bB5rv2BIzq4U9nA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-arm64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.86.3.tgz", + "integrity": "sha512-EgLwV4ORm5Hr0DmIXo0Xw/vlzwLnfAiqD2jDXIglkBsc5czJmo4/IBdGXOP65TRnsgJEqvbU3aQhuawX5++x9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-darwin-x64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.86.3.tgz", + "integrity": "sha512-dfKhfrGPRNLWLC82vy/vQGmNKmAiKWpdFuWiePRtg/E95pqw+sCu6080Y6oQLfFu37Iq3MpnXiSpDuSo7UnPWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.86.3.tgz", + "integrity": "sha512-+fVCIH+OR0SMHn2NEhb/VfbpHuUxcPtqMS34OCV3Ka99LYZUJZqth4M3lT/ppGl52mwIVLNYzR4iLe6mdZ6mYA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-arm64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.86.3.tgz", + "integrity": "sha512-tYq5rywR53Qtc+0KI6pPipOvW7a47ETY69VxfqI9BR2RKw2hBbaz0bIw6OaOgEBv2/XNwcWb7a4sr7TqgkqKAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-ia32": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.86.3.tgz", + "integrity": "sha512-CmQ5OkqnaeLdaF+bMqlYGooBuenqm3LvEN9H8BLhjkpWiFW8hnYMetiqMcJjhrXLvDw601KGqA5sr/Rsg5s45g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.86.3.tgz", + "integrity": "sha512-SEm65SQknI4pl+mH5Xf231hOkHJyrlgh5nj4qDbiBG6gFeutaNkNIeRgKEg3cflXchCr8iV/q/SyPgjhhzQb7w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-arm64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.86.3.tgz", + "integrity": "sha512-4zOr2C/eW89rxb4ozTfn7lBzyyM5ZigA1ZSRTcAR26Qbg/t2UksLdGnVX9/yxga0d6aOi0IvO/7iM2DPPRRotg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-ia32": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.86.3.tgz", + "integrity": "sha512-84Tcld32LB1loiqUvczWyVBQRCChm0wNLlkT59qF29nxh8njFIVf9yaPgXcSyyjpPoD9Tu0wnq3dvVzoMCh9AQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-riscv64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.86.3.tgz", + "integrity": "sha512-IxEqoiD7vdNpiOwccybbV93NljBy64wSTkUOknGy21SyV43C8uqESOwTwW9ywa3KufImKm8L3uQAW/B0KhJMWg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-musl-x64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.86.3.tgz", + "integrity": "sha512-ePeTPXUxPK6JgHcUfnrkIyDtyt+zlAvF22mVZv6y1g/PZFm1lSfX+Za7TYHg9KaYqaaXDiw6zICX4i44HhR8rA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-riscv64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.86.3.tgz", + "integrity": "sha512-NuXQ72dwfNLe35E+RaXJ4Noq4EkFwM65eWwCwxEWyJO9qxOx1EXiCAJii6x8kkOh5daWuMU0VAI1B9RsJaqqQQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-linux-x64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.86.3.tgz", + "integrity": "sha512-t8be9zJ5B82+og9bQmIQ83yMGYZMTMrlGA+uGWtYacmwg6w3093dk91Fx0YzNSZBp3Tk60qVYjCZnEIwy60x0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-arm64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.86.3.tgz", + "integrity": "sha512-4ghuAzjX4q8Nksm0aifRz8hgXMMxS0SuymrFfkfJlrSx68pIgvAge6AOw0edoZoe0Tf5ZbsWUWamhkNyNxkTvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-ia32": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.86.3.tgz", + "integrity": "sha512-tCaK4zIRq9mLRPxLzBAdYlfCuS/xLNpmjunYxeWkIwlJo+k53h1udyXH/FInnQ2GgEz0xMXyvH3buuPgzwWYsw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded-win32-x64": { + "version": "1.86.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.86.3.tgz", + "integrity": "sha512-zS+YNKfTF4SnOfpC77VTb0qNZyTXrxnAezSoRV0xnw6HlY+1WawMSSB6PbWtmbvyfXNgpmJUttoTtsvJjRCucg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-embedded/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "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/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/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/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/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/sync-child-process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", + "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sync-message-port": "^1.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/sync-message-port": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", + "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "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-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "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/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz", + "integrity": "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/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/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/package.json b/package.json new file mode 100644 index 0000000..82dbef1 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "name": "creamlinux", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2.5.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@tauri-apps/cli": "^2.5.0", + "@types/node": "^20.10.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "sass-embedded": "^1.86.3", + "typescript": "~5.7.2", + "typescript-eslint": "^8.26.1", + "vite": "^6.3.1" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..502406b --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +/gen/schemas diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock new file mode 100644 index 0000000..b586b93 --- /dev/null +++ b/src-tauri/Cargo.lock @@ -0,0 +1,6144 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f39be698127218cca460cb624878c9aa4e2b47dba3b277963d2bf00bad263b" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "app" +version = "0.1.0" +dependencies = [ + "bincode", + "futures", + "log", + "log4rs", + "num_cpus", + "parking_lot", + "regex", + "reqwest 0.11.27", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-dialog", + "tauri-plugin-fs", + "tauri-plugin-log", + "tauri-plugin-shell", + "tempfile", + "tokio", + "walkdir", + "xdg", + "zip", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ashpd" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.1", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +dependencies = [ + "objc2 0.6.0", +] + +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byte-unit" +version = "5.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cd29c3c585209b0cbc7309bfe3ed7efd8c84c21b7af29c8bfae908f8777174" +dependencies = [ + "rust_decimal", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.9.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "cargo_toml" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.100", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.100", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.100", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.100", +] + +[[package]] +name = "destructure_traitobject" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.59.0", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] +name = "embed-resource" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml", + "vswhom", + "winreg 0.52.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.9.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.9.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.15", +] + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.15", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.3.1", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa 1.0.15", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "httparse", + "itoa 1.0.15", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.6.0", + "libc", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.9.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +dependencies = [ + "serde", + "value-bag", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6" +dependencies = [ + "anyhow", + "arc-swap", + "chrono", + "derivative", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "once_cell", + "parking_lot", + "rand 0.8.5", + "serde", + "serde-value", + "serde_json", + "serde_yaml", + "thiserror 1.0.69", + "thread-id", + "typemap-ors", + "winapi", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "muda" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "once_cell", + "png", + "serde", + "thiserror 2.0.12", + "windows-sys 0.59.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.9.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" +dependencies = [ + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.9.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" +dependencies = [ + "bitflags 2.9.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" +dependencies = [ + "bitflags 2.9.0", + "block2 0.6.0", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "dunce", + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openssl" +version = "0.10.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "os_pipe" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plist" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +dependencies = [ + "base64 0.22.1", + "indexmap 2.9.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.24", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.12", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfd" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +dependencies = [ + "ashpd", + "block2 0.6.0", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.100", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" +dependencies = [ + "erased-serde", + "serde", + "typeid", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa 1.0.15", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.15", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.9.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.9.0", + "itoa 1.0.15", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared_child" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +dependencies = [ + "bytemuck", + "cfg_aliases", + "core-graphics", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", + "raw-window-handle", + "redox_syscall", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" +dependencies = [ + "bitflags 2.9.0", + "core-foundation 0.10.0", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-foundation 0.3.0", + "once_cell", + "parking_lot", + "raw-window-handle", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be03adf68fba02f87c4653da7bd73f40b0ecf9c6b7c2c39830f6981d0651912f" +dependencies = [ + "anyhow", + "bytes", + "dirs", + "dunce", + "embed_plist", + "futures-util", + "getrandom 0.2.15", + "glob", + "gtk", + "heck 0.5.0", + "http 1.3.1", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-foundation 0.3.0", + "objc2-ui-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest 0.12.15", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.12", + "tokio", + "tray-icon", + "url", + "urlpattern", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "toml", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.100", + "tauri-utils", + "thiserror 2.0.12", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.100", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils", + "toml", + "walkdir", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcaf6e5d6062423a0f711a23c2a573ccba222b6a16a9322d8499928f27e41376" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.12", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88371e340ad2f07409a3b68294abe73f20bc9c1bc1b631a31dc37a3d0161f682" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.12", + "toml", + "url", + "uuid", +] + +[[package]] +name = "tauri-plugin-log" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2341d5b9bc5318c8e34f35a569140c78337241aa9c14091550b424c49f0314e0" +dependencies = [ + "android_logger", + "byte-unit", + "fern", + "log", + "objc2 0.6.0", + "objc2-foundation 0.3.0", + "serde", + "serde_json", + "serde_repr", + "swift-rs", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d5eb3368b959937ad2aeaf6ef9a8f5d11e01ffe03629d3530707bbcb27ff5d" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", + "tokio", +] + +[[package]] +name = "tauri-runtime" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http 1.3.1", + "jni", + "objc2 0.6.0", + "objc2-ui-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.12", + "url", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" +dependencies = [ + "gtk", + "http 1.3.1", + "jni", + "log", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-foundation 0.3.0", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "html5ever", + "http 1.3.1", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.12", + "toml", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0" +dependencies = [ + "embed-resource", + "toml", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "thread-id" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa 1.0.15", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.44.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.24", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.9.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.9.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap 2.9.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.7.6", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.0", + "once_cell", + "png", + "serde", + "thiserror 2.0.12", + "windows-sys 0.59.0", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typemap-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867" +dependencies = [ + "unsafe-any-ors", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unsafe-any-ors" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad" +dependencies = [ + "destructure_traitobject", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom 0.3.2", + "serde", +] + +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.100", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "webview2-com-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" +dependencies = [ + "thiserror 2.0.12", + "windows", + "windows-core", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result", + "windows-strings 0.3.1", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-version" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wry" +version = "0.51.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" +dependencies = [ + "base64 0.22.1", + "block2 0.6.0", + "cookie", + "crossbeam-channel", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http 1.3.1", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "objc2 0.6.0", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.12", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xdg" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" +dependencies = [ + "async-broadcast", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.7.6", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.100", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" +dependencies = [ + "serde", + "static_assertions", + "winnow 0.7.6", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "zvariant" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "winnow 0.7.6", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" +dependencies = [ + "proc-macro-crate 3.3.0", + "proc-macro2", + "quote", + "syn 2.0.100", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "static_assertions", + "syn 2.0.100", + "winnow 0.7.6", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..7cf946e --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "app" +version = "0.1.0" +description = "DLC Manager for Steam games on Linux" +authors = ["you"] +license = "" +repository = "" +edition = "2021" +rust-version = "1.77.2" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + +[build-dependencies] +tauri-build = { version = "2.2.0", features = [] } + +[dependencies] +serde_json = { version = "1.0", features = ["raw_value"] } +serde = { version = "1.0", features = ["derive"] } +bincode = "1.3" +regex = "1" +xdg = "2" +log = "0.4" +log4rs = "1.2" +reqwest = { version = "0.11", features = ["json"] } +tokio = { version = "1", features = ["full"] } +zip = "0.6" +tempfile = "3.8" +walkdir = "2.3" +parking_lot = "0.12" +tauri = { version = "2.5.0", features = [] } +tauri-plugin-log = "2.0.0-rc" +tauri-plugin-shell = "2.0.0-rc" +tauri-plugin-dialog = "2.0.0-rc" +tauri-plugin-fs = "2.0.0-rc" +num_cpus = "1.16.0" +futures = "0.3.31" + +[features] +# this feature is used for production builds or when `devPath` points to the filesystem +# DO NOT REMOVE!! +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..795b9b7 --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..c135d7f --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,11 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "enables the default permissions", + "windows": [ + "main" + ], + "permissions": [ + "core:default" + ] +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..af02b81 Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..3309e41 Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..c978b79 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/cache.rs b/src-tauri/src/cache.rs new file mode 100644 index 0000000..e2a19df --- /dev/null +++ b/src-tauri/src/cache.rs @@ -0,0 +1,176 @@ +// src/cache.rs + +use serde::{Serialize, Deserialize}; +use serde_json::json; +use std::path::{PathBuf}; +use std::fs; +use std::io; +use std::time::{SystemTime}; +use log::{info, warn}; +use crate::dlc_manager::DlcInfoWithState; + +// Cache entry with timestamp for expiration +#[derive(Serialize, Deserialize)] +struct CacheEntry { + data: T, + timestamp: u64, // Unix timestamp in seconds +} + +// Get the cache directory +fn get_cache_dir() -> io::Result { + let xdg_dirs = xdg::BaseDirectories::with_prefix("creamlinux") + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + let cache_dir = xdg_dirs.get_cache_home(); + + // Make sure the cache directory exists + if !cache_dir.exists() { + fs::create_dir_all(&cache_dir)?; + } + + Ok(cache_dir) +} + +// Save data to cache file +pub fn save_to_cache(key: &str, data: &T, _ttl_hours: u64) -> io::Result<()> +where + T: Serialize + ?Sized, +{ + let cache_dir = get_cache_dir()?; + let cache_file = cache_dir.join(format!("{}.cache", key)); + + // Get current timestamp + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + // Create a JSON object with timestamp and data directly + let json_data = json!({ + "timestamp": now, + "data": data // No clone needed here + }); + + // Serialize and write to file + let serialized = serde_json::to_string(&json_data) + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + + fs::write(cache_file, serialized)?; + info!("Saved cache for key: {}", key); + + Ok(()) +} + +// Load data from cache file if it exists and is not expired +pub fn load_from_cache(key: &str, ttl_hours: u64) -> Option +where + T: for<'de> Deserialize<'de>, +{ + let cache_dir = match get_cache_dir() { + Ok(dir) => dir, + Err(e) => { + warn!("Failed to get cache directory: {}", e); + return None; + } + }; + + let cache_file = cache_dir.join(format!("{}.cache", key)); + + // Check if cache file exists + if !cache_file.exists() { + return None; + } + + // Read and deserialize + let cached_data = match fs::read_to_string(&cache_file) { + Ok(data) => data, + Err(e) => { + warn!("Failed to read cache file {}: {}", cache_file.display(), e); + return None; + } + }; + + // Parse the JSON + let json_value: serde_json::Value = match serde_json::from_str(&cached_data) { + Ok(v) => v, + Err(e) => { + warn!("Failed to parse cache file {}: {}", cache_file.display(), e); + return None; + } + }; + + // Extract timestamp + let timestamp = match json_value.get("timestamp").and_then(|v| v.as_u64()) { + Some(ts) => ts, + None => { + warn!("Invalid timestamp in cache file {}", cache_file.display()); + return None; + } + }; + + // Check expiration + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap_or_default() + .as_secs(); + + let age_hours = (now - timestamp) / 3600; + + if age_hours > ttl_hours { + info!("Cache for key {} is expired ({} hours old)", key, age_hours); + return None; + } + + // Extract data + let data: T = match serde_json::from_value(json_value["data"].clone()) { + Ok(d) => d, + Err(e) => { + warn!("Failed to parse data in cache file {}: {}", cache_file.display(), e); + return None; + } + }; + + info!("Using cache for key {} ({} hours old)", key, age_hours); + Some(data) +} + +// Cache game scanning results +pub fn cache_games(games: &[crate::installer::Game]) -> io::Result<()> { + save_to_cache("games", games, 24) // Cache games for 24 hours +} + +// Load cached game scanning results +pub fn load_cached_games() -> Option> { + load_from_cache("games", 24) +} + +// Cache DLC list for a game +pub fn cache_dlcs(game_id: &str, dlcs: &[DlcInfoWithState]) -> io::Result<()> { + save_to_cache(&format!("dlc_{}", game_id), dlcs, 168) // Cache DLCs for 7 days (168 hours) +} + +// Load cached DLC list +pub fn load_cached_dlcs(game_id: &str) -> Option> { + load_from_cache(&format!("dlc_{}", game_id), 168) +} + +// Clear all caches +pub fn clear_all_caches() -> io::Result<()> { + let cache_dir = get_cache_dir()?; + + for entry in fs::read_dir(cache_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() && path.extension().map_or(false, |ext| ext == "cache") { + if let Err(e) = fs::remove_file(&path) { + warn!("Failed to remove cache file {}: {}", path.display(), e); + } else { + info!("Removed cache file: {}", path.display()); + } + } + } + + info!("All caches cleared"); + Ok(()) +} \ No newline at end of file diff --git a/src-tauri/src/dlc_manager.rs b/src-tauri/src/dlc_manager.rs new file mode 100644 index 0000000..a98e411 --- /dev/null +++ b/src-tauri/src/dlc_manager.rs @@ -0,0 +1,339 @@ +// src/dlc_manager.rs +use serde::{Serialize, Deserialize}; +use std::fs; +use std::path::Path; +use log::{info, error}; +use std::collections::{HashMap, HashSet}; +use tauri::Manager; + +/// More detailed DLC information with enabled state +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DlcInfoWithState { + pub appid: String, + pub name: String, + pub enabled: bool, +} + +/// Parse the cream_api.ini file to extract both enabled and disabled DLCs +pub fn get_enabled_dlcs(game_path: &str) -> Result, String> { + info!("Reading enabled DLCs from {}", game_path); + + let cream_api_path = Path::new(game_path).join("cream_api.ini"); + if !cream_api_path.exists() { + return Err(format!("cream_api.ini not found at {}", cream_api_path.display())); + } + + let contents = match fs::read_to_string(&cream_api_path) { + Ok(c) => c, + Err(e) => return Err(format!("Failed to read cream_api.ini: {}", e)) + }; + + // Extract DLCs - they are in the [dlc] section with format "appid = name" + let mut in_dlc_section = false; + let mut enabled_dlcs = Vec::new(); + + for line in contents.lines() { + let trimmed = line.trim(); + + // Check if we're in the DLC section + if trimmed == "[dlc]" { + in_dlc_section = true; + continue; + } + + // Check if we're leaving the DLC section (another section begins) + if in_dlc_section && trimmed.starts_with('[') && trimmed.ends_with(']') { + in_dlc_section = false; + continue; + } + + // Skip empty lines and non-DLC comments + if in_dlc_section && !trimmed.is_empty() && !trimmed.starts_with(';') { + // Extract the DLC app ID + if let Some(appid) = trimmed.split('=').next() { + let appid_clean = appid.trim(); + // Check if the line is commented out (indicating a disabled DLC) + if !appid_clean.starts_with("#") { + enabled_dlcs.push(appid_clean.to_string()); + } + } + } + } + + info!("Found {} enabled DLCs", enabled_dlcs.len()); + Ok(enabled_dlcs) +} + +/// Get all DLCs (both enabled and disabled) from cream_api.ini +pub fn get_all_dlcs(game_path: &str) -> Result, String> { + info!("Reading all DLCs from {}", game_path); + + let cream_api_path = Path::new(game_path).join("cream_api.ini"); + if !cream_api_path.exists() { + return Err(format!("cream_api.ini not found at {}", cream_api_path.display())); + } + + let contents = match fs::read_to_string(&cream_api_path) { + Ok(c) => c, + Err(e) => return Err(format!("Failed to read cream_api.ini: {}", e)) + }; + + // Extract DLCs - both enabled and disabled + let mut in_dlc_section = false; + let mut all_dlcs = Vec::new(); + + for line in contents.lines() { + let trimmed = line.trim(); + + // Check if we're in the DLC section + if trimmed == "[dlc]" { + in_dlc_section = true; + continue; + } + + // Check if we're leaving the DLC section (another section begins) + if in_dlc_section && trimmed.starts_with('[') && trimmed.ends_with(']') { + in_dlc_section = false; + continue; + } + + // Process DLC entries (both enabled and commented/disabled) + if in_dlc_section && !trimmed.is_empty() && !trimmed.starts_with(';') { + let is_commented = trimmed.starts_with("#"); + let actual_line = if is_commented { + trimmed.trim_start_matches('#').trim() + } else { + trimmed + }; + + let parts: Vec<&str> = actual_line.splitn(2, '=').collect(); + if parts.len() == 2 { + let appid = parts[0].trim(); + let name = parts[1].trim(); + + all_dlcs.push(DlcInfoWithState { + appid: appid.to_string(), + name: name.to_string().trim_matches('"').to_string(), + enabled: !is_commented, + }); + } + } + } + + info!("Found {} total DLCs ({} enabled, {} disabled)", + all_dlcs.len(), + all_dlcs.iter().filter(|d| d.enabled).count(), + all_dlcs.iter().filter(|d| !d.enabled).count()); + + Ok(all_dlcs) +} + +/// Update the cream_api.ini file with the user's DLC selections +pub fn update_dlc_configuration(game_path: &str, dlcs: Vec) -> Result<(), String> { + info!("Updating DLC configuration for {}", game_path); + + let cream_api_path = Path::new(game_path).join("cream_api.ini"); + if !cream_api_path.exists() { + return Err(format!("cream_api.ini not found at {}", cream_api_path.display())); + } + + // Read the current file contents + let current_contents = match fs::read_to_string(&cream_api_path) { + Ok(c) => c, + Err(e) => return Err(format!("Failed to read cream_api.ini: {}", e)) + }; + + // Create a mapping of DLC appid to its state for easy lookup + let dlc_states: HashMap = dlcs.iter() + .map(|dlc| (dlc.appid.clone(), (dlc.enabled, dlc.name.clone()))) + .collect(); + + // Keep track of processed DLCs to avoid duplicates + let mut processed_dlcs = HashSet::new(); + + // Process the file line by line to retain most of the original structure + let mut new_contents = Vec::new(); + let mut in_dlc_section = false; + + for line in current_contents.lines() { + let trimmed = line.trim(); + + // Add section markers directly + if trimmed == "[dlc]" { + in_dlc_section = true; + new_contents.push(line.to_string()); + continue; + } + + // Check if we're leaving the DLC section (another section begins) + if in_dlc_section && trimmed.starts_with('[') && trimmed.ends_with(']') { + in_dlc_section = false; + + // Before leaving the DLC section, add any DLCs that weren't processed yet + for (appid, (enabled, name)) in &dlc_states { + if !processed_dlcs.contains(appid) { + if *enabled { + new_contents.push(format!("{} = {}", appid, name)); + } else { + new_contents.push(format!("# {} = {}", appid, name)); + } + } + } + + // Now add the section marker + new_contents.push(line.to_string()); + continue; + } + + if in_dlc_section && !trimmed.is_empty() { + let is_comment_line = trimmed.starts_with(';'); + + // If it's a regular comment line (not a DLC), keep it as is + if is_comment_line { + new_contents.push(line.to_string()); + continue; + } + + // Check if it's a commented-out DLC line or a regular DLC line + let is_commented = trimmed.starts_with("#"); + let actual_line = if is_commented { + trimmed.trim_start_matches('#').trim() + } else { + trimmed + }; + + // Extract appid and name + let parts: Vec<&str> = actual_line.splitn(2, '=').collect(); + if parts.len() == 2 { + let appid = parts[0].trim(); + let name = parts[1].trim(); + + // Check if this DLC exists in our updated list + if let Some((enabled, _)) = dlc_states.get(appid) { + // Add the DLC with its updated state + if *enabled { + new_contents.push(format!("{} = {}", appid, name)); + } else { + new_contents.push(format!("# {} = {}", appid, name)); + } + processed_dlcs.insert(appid.to_string()); + } else { + // Not in our list - keep the original line + new_contents.push(line.to_string()); + } + } else { + // Invalid format or not a DLC line - keep as is + new_contents.push(line.to_string()); + } + } else if !in_dlc_section || trimmed.is_empty() { + // Not a DLC line or empty line - keep as is + new_contents.push(line.to_string()); + } + } + + // If we never left the DLC section, make sure we add any unprocessed DLCs + if in_dlc_section { + for (appid, (enabled, name)) in &dlc_states { + if !processed_dlcs.contains(appid) { + if *enabled { + new_contents.push(format!("{} = {}", appid, name)); + } else { + new_contents.push(format!("# {} = {}", appid, name)); + } + } + } + } + + // Write the updated file + match fs::write(&cream_api_path, new_contents.join("\n")) { + Ok(_) => { + info!("Successfully updated DLC configuration at {}", cream_api_path.display()); + Ok(()) + }, + Err(e) => { + error!("Failed to write updated cream_api.ini: {}", e); + Err(format!("Failed to write updated cream_api.ini: {}", e)) + } + } +} + +/// Get app ID from game path by reading cream_api.ini +#[allow(dead_code)] +fn extract_app_id_from_config(game_path: &str) -> Option { + if let Ok(contents) = fs::read_to_string(Path::new(game_path).join("cream_api.ini")) { + let re = regex::Regex::new(r"APPID\s*=\s*(\d+)").unwrap(); + if let Some(cap) = re.captures(&contents) { + return Some(cap[1].to_string()); + } + } + None +} + +/// Create a custom installation with selected DLCs +pub async fn install_cream_with_dlcs( + game_id: String, + app_handle: tauri::AppHandle, + selected_dlcs: Vec +) -> Result<(), String> { + use crate::AppState; + + // Count enabled DLCs for logging + let enabled_dlc_count = selected_dlcs.iter().filter(|dlc| dlc.enabled).count(); + info!("Starting installation of CreamLinux with {} selected DLCs", enabled_dlc_count); + + // Get the game from state + let game = { + let state = app_handle.state::(); + let games = state.games.lock(); + match games.get(&game_id) { + Some(g) => g.clone(), + None => return Err(format!("Game with ID {} not found", game_id)) + } + }; + + info!("Installing CreamLinux for game: {} ({})", game.title, game_id); + + // Install CreamLinux first - but provide the DLCs directly instead of fetching them again + use crate::installer::install_creamlinux_with_dlcs; + + // Convert DlcInfoWithState to installer::DlcInfo for those that are enabled + let enabled_dlcs = selected_dlcs.iter() + .filter(|dlc| dlc.enabled) + .map(|dlc| crate::installer::DlcInfo { + appid: dlc.appid.clone(), + name: dlc.name.clone(), + }) + .collect::>(); + + let app_handle_clone = app_handle.clone(); + let game_title = game.title.clone(); + + // Use direct installation with provided DLCs instead of re-fetching + match install_creamlinux_with_dlcs( + &game.path, + &game_id, + enabled_dlcs, + move |progress, message| { + // Emit progress updates during installation + use crate::installer::emit_progress; + emit_progress( + &app_handle_clone, + &format!("Installing CreamLinux for {}", game_title), + message, + progress * 100.0, // Scale progress from 0 to 100% + false, + false, + None + ); + } + ).await { + Ok(_) => { + info!("CreamLinux installation completed successfully for game: {}", game.title); + Ok(()) + }, + Err(e) => { + error!("Failed to install CreamLinux: {}", e); + Err(format!("Failed to install CreamLinux: {}", e)) + } + } +} \ No newline at end of file diff --git a/src-tauri/src/installer.rs b/src-tauri/src/installer.rs new file mode 100644 index 0000000..2776944 --- /dev/null +++ b/src-tauri/src/installer.rs @@ -0,0 +1,997 @@ +// src/installer.rs +use serde::{Serialize, Deserialize}; +use std::fs; +use std::io; +use std::path::Path; +use log::{info, error, warn}; +use reqwest; +use tauri::{AppHandle, Emitter}; +use tempfile::tempdir; +use zip::ZipArchive; +use std::time::Duration; +use serde_json::json; +use std::sync::atomic::Ordering; +use crate::AppState; +use tauri::Manager; + +// Constants for API endpoints and downloads +const CREAMLINUX_RELEASE_URL: &str = "https://github.com/anticitizn/creamlinux/releases/latest/download/creamlinux.zip"; +const SMOKEAPI_REPO: &str = "acidicoala/SmokeAPI"; + +// Type of installer +#[derive(Debug, Clone, Copy)] +pub enum InstallerType { + Cream, + Smoke +} + +// Action to perform +#[derive(Debug, Clone, Copy)] +pub enum InstallerAction { + Install, + Uninstall +} + +// Error type combining all possible errors +#[derive(Debug)] +pub enum InstallerError { + IoError(io::Error), + ReqwestError(reqwest::Error), + ZipError(zip::result::ZipError), + InstallationError(String), +} + +impl From for InstallerError { + fn from(err: io::Error) -> Self { + InstallerError::IoError(err) + } +} + +impl From for InstallerError { + fn from(err: reqwest::Error) -> Self { + InstallerError::ReqwestError(err) + } +} + +impl From for InstallerError { + fn from(err: zip::result::ZipError) -> Self { + InstallerError::ZipError(err) + } +} + +impl std::fmt::Display for InstallerError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InstallerError::IoError(e) => write!(f, "IO error: {}", e), + InstallerError::ReqwestError(e) => write!(f, "Network error: {}", e), + InstallerError::ZipError(e) => write!(f, "Zip extraction error: {}", e), + InstallerError::InstallationError(e) => write!(f, "Installation error: {}", e), + } + } +} + +impl std::error::Error for InstallerError {} + +/// DLC Information structure +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct DlcInfo { + pub appid: String, + pub name: String, +} + +/// Struct to hold installation instructions for the frontend +#[derive(Serialize, Debug, Clone)] +pub struct InstallationInstructions { + #[serde(rename = "type")] + pub type_: String, + pub command: String, + pub game_title: String, + pub dlc_count: Option, +} + +/// Game information structure from searcher module +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Game { + pub id: String, + pub title: String, + pub path: String, + pub native: bool, + pub api_files: Vec, + pub cream_installed: bool, + pub smoke_installed: bool, + pub installing: bool, +} + +/// Emit a progress update to the frontend +pub fn emit_progress( + app_handle: &AppHandle, + title: &str, + message: &str, + progress: f32, + complete: bool, + show_instructions: bool, + instructions: Option +) { + let mut payload = json!({ + "title": title, + "message": message, + "progress": progress, + "complete": complete, + "show_instructions": show_instructions + }); + + if let Some(inst) = instructions { + payload["instructions"] = serde_json::to_value(inst).unwrap_or_default(); + } + + if let Err(e) = app_handle.emit("installation-progress", payload) { + warn!("Failed to emit progress event: {}", e); + } +} + +/// Process a single game action (install/uninstall Cream/Smoke) +pub async fn process_action( + _game_id: String, + installer_type: InstallerType, + action: InstallerAction, + game: Game, + app_handle: AppHandle +) -> Result<(), String> { + match (installer_type, action) { + (InstallerType::Cream, InstallerAction::Install) => { + // We only allow CreamLinux for native games + if !game.native { + return Err("CreamLinux can only be installed on native Linux games".to_string()); + } + + info!("Installing CreamLinux for game: {}", game.title); + let game_title = game.title.clone(); + + emit_progress( + &app_handle, + &format!("Installing CreamLinux for {}", game_title), + "Fetching DLC list...", + 10.0, + false, + false, + None + ); + + // Fetch DLC list + let dlcs = match fetch_dlc_details(&game.id).await { + Ok(dlcs) => dlcs, + Err(e) => { + error!("Failed to fetch DLC details: {}", e); + return Err(format!("Failed to fetch DLC details: {}", e)); + } + }; + + let dlc_count = dlcs.len(); + info!("Found {} DLCs for {}", dlc_count, game_title); + + emit_progress( + &app_handle, + &format!("Installing CreamLinux for {}", game_title), + "Downloading CreamLinux...", + 30.0, + false, + false, + None + ); + + // Install CreamLinux + let app_handle_clone = app_handle.clone(); + let game_title_clone = game_title.clone(); + + match install_creamlinux(&game.path, &game.id, dlcs, move |progress, message| { + // Emit progress updates during installation + emit_progress( + &app_handle_clone, + &format!("Installing CreamLinux for {}", game_title_clone), + message, + 30.0 + (progress * 60.0), // Scale progress from 30% to 90% + false, + false, + None + ); + }).await { + Ok(_) => { + // Emit completion with instructions + let instructions = InstallationInstructions { + type_: "cream_install".to_string(), + command: "sh ./cream.sh %command%".to_string(), + game_title: game_title.clone(), + dlc_count: Some(dlc_count) + }; + + emit_progress( + &app_handle, + &format!("Installation Completed: {}", game_title), + "CreamLinux has been installed successfully!", + 100.0, + true, + true, + Some(instructions) + ); + + info!("CreamLinux installation completed for: {}", game_title); + Ok(()) + }, + Err(e) => { + error!("Failed to install CreamLinux: {}", e); + Err(format!("Failed to install CreamLinux: {}", e)) + } + } + }, + (InstallerType::Cream, InstallerAction::Uninstall) => { + // Ensure this is a native game + if !game.native { + return Err("CreamLinux can only be uninstalled from native Linux games".to_string()); + } + + let game_title = game.title.clone(); + info!("Uninstalling CreamLinux from game: {}", game_title); + + emit_progress( + &app_handle, + &format!("Uninstalling CreamLinux from {}", game_title), + "Removing CreamLinux files...", + 30.0, + false, + false, + None + ); + + // Uninstall CreamLinux + match uninstall_creamlinux(&game.path) { + Ok(_) => { + // Emit completion with instructions + let instructions = InstallationInstructions { + type_: "cream_uninstall".to_string(), + command: "sh ./cream.sh %command%".to_string(), + game_title: game_title.clone(), + dlc_count: None + }; + + emit_progress( + &app_handle, + &format!("Uninstallation Completed: {}", game_title), + "CreamLinux has been uninstalled successfully!", + 100.0, + true, + true, + Some(instructions) + ); + + info!("CreamLinux uninstallation completed for: {}", game_title); + Ok(()) + }, + Err(e) => { + error!("Failed to uninstall CreamLinux: {}", e); + Err(format!("Failed to uninstall CreamLinux: {}", e)) + } + } + }, + (InstallerType::Smoke, InstallerAction::Install) => { + // We only allow SmokeAPI for Proton/Windows games + if game.native { + return Err("SmokeAPI can only be installed on Proton/Windows games".to_string()); + } + + // Check if we have any Steam API DLLs to patch + if game.api_files.is_empty() { + return Err("No Steam API DLLs found to patch. SmokeAPI cannot be installed.".to_string()); + } + + let game_title = game.title.clone(); + info!("Installing SmokeAPI for game: {}", game_title); + + emit_progress( + &app_handle, + &format!("Installing SmokeAPI for {}", game_title), + "Fetching SmokeAPI release information...", + 10.0, + false, + false, + None + ); + + // Create clones for the closure + let app_handle_clone = app_handle.clone(); + let game_title_clone = game_title.clone(); + let api_files = game.api_files.clone(); + + // Call the SmokeAPI installation with progress updates + match install_smokeapi(&game.path, &api_files, move |progress, message| { + // Emit progress updates during installation + emit_progress( + &app_handle_clone, + &format!("Installing SmokeAPI for {}", game_title_clone), + message, + 10.0 + (progress * 90.0), // Scale progress from 10% to 100% + false, + false, + None + ); + }).await { + Ok(_) => { + // Emit completion with instructions + let instructions = InstallationInstructions { + type_: "smoke_install".to_string(), + command: "No additional steps needed. SmokeAPI will work automatically.".to_string(), + game_title: game_title.clone(), + dlc_count: Some(game.api_files.len()) + }; + + emit_progress( + &app_handle, + &format!("Installation Completed: {}", game_title), + "SmokeAPI has been installed successfully!", + 100.0, + true, + true, + Some(instructions) + ); + + info!("SmokeAPI installation completed for: {}", game_title); + Ok(()) + }, + Err(e) => { + error!("Failed to install SmokeAPI: {}", e); + Err(format!("Failed to install SmokeAPI: {}", e)) + } + } + }, + (InstallerType::Smoke, InstallerAction::Uninstall) => { + // Ensure this is a non-native game + if game.native { + return Err("SmokeAPI can only be uninstalled from Proton/Windows games".to_string()); + } + + let game_title = game.title.clone(); + info!("Uninstalling SmokeAPI from game: {}", game_title); + + emit_progress( + &app_handle, + &format!("Uninstalling SmokeAPI from {}", game_title), + "Restoring original files...", + 30.0, + false, + false, + None + ); + + // Uninstall SmokeAPI + match uninstall_smokeapi(&game.path, &game.api_files) { + Ok(_) => { + // Emit completion with instructions + let instructions = InstallationInstructions { + type_: "smoke_uninstall".to_string(), + command: "Original Steam API files have been restored.".to_string(), + game_title: game_title.clone(), + dlc_count: None + }; + + emit_progress( + &app_handle, + &format!("Uninstallation Completed: {}", game_title), + "SmokeAPI has been uninstalled successfully!", + 100.0, + true, + true, + Some(instructions) + ); + + info!("SmokeAPI uninstallation completed for: {}", game_title); + Ok(()) + }, + Err(e) => { + error!("Failed to uninstall SmokeAPI: {}", e); + Err(format!("Failed to uninstall SmokeAPI: {}", e)) + } + } + } + } +} + +// +// CreamLinux specific functions +// + +/// Install CreamLinux for a game +async fn install_creamlinux( + game_path: &str, + app_id: &str, + dlcs: Vec, + progress_callback: F +) -> Result<(), InstallerError> +where + F: Fn(f32, &str) + Send + 'static +{ + // Progress update + progress_callback(0.1, "Preparing to download CreamLinux..."); + + // Download CreamLinux zip + let client = reqwest::Client::new(); + progress_callback(0.2, "Downloading CreamLinux..."); + + let response = client.get(CREAMLINUX_RELEASE_URL) + .timeout(Duration::from_secs(30)) + .send() + .await?; + + if !response.status().is_success() { + return Err(InstallerError::InstallationError( + format!("Failed to download CreamLinux: HTTP {}", response.status()) + )); + } + + // Save to temporary file + progress_callback(0.4, "Saving downloaded files..."); + let temp_dir = tempdir()?; + let zip_path = temp_dir.path().join("creamlinux.zip"); + let content = response.bytes().await?; + fs::write(&zip_path, &content)?; + + // Extract the zip + progress_callback(0.5, "Extracting CreamLinux files..."); + let file = fs::File::open(&zip_path)?; + let mut archive = ZipArchive::new(file)?; + + for i in 0..archive.len() { + let mut file = archive.by_index(i)?; + let outpath = Path::new(game_path).join(file.name()); + + if file.name().ends_with('/') { + fs::create_dir_all(&outpath)?; + } else { + if let Some(p) = outpath.parent() { + if !p.exists() { + fs::create_dir_all(p)?; + } + } + let mut outfile = fs::File::create(&outpath)?; + io::copy(&mut file, &mut outfile)?; + } + + // Set executable permissions for cream.sh + if file.name() == "cream.sh" { + progress_callback(0.6, "Setting executable permissions..."); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&outpath)?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(&outpath, perms)?; + } + } + } + + // Create cream_api.ini with DLC info + progress_callback(0.8, "Creating configuration file..."); + let cream_api_path = Path::new(game_path).join("cream_api.ini"); + let mut config = String::new(); + + config.push_str(&format!("APPID = {}\n[config]\n", app_id)); + config.push_str("issubscribedapp_on_false_use_real = true\n"); + config.push_str("[methods]\n"); + config.push_str("disable_steamapps_issubscribedapp = false\n"); + config.push_str("[dlc]\n"); + + for dlc in dlcs { + config.push_str(&format!("{} = {}\n", dlc.appid, dlc.name)); + } + + fs::write(cream_api_path, config)?; + progress_callback(1.0, "Installation completed successfully!"); + + Ok(()) +} + +/// Install CreamLinux for a game with pre-fetched DLC list +/// This avoids the redundant network calls to Steam API +pub async fn install_creamlinux_with_dlcs( + game_path: &str, + app_id: &str, + dlcs: Vec, + progress_callback: F +) -> Result<(), InstallerError> +where + F: Fn(f32, &str) + Send + 'static +{ + // Progress update + progress_callback(0.1, "Preparing to download CreamLinux..."); + + // Download CreamLinux zip + let client = reqwest::Client::new(); + progress_callback(0.2, "Downloading CreamLinux..."); + + let response = client.get(CREAMLINUX_RELEASE_URL) + .timeout(Duration::from_secs(30)) + .send() + .await?; + + if !response.status().is_success() { + return Err(InstallerError::InstallationError( + format!("Failed to download CreamLinux: HTTP {}", response.status()) + )); + } + + // Save to temporary file + progress_callback(0.4, "Saving downloaded files..."); + let temp_dir = tempdir()?; + let zip_path = temp_dir.path().join("creamlinux.zip"); + let content = response.bytes().await?; + fs::write(&zip_path, &content)?; + + // Extract the zip + progress_callback(0.5, "Extracting CreamLinux files..."); + let file = fs::File::open(&zip_path)?; + let mut archive = ZipArchive::new(file)?; + + for i in 0..archive.len() { + let mut file = archive.by_index(i)?; + let outpath = Path::new(game_path).join(file.name()); + + if file.name().ends_with('/') { + fs::create_dir_all(&outpath)?; + } else { + if let Some(p) = outpath.parent() { + if !p.exists() { + fs::create_dir_all(p)?; + } + } + let mut outfile = fs::File::create(&outpath)?; + io::copy(&mut file, &mut outfile)?; + } + + // Set executable permissions for cream.sh + if file.name() == "cream.sh" { + progress_callback(0.6, "Setting executable permissions..."); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&outpath)?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(&outpath, perms)?; + } + } + } + + // Create cream_api.ini with DLC info - using the provided DLCs directly + progress_callback(0.8, "Creating configuration file..."); + let cream_api_path = Path::new(game_path).join("cream_api.ini"); + let mut config = String::new(); + + config.push_str(&format!("APPID = {}\n[config]\n", app_id)); + config.push_str("issubscribedapp_on_false_use_real = true\n"); + config.push_str("[methods]\n"); + config.push_str("disable_steamapps_issubscribedapp = false\n"); + config.push_str("[dlc]\n"); + + for dlc in dlcs { + config.push_str(&format!("{} = {}\n", dlc.appid, dlc.name)); + } + + fs::write(cream_api_path, config)?; + progress_callback(1.0, "Installation completed successfully!"); + + Ok(()) +} + +/// Uninstall CreamLinux from a game +fn uninstall_creamlinux(game_path: &str) -> Result<(), InstallerError> { + info!("Uninstalling CreamLinux from: {}", game_path); + + // Files to remove during uninstallation + let files_to_remove = [ + "cream.sh", + "cream_api.ini", + "cream_api.so", + "lib32Creamlinux.so", + "lib64Creamlinux.so" + ]; + + for file in &files_to_remove { + let file_path = Path::new(game_path).join(file); + if file_path.exists() { + match fs::remove_file(&file_path) { + Ok(_) => info!("Removed file: {}", file_path.display()), + Err(e) => { + error!("Failed to remove {}: {}", file_path.display(), e); + // Continue with other files even if one fails + } + } + } + } + + info!("CreamLinux uninstallation completed for: {}", game_path); + Ok(()) +} + +/// Fetch DLC details from Steam API +pub async fn fetch_dlc_details(app_id: &str) -> Result, InstallerError> { + let client = reqwest::Client::new(); + let base_url = format!("https://store.steampowered.com/api/appdetails?appids={}", app_id); + + let response = client.get(&base_url) + .timeout(Duration::from_secs(10)) + .send() + .await?; + + if !response.status().is_success() { + return Err(InstallerError::InstallationError( + format!("Failed to fetch game details: HTTP {}", response.status()) + )); + } + + let data: serde_json::Value = response.json().await?; + let dlc_ids = match data.get(app_id) + .and_then(|app| app.get("data")) + .and_then(|data| data.get("dlc")) + { + Some(dlc_array) => { + match dlc_array.as_array() { + Some(array) => array.iter() + .filter_map(|id| id.as_u64().map(|n| n.to_string())) + .collect::>(), + _ => Vec::new(), + } + }, + _ => Vec::new(), + }; + + info!("Found {} DLCs for game ID {}", dlc_ids.len(), app_id); + + let mut dlc_details = Vec::new(); + + for dlc_id in dlc_ids { + let dlc_url = format!("https://store.steampowered.com/api/appdetails?appids={}", dlc_id); + + // Add a small delay to avoid rate limiting + tokio::time::sleep(Duration::from_millis(300)).await; + + let dlc_response = client.get(&dlc_url) + .timeout(Duration::from_secs(10)) + .send() + .await?; + + if dlc_response.status().is_success() { + let dlc_data: serde_json::Value = dlc_response.json().await?; + + let dlc_name = match dlc_data.get(&dlc_id) + .and_then(|app| app.get("data")) + .and_then(|data| data.get("name")) + { + Some(name) => { + match name.as_str() { + Some(s) => s.to_string(), + _ => "Unknown DLC".to_string(), + } + }, + _ => "Unknown DLC".to_string(), + }; + + info!("Found DLC: {} ({})", dlc_name, dlc_id); + dlc_details.push(DlcInfo { + appid: dlc_id, + name: dlc_name, + }); + } else if dlc_response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS { + // If rate limited, wait longer + error!("Rate limited by Steam API, waiting 10 seconds"); + tokio::time::sleep(Duration::from_secs(10)).await; + } + } + + info!("Successfully retrieved details for {} DLCs", dlc_details.len()); + Ok(dlc_details) +} + +/// Fetch DLC details from Steam API with progress updates +pub async fn fetch_dlc_details_with_progress(app_id: &str, app_handle: &tauri::AppHandle) -> Result, InstallerError> { + info!("Starting DLC details fetch with progress for game ID: {}", app_id); + + // Get a reference to a cancellation flag from app state + let state = app_handle.state::(); + let should_cancel = state.fetch_cancellation.clone(); + + let client = reqwest::Client::new(); + let base_url = format!("https://store.steampowered.com/api/appdetails?appids={}", app_id); + + // Emit initial progress + emit_dlc_progress(app_handle, "Looking up game details...", 5, None); + info!("Emitted initial DLC progress: 5%"); + + let response = client.get(&base_url) + .timeout(Duration::from_secs(10)) + .send() + .await?; + + if !response.status().is_success() { + let error_msg = format!("Failed to fetch game details: HTTP {}", response.status()); + error!("{}", error_msg); + return Err(InstallerError::InstallationError(error_msg)); + } + + let data: serde_json::Value = response.json().await?; + let dlc_ids = match data.get(app_id) + .and_then(|app| app.get("data")) + .and_then(|data| data.get("dlc")) + { + Some(dlc_array) => { + match dlc_array.as_array() { + Some(array) => array.iter() + .filter_map(|id| id.as_u64().map(|n| n.to_string())) + .collect::>(), + _ => Vec::new(), + } + }, + _ => Vec::new(), + }; + + info!("Found {} DLCs for game ID {}", dlc_ids.len(), app_id); + emit_dlc_progress(app_handle, &format!("Found {} DLCs. Fetching details...", dlc_ids.len()), 10, None); + info!("Emitted DLC progress: 10%, found {} DLCs", dlc_ids.len()); + + let mut dlc_details = Vec::new(); + let total_dlcs = dlc_ids.len(); + + for (index, dlc_id) in dlc_ids.iter().enumerate() { + // Check if cancellation was requested + if should_cancel.load(Ordering::SeqCst) { + info!("DLC fetch cancelled for game {}", app_id); + return Err(InstallerError::InstallationError("Operation cancelled by user".to_string())); + } + let progress_percent = 10.0 + (index as f32 / total_dlcs as f32) * 90.0; + let progress_rounded = progress_percent as u32; + let remaining_dlcs = total_dlcs - index; + + // Estimate time remaining (rough calculation - 300ms per DLC) + let est_time_left = if remaining_dlcs > 0 { + let seconds = (remaining_dlcs as f32 * 0.3).ceil() as u32; + if seconds < 60 { + format!("~{} seconds", seconds) + } else { + format!("~{} minute(s)", (seconds as f32 / 60.0).ceil() as u32) + } + } else { + "almost done".to_string() + }; + + info!("Processing DLC {}/{} - Progress: {}%", index + 1, total_dlcs, progress_rounded); + emit_dlc_progress( + app_handle, + &format!("Processing DLC {}/{}", index + 1, total_dlcs), + progress_rounded, + Some(&est_time_left) + ); + + let dlc_url = format!("https://store.steampowered.com/api/appdetails?appids={}", dlc_id); + + // Add a small delay to avoid rate limiting + tokio::time::sleep(Duration::from_millis(300)).await; + + let dlc_response = client.get(&dlc_url) + .timeout(Duration::from_secs(10)) + .send() + .await?; + + if dlc_response.status().is_success() { + let dlc_data: serde_json::Value = dlc_response.json().await?; + + let dlc_name = match dlc_data.get(&dlc_id) + .and_then(|app| app.get("data")) + .and_then(|data| data.get("name")) + { + Some(name) => { + match name.as_str() { + Some(s) => s.to_string(), + _ => "Unknown DLC".to_string(), + } + }, + _ => "Unknown DLC".to_string(), + }; + + info!("Found DLC: {} ({})", dlc_name, dlc_id); + let dlc_info = DlcInfo { + appid: dlc_id.clone(), + name: dlc_name, + }; + + // Emit each DLC as we find it + if let Ok(json) = serde_json::to_string(&dlc_info) { + if let Err(e) = app_handle.emit("dlc-found", json) { + warn!("Failed to emit dlc-found event: {}", e); + } else { + info!("Emitted dlc-found event for DLC: {}", dlc_id); + } + } + + dlc_details.push(dlc_info); + } else if dlc_response.status() == reqwest::StatusCode::TOO_MANY_REQUESTS { + // If rate limited, wait longer + error!("Rate limited by Steam API, waiting 10 seconds"); + emit_dlc_progress(app_handle, "Rate limited by Steam. Waiting...", progress_rounded, None); + tokio::time::sleep(Duration::from_secs(10)).await; + } + } + + // Final progress update + info!("Completed DLC fetch. Found {} DLCs in total", dlc_details.len()); + emit_dlc_progress(app_handle, &format!("Completed! Found {} DLCs", dlc_details.len()), 100, None); + info!("Emitted final DLC progress: 100%"); + + Ok(dlc_details) +} + +/// Emit DLC progress updates to the frontend +fn emit_dlc_progress( + app_handle: &tauri::AppHandle, + message: &str, + progress: u32, + time_left: Option<&str> +) { + let mut payload = json!({ + "message": message, + "progress": progress + }); + + if let Some(time) = time_left { + payload["timeLeft"] = json!(time); + } + + if let Err(e) = app_handle.emit("dlc-progress", payload) { + warn!("Failed to emit dlc-progress event: {}", e); + } +} + +// +// SmokeAPI specific functions +// + +/// Install SmokeAPI for a game +async fn install_smokeapi( + game_path: &str, + api_files: &[String], + progress_callback: F +) -> Result<(), InstallerError> +where + F: Fn(f32, &str) + Send + 'static +{ + // 1. Get the latest SmokeAPI release + progress_callback(0.1, "Fetching latest SmokeAPI release..."); + let client = reqwest::Client::new(); + let releases_url = format!("https://api.github.com/repos/{}/releases/latest", SMOKEAPI_REPO); + + let response = client.get(&releases_url) + .header("User-Agent", "CreamLinux") + .timeout(Duration::from_secs(10)) + .send() + .await?; + + if !response.status().is_success() { + return Err(InstallerError::InstallationError( + format!("Failed to fetch SmokeAPI releases: HTTP {}", response.status()) + )); + } + + let release_info: serde_json::Value = response.json().await?; + let latest_version = match release_info.get("tag_name") { + Some(tag) => tag.as_str().unwrap_or("latest"), + _ => "latest", + }; + + info!("Latest SmokeAPI version: {}", latest_version); + + // 2. Construct download URL + let zip_url = format!( + "https://github.com/{}/releases/download/{}/SmokeAPI-{}.zip", + SMOKEAPI_REPO, latest_version, latest_version + ); + + // 3. Download the zip + progress_callback(0.3, "Downloading SmokeAPI..."); + let response = client.get(&zip_url) + .timeout(Duration::from_secs(30)) + .send() + .await?; + + if !response.status().is_success() { + return Err(InstallerError::InstallationError( + format!("Failed to download SmokeAPI: HTTP {}", response.status()) + )); + } + + // 4. Save to temporary file + progress_callback(0.5, "Saving downloaded files..."); + let temp_dir = tempdir()?; + let zip_path = temp_dir.path().join("smokeapi.zip"); + let content = response.bytes().await?; + fs::write(&zip_path, &content)?; + + // 5. Extract and install for each API file + progress_callback(0.6, "Extracting SmokeAPI files..."); + let file = fs::File::open(&zip_path)?; + let mut archive = ZipArchive::new(file)?; + + for (i, api_file) in api_files.iter().enumerate() { + let progress = 0.6 + (i as f32 / api_files.len() as f32) * 0.3; + progress_callback(progress, &format!("Installing SmokeAPI for {}", api_file)); + + let api_dir = Path::new(game_path).join(Path::new(api_file).parent().unwrap_or_else(|| Path::new(""))); + let api_name = Path::new(api_file).file_name().unwrap_or_default(); + + // Backup original file + let original_path = api_dir.join(api_name); + let backup_path = api_dir.join(api_name.to_string_lossy().replace(".dll", "_o.dll")); + + info!("Processing: {}", original_path.display()); + info!("Backup path: {}", backup_path.display()); + + // Only backup if not already backed up + if !backup_path.exists() && original_path.exists() { + fs::copy(&original_path, &backup_path)?; + info!("Created backup: {}", backup_path.display()); + } + + // Extract the appropriate DLL directly to the game directory + if let Ok(mut file) = archive.by_name(&api_name.to_string_lossy()) { + let mut outfile = fs::File::create(&original_path)?; + io::copy(&mut file, &mut outfile)?; + info!("Installed SmokeAPI as: {}", original_path.display()); + } else { + return Err(InstallerError::InstallationError( + format!("Could not find {} in the SmokeAPI zip file", api_name.to_string_lossy()) + )); + } + } + + progress_callback(1.0, "SmokeAPI installation completed!"); + info!("SmokeAPI installation completed for: {}", game_path); + Ok(()) +} + +/// Uninstall SmokeAPI from a game +fn uninstall_smokeapi(game_path: &str, api_files: &[String]) -> Result<(), InstallerError> { + info!("Uninstalling SmokeAPI from: {}", game_path); + + for api_file in api_files { + let api_path = Path::new(game_path).join(api_file); + let api_dir = api_path.parent().unwrap_or_else(|| Path::new(game_path)); + let api_name = api_path.file_name().unwrap_or_default(); + + let original_path = api_dir.join(api_name); + let backup_path = api_dir.join(api_name.to_string_lossy().replace(".dll", "_o.dll")); + + info!("Processing: {}", original_path.display()); + info!("Backup path: {}", backup_path.display()); + + if backup_path.exists() { + // Remove the SmokeAPI version + if original_path.exists() { + match fs::remove_file(&original_path) { + Ok(_) => info!("Removed SmokeAPI file: {}", original_path.display()), + Err(e) => error!("Failed to remove SmokeAPI file: {}, error: {}", original_path.display(), e) + } + } + + // Restore the original file + match fs::rename(&backup_path, &original_path) { + Ok(_) => info!("Restored original file: {}", original_path.display()), + Err(e) => { + error!("Failed to restore original file: {}, error: {}", original_path.display(), e); + // Try to copy instead if rename fails + if let Err(copy_err) = fs::copy(&backup_path, &original_path).and_then(|_| fs::remove_file(&backup_path)) { + error!("Failed to copy backup file: {}", copy_err); + } + } + } + } else { + info!("No backup found for: {}", api_file); + } + } + + info!("SmokeAPI uninstallation completed for: {}", game_path); + Ok(()) +} \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..cdc6dfe --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,487 @@ +// src/main.rs +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +mod searcher; +mod installer; +mod dlc_manager; +mod cache; // Keep the module for now, but we won't use its functionality + +use serde::{Serialize, Deserialize}; +use std::collections::HashMap; +use parking_lot::Mutex; +use tokio::time::Instant; +use tokio::time::Duration; +use tauri::State; +use tauri::{Manager, Emitter}; +use log::{info, warn, error, debug}; +use installer::{InstallerType, InstallerAction, Game}; +use dlc_manager::DlcInfoWithState; +use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GameAction { +game_id: String, +action: String, +} + +#[derive(Debug, Clone)] +struct DlcCache { + data: Vec, + timestamp: Instant, +} + +// Structure to hold the state of installed games +struct AppState { +games: Mutex>, +dlc_cache: Mutex>, +fetch_cancellation: Arc, +} + +#[tauri::command] +fn get_all_dlcs_command(game_path: String) -> Result, String> { + info!("Getting all DLCs (enabled and disabled) for: {}", game_path); + dlc_manager::get_all_dlcs(&game_path) +} + +// Scan and get the list of Steam games +#[tauri::command] +async fn scan_steam_games(state: State<'_, AppState>, app_handle: tauri::AppHandle) -> Result, String> { + info!("Starting Steam games scan"); + emit_scan_progress(&app_handle, "Locating Steam libraries...", 10); + + // Get default Steam paths + let paths = searcher::get_default_steam_paths(); + + // Find Steam libraries + emit_scan_progress(&app_handle, "Finding Steam libraries...", 15); + let libraries = searcher::find_steam_libraries(&paths); + + // Group libraries by path to avoid duplicates in logs + let mut unique_libraries = std::collections::HashSet::new(); + for lib in &libraries { + unique_libraries.insert(lib.to_string_lossy().to_string()); + } + + info!("Found {} Steam library directories:", unique_libraries.len()); + for (i, lib) in unique_libraries.iter().enumerate() { + info!(" Library {}: {}", i+1, lib); + } + + emit_scan_progress(&app_handle, &format!("Found {} Steam libraries. Starting game scan...", unique_libraries.len()), 20); + + // Find installed games + let games_info = searcher::find_installed_games(&libraries).await; + + emit_scan_progress(&app_handle, &format!("Found {} games. Processing...", games_info.len()), 90); + + // Log summary of games found + info!("Games scan complete - Found {} games", games_info.len()); + info!("Native games: {}", games_info.iter().filter(|g| g.native).count()); + info!("Proton games: {}", games_info.iter().filter(|g| !g.native).count()); + info!("Games with CreamLinux: {}", games_info.iter().filter(|g| g.cream_installed).count()); + info!("Games with SmokeAPI: {}", games_info.iter().filter(|g| g.smoke_installed).count()); + + // Convert to our Game struct + let mut result = Vec::new(); + + info!("Processing games into application state..."); + for game_info in games_info { + // Only log detailed game info at Debug level to keep Info logs cleaner + debug!("Processing game: {}, Native: {}, CreamLinux: {}, SmokeAPI: {}", + game_info.title, game_info.native, game_info.cream_installed, game_info.smoke_installed); + + let game = Game { + id: game_info.id, + title: game_info.title, + path: game_info.path.to_string_lossy().to_string(), + native: game_info.native, + api_files: game_info.api_files, + cream_installed: game_info.cream_installed, + smoke_installed: game_info.smoke_installed, + installing: false, + }; + + result.push(game.clone()); + + // Store in state for later use + state.games.lock().insert(game.id.clone(), game); + } + + emit_scan_progress(&app_handle, &format!("Scan complete. Found {} games.", result.len()), 100); + + info!("Game scan completed successfully"); + Ok(result) +} + +// Helper function to emit scan progress events +fn emit_scan_progress(app_handle: &tauri::AppHandle, message: &str, progress: u32) { + // Log first, then emit the event + info!("Scan progress: {}% - {}", progress, message); + + let payload = serde_json::json!({ + "message": message, + "progress": progress + }); + + if let Err(e) = app_handle.emit("scan-progress", payload) { + warn!("Failed to emit scan-progress event: {}", e); + } +} + +// Fetch game info by ID - useful for single game updates +#[tauri::command] +fn get_game_info(game_id: String, state: State) -> Result { + let games = state.games.lock(); + games.get(&game_id) + .cloned() + .ok_or_else(|| format!("Game with ID {} not found", game_id)) +} + +// Unified action handler for installation and uninstallation +#[tauri::command] +async fn process_game_action( +game_action: GameAction, +state: State<'_, AppState>, +app_handle: tauri::AppHandle +) -> Result { +// Clone the information we need from state to avoid lifetime issues +let game = { + let games = state.games.lock(); + games.get(&game_action.game_id) + .cloned() + .ok_or_else(|| format!("Game with ID {} not found", game_action.game_id))? +}; + +// Parse the action string to determine type and operation +let (installer_type, action) = match game_action.action.as_str() { + "install_cream" => (InstallerType::Cream, InstallerAction::Install), + "uninstall_cream" => (InstallerType::Cream, InstallerAction::Uninstall), + "install_smoke" => (InstallerType::Smoke, InstallerAction::Install), + "uninstall_smoke" => (InstallerType::Smoke, InstallerAction::Uninstall), + _ => return Err(format!("Invalid action: {}", game_action.action)) +}; + +// Execute the action +installer::process_action( + game_action.game_id.clone(), + installer_type, + action, + game.clone(), + app_handle.clone() +).await?; + +// Update game status in state based on the action +let updated_game = { + let mut games_map = state.games.lock(); + let game = games_map.get_mut(&game_action.game_id) + .ok_or_else(|| format!("Game with ID {} not found after action", game_action.game_id))?; + + // Update installation status + match (installer_type, action) { + (InstallerType::Cream, InstallerAction::Install) => { + game.cream_installed = true; + }, + (InstallerType::Cream, InstallerAction::Uninstall) => { + game.cream_installed = false; + }, + (InstallerType::Smoke, InstallerAction::Install) => { + game.smoke_installed = true; + }, + (InstallerType::Smoke, InstallerAction::Uninstall) => { + game.smoke_installed = false; + } + } + + // Reset installing flag + game.installing = false; + + // Return updated game info + game.clone() +}; + +// Removed cache update + +// Emit an event to update the UI for this specific game +if let Err(e) = app_handle.emit("game-updated", &updated_game) { + warn!("Failed to emit game-updated event: {}", e); +} + +Ok(updated_game) +} + +// Fetch DLC list for a game +#[tauri::command] +async fn fetch_game_dlcs(game_id: String, app_handle: tauri::AppHandle) -> Result, String> { + info!("Fetching DLCs for game ID: {}", game_id); + + // Removed cache checking + + // Always fetch fresh DLC data instead of using cache + match installer::fetch_dlc_details(&game_id).await { + Ok(dlcs) => { + // Convert to DlcInfoWithState (all enabled by default) + let dlcs_with_state = dlcs.into_iter() + .map(|dlc| DlcInfoWithState { + appid: dlc.appid, + name: dlc.name, + enabled: true, + }) + .collect::>(); + + // Cache in memory for this session (but not on disk) + let state = app_handle.state::(); + let mut cache = state.dlc_cache.lock(); + cache.insert(game_id.clone(), DlcCache { + data: dlcs_with_state.clone(), + timestamp: Instant::now(), + }); + + Ok(dlcs_with_state) + }, + Err(e) => Err(format!("Failed to fetch DLC details: {}", e)) + } +} + +#[tauri::command] +fn abort_dlc_fetch(game_id: String, app_handle: tauri::AppHandle) -> Result<(), String> { + info!("Request to abort DLC fetch for game ID: {}", game_id); + + let state = app_handle.state::(); + state.fetch_cancellation.store(true, Ordering::SeqCst); + + // Reset after a short delay + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(500)); + let state = app_handle.state::(); + state.fetch_cancellation.store(false, Ordering::SeqCst); + }); + + Ok(()) +} + +// Fetch DLC list with progress updates (streaming) +#[tauri::command] +async fn stream_game_dlcs(game_id: String, app_handle: tauri::AppHandle) -> Result<(), String> { + info!("Streaming DLCs for game ID: {}", game_id); + + // Removed cached DLC check - always fetch fresh data + + // Always fetch fresh DLC data from API + match installer::fetch_dlc_details_with_progress(&game_id, &app_handle).await { + Ok(dlcs) => { + info!("Successfully streamed {} DLCs for game {}", dlcs.len(), game_id); + + // Convert to DLCInfoWithState for in-memory caching only + let dlcs_with_state = dlcs.into_iter() + .map(|dlc| DlcInfoWithState { + appid: dlc.appid, + name: dlc.name, + enabled: true, + }) + .collect::>(); + + // Update in-memory cache without storing to disk + let state = app_handle.state::(); + let mut dlc_cache = state.dlc_cache.lock(); + dlc_cache.insert(game_id.clone(), DlcCache { + data: dlcs_with_state, + timestamp: tokio::time::Instant::now(), + }); + + Ok(()) + }, + Err(e) => { + error!("Failed to stream DLC details: {}", e); + // Emit error event + let error_payload = serde_json::json!({ + "error": format!("Failed to fetch DLC details: {}", e) + }); + + if let Err(emit_err) = app_handle.emit("dlc-error", error_payload) { + warn!("Failed to emit dlc-error event: {}", emit_err); + } + + Err(format!("Failed to fetch DLC details: {}", e)) + } + } +} + +// Clear caches command renamed to flush_data for clarity +#[tauri::command] +fn clear_caches() -> Result<(), String> { + info!("Data flush requested - cleaning in-memory state only"); + Ok(()) +} + +// Get the list of enabled DLCs for a game +#[tauri::command] +fn get_enabled_dlcs_command(game_path: String) -> Result, String> { + info!("Getting enabled DLCs for: {}", game_path); + dlc_manager::get_enabled_dlcs(&game_path) +} + +// Update the DLC configuration for a game +#[tauri::command] +fn update_dlc_configuration_command(game_path: String, dlcs: Vec) -> Result<(), String> { + info!("Updating DLC configuration for: {}", game_path); + dlc_manager::update_dlc_configuration(&game_path, dlcs) +} + +// Install CreamLinux with selected DLCs +#[tauri::command] +async fn install_cream_with_dlcs_command( + game_id: String, + selected_dlcs: Vec, + app_handle: tauri::AppHandle +) -> Result { + info!("Installing CreamLinux with selected DLCs for game: {}", game_id); + + // Clone selected_dlcs for later use + let selected_dlcs_clone = selected_dlcs.clone(); + + // Install CreamLinux with the selected DLCs + match dlc_manager::install_cream_with_dlcs(game_id.clone(), app_handle.clone(), selected_dlcs).await { + Ok(_) => { + // Return updated game info + let state = app_handle.state::(); + + // Get a mutable reference and update the game + let game = { + let mut games_map = state.games.lock(); + let game = games_map.get_mut(&game_id) + .ok_or_else(|| format!("Game with ID {} not found after installation", game_id))?; + + // Update installation status + game.cream_installed = true; + game.installing = false; + + // Clone the game for returning later + game.clone() + }; // mutable borrow ends here + + // Removed game caching + + // Emit an event to update the UI + if let Err(e) = app_handle.emit("game-updated", &game) { + warn!("Failed to emit game-updated event: {}", e); + } + + // Show installation complete dialog with instructions + let instructions = installer::InstallationInstructions { + type_: "cream_install".to_string(), + command: "sh ./cream.sh %command%".to_string(), + game_title: game.title.clone(), + dlc_count: Some(selected_dlcs_clone.iter().filter(|dlc| dlc.enabled).count()) + }; + + installer::emit_progress( + &app_handle, + &format!("Installation Completed: {}", game.title), + "CreamLinux has been installed successfully!", + 100.0, + true, + true, + Some(instructions) + ); + + Ok(game) + }, + Err(e) => { + error!("Failed to install CreamLinux with selected DLCs: {}", e); + Err(format!("Failed to install CreamLinux with selected DLCs: {}", e)) + } + } +} + +// Setup logging +fn setup_logging() -> Result<(), Box> { + use log::LevelFilter; + use log4rs::append::file::FileAppender; + use log4rs::config::{Appender, Config, Root}; + use log4rs::encode::pattern::PatternEncoder; + use std::fs; + + // Get XDG cache directory + let xdg_dirs = xdg::BaseDirectories::with_prefix("creamlinux")?; + let log_path = xdg_dirs.place_cache_file("creamlinux.log")?; + + // Clear the log file on startup + if log_path.exists() { + if let Err(e) = fs::write(&log_path, "") { + eprintln!("Warning: Failed to clear log file: {}", e); + } + } + + // Create a file appender with improved log format + let file = FileAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "[{d(%Y-%m-%d %H:%M:%S)}] {l}: {m}\n" + ))) + .build(log_path)?; + + // Build the config + let config = Config::builder() + .appender(Appender::builder().build("file", Box::new(file))) + .build(Root::builder().appender("file").build(LevelFilter::Info))?; + + // Initialize log4rs with this config + log4rs::init_config(config)?; + + info!("CreamLinux started with a clean log file"); + Ok(()) +} + +fn main() { + // Set up logging first + if let Err(e) = setup_logging() { + eprintln!("Warning: Failed to initialize logging: {}", e); + } + + info!("Initializing CreamLinux application"); + + let app_state = AppState { + games: Mutex::new(HashMap::new()), + dlc_cache: Mutex::new(HashMap::new()), + fetch_cancellation: Arc::new(AtomicBool::new(false)), + }; + + tauri::Builder::default() + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()) + .manage(app_state) + .invoke_handler(tauri::generate_handler![ + scan_steam_games, + get_game_info, + process_game_action, + fetch_game_dlcs, + stream_game_dlcs, + get_enabled_dlcs_command, + update_dlc_configuration_command, + install_cream_with_dlcs_command, + get_all_dlcs_command, + clear_caches, + abort_dlc_fetch, + ]) + .setup(|app| { + // Add a setup handler to do any initialization work + info!("Tauri application setup"); + + #[cfg(debug_assertions)] + { + if std::env::var("OPEN_DEVTOOLS").ok().as_deref() == Some("1") { + if let Some(window) = app.get_webview_window("main") { + window.open_devtools(); + } + } + } + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} \ No newline at end of file diff --git a/src-tauri/src/searcher.rs b/src-tauri/src/searcher.rs new file mode 100644 index 0000000..df730cd --- /dev/null +++ b/src-tauri/src/searcher.rs @@ -0,0 +1,587 @@ +// src/searcher.rs +use std::fs; +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::collections::HashSet; +use log::{info, debug, warn, error}; +use regex::Regex; +use walkdir::WalkDir; +use tokio::sync::mpsc; +use std::sync::Arc; + +/// Game information structure +#[derive(Debug, Clone)] +pub struct GameInfo { + pub id: String, + pub title: String, + pub path: PathBuf, + pub native: bool, + pub api_files: Vec, + pub cream_installed: bool, + pub smoke_installed: bool, +} + +/// Find potential Steam installation directories +pub fn get_default_steam_paths() -> Vec { + let mut paths = Vec::new(); + + // Get user's home directory + if let Ok(home) = std::env::var("HOME") { + info!("Searching for Steam in home directory: {}", home); + + // Common Steam installation locations on Linux + let common_paths = [ + ".steam/steam", // Steam symlink directory + ".steam/root", // Alternative symlink + ".local/share/Steam", // Flatpak Steam installation + ".var/app/com.valvesoftware.Steam/.local/share/Steam", // Flatpak container path + ".var/app/com.valvesoftware.Steam/data/Steam", // Alternative Flatpak path + "/run/media/mmcblk0p1", // Removable Storage path + ]; + + for path in &common_paths { + let full_path = PathBuf::from(&home).join(path); + if full_path.exists() { + debug!("Found Steam directory: {}", full_path.display()); + paths.push(full_path); + } + } + } + + // Add Steam Deck paths if they exist (these don't rely on HOME) + let deck_paths = [ + "/home/deck/.steam/steam", + "/home/deck/.local/share/Steam", + ]; + + for path in &deck_paths { + let p = PathBuf::from(path); + if p.exists() && !paths.contains(&p) { + debug!("Found Steam Deck path: {}", p.display()); + paths.push(p); + } + } + + // Try to extract paths from Steam registry file + if let Some(registry_paths) = read_steam_registry() { + for path in registry_paths { + if !paths.contains(&path) && path.exists() { + debug!("Adding Steam path from registry: {}", path.display()); + paths.push(path); + } + } + } + + info!("Found {} potential Steam directories", paths.len()); + paths +} + +/// Try to read the Steam registry file to find installation paths +fn read_steam_registry() -> Option> { + let home = match std::env::var("HOME") { + Ok(h) => h, + Err(_) => return None, + }; + + let registry_paths = [ + format!("{}/.steam/registry.vdf", home), + format!("{}/.steam/steam/registry.vdf", home), + format!("{}/.local/share/Steam/registry.vdf", home), + ]; + + for registry_path in registry_paths { + let path = Path::new(®istry_path); + if path.exists() { + debug!("Found Steam registry at: {}", path.display()); + + if let Ok(content) = fs::read_to_string(path) { + let mut paths = Vec::new(); + + // Extract Steam installation paths + let re_steam_path = Regex::new(r#""SteamPath"\s+"([^"]+)""#).unwrap(); + if let Some(cap) = re_steam_path.captures(&content) { + let steam_path = PathBuf::from(&cap[1]); + paths.push(steam_path); + } + + // Look for install path + let re_install_path = Regex::new(r#""InstallPath"\s+"([^"]+)""#).unwrap(); + if let Some(cap) = re_install_path.captures(&content) { + let install_path = PathBuf::from(&cap[1]); + if !paths.contains(&install_path) { + paths.push(install_path); + } + } + + if !paths.is_empty() { + return Some(paths); + } + } + } + } + + None +} + +/// Find all Steam library folders from base Steam installation paths +pub fn find_steam_libraries(base_paths: &[PathBuf]) -> Vec { + let mut libraries = HashSet::new(); + + for base_path in base_paths { + debug!("Looking for Steam libraries in: {}", base_path.display()); + + // Check if this path contains a steamapps directory + let steamapps_path = base_path.join("steamapps"); + if steamapps_path.exists() && steamapps_path.is_dir() { + debug!("Found steamapps directory: {}", steamapps_path.display()); + libraries.insert(steamapps_path.clone()); + + // Check for additional libraries in libraryfolders.vdf + parse_library_folders_vdf(&steamapps_path, &mut libraries); + } + + // Also check for steamapps in common locations relative to this path + let possible_steamapps = [ + base_path.join("steam/steamapps"), + base_path.join("Steam/steamapps"), + ]; + + for path in &possible_steamapps { + if path.exists() && path.is_dir() && !libraries.contains(path) { + debug!("Found steamapps directory: {}", path.display()); + libraries.insert(path.clone()); + + // Check for additional libraries in libraryfolders.vdf + parse_library_folders_vdf(path, &mut libraries); + } + } + } + + let result: Vec = libraries.into_iter().collect(); + info!("Found {} Steam library directories", result.len()); + for (i, lib) in result.iter().enumerate() { + info!(" Library {}: {}", i+1, lib.display()); + } + result +} + +/// Parse libraryfolders.vdf to extract additional library paths +fn parse_library_folders_vdf(steamapps_path: &Path, libraries: &mut HashSet) { + // Check both possible locations of the VDF file + let vdf_paths = [ + steamapps_path.join("libraryfolders.vdf"), + steamapps_path.join("config/libraryfolders.vdf"), + ]; + + for vdf_path in &vdf_paths { + if vdf_path.exists() { + debug!("Found library folders VDF: {}", vdf_path.display()); + + if let Ok(content) = fs::read_to_string(vdf_path) { + // Extract library paths using regex for both new and old format VDFs + let re_path = Regex::new(r#""path"\s+"([^"]+)""#).unwrap(); + for cap in re_path.captures_iter(&content) { + let path_str = &cap[1]; + let lib_path = PathBuf::from(path_str).join("steamapps"); + + if lib_path.exists() && lib_path.is_dir() && !libraries.contains(&lib_path) { + debug!("Found library from VDF: {}", lib_path.display()); + // Clone lib_path before inserting to avoid ownership issues + let lib_path_clone = lib_path.clone(); + libraries.insert(lib_path_clone); + + // Recursively check this library for more libraries + parse_library_folders_vdf(&lib_path, libraries); + } + } + } + } + } +} + +/// Parse an appmanifest ACF file to extract game information +fn parse_appmanifest(path: &Path) -> Option<(String, String, String)> { + match fs::read_to_string(path) { + Ok(content) => { + // Use regex to extract the app ID, name, and install directory + let re_appid = Regex::new(r#""appid"\s+"(\d+)""#).unwrap(); + let re_name = Regex::new(r#""name"\s+"([^"]+)""#).unwrap(); + let re_installdir = Regex::new(r#""installdir"\s+"([^"]+)""#).unwrap(); + + if let (Some(app_id_cap), Some(name_cap), Some(dir_cap)) = ( + re_appid.captures(&content), + re_name.captures(&content), + re_installdir.captures(&content) + ) { + let app_id = app_id_cap[1].to_string(); + let name = name_cap[1].to_string(); + let install_dir = dir_cap[1].to_string(); + + return Some((app_id, name, install_dir)); + } + } + Err(e) => { + error!("Failed to read ACF file {}: {}", path.display(), e); + } + } + + None +} + +/// Check if a file is a Linux ELF binary +fn is_elf_binary(path: &Path) -> bool { + if let Ok(mut file) = fs::File::open(path) { + let mut buffer = [0; 4]; + if file.read_exact(&mut buffer).is_ok() { + // Check for ELF magic number (0x7F 'E' 'L' 'F') + return buffer[0] == 0x7F && buffer[1] == b'E' && buffer[2] == b'L' && buffer[3] == b'F'; + } + } + + false +} + +/// Check if a game has CreamLinux installed +fn check_creamlinux_installed(game_path: &Path) -> bool { + let cream_files = [ + "cream.sh", + "cream_api.ini", + "cream_api.so", + ]; + + for file in &cream_files { + if game_path.join(file).exists() { + debug!("CreamLinux installation detected: {}", file); + return true; + } + } + + false +} + +/// Check if a game has SmokeAPI installed +fn check_smokeapi_installed(game_path: &Path, api_files: &[String]) -> bool { + if api_files.is_empty() { + return false; + } + + // SmokeAPI creates backups with _o.dll suffix + for api_file in api_files { + let api_path = game_path.join(api_file); + let api_dir = api_path.parent().unwrap_or(game_path); + let api_filename = api_path.file_name().unwrap_or_default(); + + // Check for backup file (original file renamed with _o.dll suffix) + let backup_name = api_filename.to_string_lossy().replace(".dll", "_o.dll"); + let backup_path = api_dir.join(backup_name); + + if backup_path.exists() { + debug!("SmokeAPI backup file found: {}", backup_path.display()); + return true; + } + } + + false +} + +/// Scan a game directory to determine if it's native or needs Proton +/// Also collect any Steam API DLLs for potential SmokeAPI installation +fn scan_game_directory(game_path: &Path) -> (bool, Vec) { + let mut found_exe = false; + let mut found_linux_binary = false; + let mut steam_api_files = Vec::new(); + + // Directories to skip for better performance + let skip_dirs = [ + "videos", "video", "movies", "movie", + "sound", "sounds", "audio", + "textures", "music", "localization", + "shaders", "logs", "assets/audio", + "assets/video", "assets/textures" + ]; + + // Only scan to a reasonable depth (avoid extreme recursion) + const MAX_DEPTH: usize = 8; + + // File extensions to check for (executable and Steam API files) + let exe_extensions = ["exe", "bat", "cmd", "msi"]; + let binary_extensions = ["so", "bin", "sh", "x86", "x86_64"]; + + // Recursively walk through the game directory with optimized settings + for entry in WalkDir::new(game_path) + .max_depth(MAX_DEPTH) // Limit depth to avoid traversing too deep + .follow_links(false) // Don't follow symlinks to prevent cycles + .into_iter() + .filter_entry(|e| { + // Skip certain directories for performance + if e.file_type().is_dir() { + let file_name = e.file_name().to_string_lossy().to_lowercase(); + if skip_dirs.iter().any(|&dir| file_name == dir) { + debug!("Skipping directory: {}", e.path().display()); + return false; + } + } + true + }) + .filter_map(Result::ok) { + + let path = entry.path(); + if !path.is_file() { + continue; + } + + // Check file extension + if let Some(ext) = path.extension() { + let ext_str = ext.to_string_lossy().to_lowercase(); + + // Check for Windows executables + if exe_extensions.iter().any(|&e| ext_str == e) { + found_exe = true; + } + + // Check for Steam API DLLs + if ext_str == "dll" { + let filename = path.file_name().unwrap_or_default().to_string_lossy().to_lowercase(); + if filename == "steam_api.dll" || filename == "steam_api64.dll" { + if let Ok(rel_path) = path.strip_prefix(game_path) { + let rel_path_str = rel_path.to_string_lossy().to_string(); + debug!("Found Steam API DLL: {}", rel_path_str); + steam_api_files.push(rel_path_str); + } + } + } + + // Check for Linux binary files + if binary_extensions.iter().any(|&e| ext_str == e) { + found_linux_binary = true; + + // Check if it's actually an ELF binary for more certainty + if ext_str == "so" && is_elf_binary(path) { + found_linux_binary = true; + } + } + } + + // Check for Linux executables (no extension) + #[cfg(unix)] + if !path.extension().is_some() { + use std::os::unix::fs::PermissionsExt; + + if let Ok(metadata) = path.metadata() { + let is_executable = metadata.permissions().mode() & 0o111 != 0; + + // Check executable permission and ELF format + if is_executable && is_elf_binary(path) { + found_linux_binary = true; + } + } + } + + // If we've found enough evidence for both platforms and Steam API DLLs, we can stop + // This early break greatly improves performance for large game directories + if found_exe && found_linux_binary && !steam_api_files.is_empty() { + debug!("Found sufficient evidence, breaking scan early"); + break; + } + } + + // A game is considered native if it has Linux binaries but no Windows executables + let is_native = found_linux_binary && !found_exe; + + debug!("Game scan results: native={}, exe={}, api_dlls={}", is_native, found_exe, steam_api_files.len()); + (is_native, steam_api_files) +} + +/// Find all installed Steam games from library folders +pub async fn find_installed_games(steamapps_paths: &[PathBuf]) -> Vec { + + let mut games = Vec::new(); + let seen_ids = Arc::new(tokio::sync::Mutex::new(HashSet::new())); + + // IDs to skip (tools, redistributables, etc.) + let skip_ids = Arc::new([ + "228980", // Steamworks Common Redistributables + "1070560", // Steam Linux Runtime + "1391110", // Steam Linux Runtime - Soldier + "1628350", // Steam Linux Runtime - Sniper + "1493710", // Proton Experimental + "2180100", // Steam Linux Runtime - Scout + ].iter().copied().collect::>()); + + // Name patterns to skip (case insensitive) + let skip_patterns = Arc::new( + [ + r"(?i)steam linux runtime", + r"(?i)proton", + r"(?i)steamworks common", + r"(?i)redistributable", + r"(?i)dotnet", + r"(?i)vc redist", + ] + .iter() + .map(|pat| Regex::new(pat).unwrap()) + .collect::>() + ); + + info!("Scanning for installed games in parallel..."); + + // Create a channel to collect results + let (tx, mut rx) = mpsc::channel(32); + + // First collect all appmanifest files to process + let mut app_manifests = Vec::new(); + for steamapps_dir in steamapps_paths { + if let Ok(entries) = fs::read_dir(steamapps_dir) { + for entry in entries.flatten() { + let path = entry.path(); + let filename = path.file_name().unwrap_or_default().to_string_lossy(); + + // Check for appmanifest files + if filename.starts_with("appmanifest_") && filename.ends_with(".acf") { + app_manifests.push((path, steamapps_dir.clone())); + } + } + } + } + + info!("Found {} appmanifest files to process", app_manifests.len()); + + // Process each appmanifest file in parallel with a maximum concurrency + let max_concurrent = num_cpus::get().max(1).min(8); // Use between 1 and 8 CPU cores + info!("Using {} concurrent scanners", max_concurrent); + + // Use a semaphore to limit concurrency + let semaphore = Arc::new(tokio::sync::Semaphore::new(max_concurrent)); + + // Create a Vec to store all our task handles + let mut handles = Vec::new(); + + // Process each manifest file + for (manifest_idx, (path, steamapps_dir)) in app_manifests.iter().enumerate() { + // Clone what we need for the task + let path = path.clone(); + let steamapps_dir = steamapps_dir.clone(); + let skip_patterns = Arc::clone(&skip_patterns); + let tx = tx.clone(); + let seen_ids = Arc::clone(&seen_ids); + let semaphore = Arc::clone(&semaphore); + let skip_ids = Arc::clone(&skip_ids); + + // Create a new task + let handle = tokio::spawn(async move { + // Acquire a permit from the semaphore + let _permit = semaphore.acquire().await.unwrap(); + + // Parse the appmanifest file + if let Some((id, name, install_dir)) = parse_appmanifest(&path) { + // Skip if in exclusion list + if skip_ids.contains(id.as_str()) { + return; + } + + // Add a guard against duplicates + { + let mut seen = seen_ids.lock().await; + if seen.contains(&id) { + return; + } + seen.insert(id.clone()); + } + + // Skip if the name matches any exclusion patterns + if skip_patterns.iter().any(|re| re.is_match(&name)) { + debug!("Skipping runtime/tool: {} ({})", name, id); + return; + } + + // Full path to the game directory + let game_path = steamapps_dir.join("common").join(&install_dir); + + // Skip if game directory doesn't exist + if !game_path.exists() { + warn!("Game directory not found: {}", game_path.display()); + return; + } + + // Scan the game directory to determine platform and find Steam API DLLs + info!("Scanning game: {} at {}", name, game_path.display()); + + // Scanning is I/O heavy but not CPU heavy, so we can just do it directly + let (is_native, api_files) = scan_game_directory(&game_path); + + // Check for CreamLinux installation + let cream_installed = check_creamlinux_installed(&game_path); + + // Check for SmokeAPI installation (only for non-native games with Steam API DLLs) + let smoke_installed = if !is_native && !api_files.is_empty() { + check_smokeapi_installed(&game_path, &api_files) + } else { + false + }; + + // Create the game info + let game_info = GameInfo { + id, + title: name, + path: game_path, + native: is_native, + api_files, + cream_installed, + smoke_installed, + }; + + // Send the game info through the channel + if tx.send(game_info).await.is_err() { + error!("Failed to send game info through channel"); + } + } + }); + + handles.push(handle); + + // Every 10 files, yield to allow progress updates + if manifest_idx % 10 == 0 { + // We would update progress here in a full implementation + tokio::task::yield_now().await; + } + } + + // Drop the original sender so the receiver knows when we're done + drop(tx); + + // Spawn a task to collect all the results + let receiver_task = tokio::spawn(async move { + let mut results = Vec::new(); + while let Some(game) = rx.recv().await { + info!("Found game: {} ({})", game.title, game.id); + info!(" Path: {}", game.path.display()); + info!(" Status: Native={}, Cream={}, Smoke={}", + game.native, game.cream_installed, game.smoke_installed); + + // Log Steam API DLLs if any + if !game.api_files.is_empty() { + info!(" Steam API files:"); + for api_file in &game.api_files { + info!(" - {}", api_file); + } + } + + results.push(game); + } + results + }); + + // Wait for all scan tasks to complete - but don't wait for the results yet + for handle in handles { + // Ignore errors - the receiver task will just get fewer results + let _ = handle.await; + } + + // Now wait for all results to be collected + if let Ok(results) = receiver_task.await { + games = results; + } + + info!("Found {} installed games", games.len()); + games +} \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..2e4e70d --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,41 @@ +{ + "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", + "build": { + "frontendDist": "../dist", + "devUrl": "http://localhost:1420", + "beforeDevCommand": "npm run dev", + "beforeBuildCommand": "npm run build" + }, + "bundle": { + "active": true, + "targets": "all", + "category": "Utility", + "icon": [ + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.png" + ] + }, + "productName": "Creamlinux", + "mainBinaryName": "creamlinux", + "version": "0.1.0", + "identifier": "com.creamlinux.dev", + "plugins": {}, + "app": { + "withGlobalTauri": false, + "windows": [ + { + "title": "Creamlinux", + "width": 1000, + "height": 700, + "minWidth": 800, + "minHeight": 600, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": null + } + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..fcec414 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,866 @@ +import { useState, useEffect, useRef, useCallback } from 'react'; +import { invoke } from '@tauri-apps/api/core'; +import { listen } from '@tauri-apps/api/event'; +import './styles/main.scss'; +import GameList from './components/GameList'; +import Header from './components/Header'; +import Sidebar from './components/Sidebar'; +import ProgressDialog from './components/ProgressDialog'; +import DlcSelectionDialog from './components/DlcSelectionDialog'; +import AnimatedBackground from './components/AnimatedBackground'; +import InitialLoadingScreen from './components/InitialLoadingScreen'; +import { ActionType } from './components/ActionButton'; + +// Game interface +interface Game { + id: string; + title: string; + path: string; + native: boolean; + platform?: string; + api_files: string[]; + cream_installed?: boolean; + smoke_installed?: boolean; + installing?: boolean; +} + +// Interface for installation instructions +interface InstructionInfo { + type: string; + command: string; + game_title: string; + dlc_count?: number; +} + +// Interface for DLC information +interface DlcInfo { + appid: string; + name: string; + enabled: boolean; +} + +function App() { + const [games, setGames] = useState([]); + const [filter, setFilter] = useState("all"); + const [searchQuery, setSearchQuery] = useState(""); // Added search query state + const [isLoading, setIsLoading] = useState(true); + const [isInitialLoad, setIsInitialLoad] = useState(true); + const [scanProgress, setScanProgress] = useState({ + message: "Initializing...", + progress: 0 + }); + const [error, setError] = useState(null); + const refreshInProgress = useRef(false); + const [isFetchingDlcs, setIsFetchingDlcs] = useState(false); + const dlcFetchController = useRef(null); + const activeDlcFetchId = useRef(null); + + // Progress dialog state + const [progressDialog, setProgressDialog] = useState({ + visible: false, + title: '', + message: '', + progress: 0, + showInstructions: false, + instructions: undefined as InstructionInfo | undefined + }); + + // DLC selection dialog state + const [dlcDialog, setDlcDialog] = useState({ + visible: false, + gameId: '', + gameTitle: '', + dlcs: [] as DlcInfo[], + enabledDlcs: [] as string[], + isLoading: false, + isEditMode: false, + progress: 0, + progressMessage: '', + timeLeft: '', + error: null as string | null + }); + + // Handle search query changes + const handleSearchChange = (query: string) => { + setSearchQuery(query); + }; + + // Move the loadGames function outside of the useEffect to make it reusable + const loadGames = useCallback(async () => { + try { + setIsLoading(true); + setError(null); + + console.log("Invoking scan_steam_games"); + const steamGames = await invoke('scan_steam_games').catch(err => { + console.error('Error from scan_steam_games:', err); + throw err; + }); + + // Add platform property to match the GameList component's expectation + const gamesWithPlatform = steamGames.map(game => ({ + ...game, + platform: 'Steam' + })); + + console.log(`Loaded ${gamesWithPlatform.length} games`); + setGames(gamesWithPlatform); + setIsInitialLoad(false); // Mark initial load as complete + return true; + } catch (error) { + console.error('Error loading games:', error); + setError(`Failed to load games: ${error}`); + setIsInitialLoad(false); // Mark initial load as complete even on error + return false; + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + // Set up event listeners first + const setupEventListeners = async () => { + try { + console.log("Setting up event listeners"); + + // Listen for progress updates from the backend + const unlistenProgress = await listen('installation-progress', (event) => { + console.log("Received installation-progress event:", event); + + const { + title, + message, + progress, + complete, + show_instructions, + instructions + } = event.payload as { + title: string; + message: string; + progress: number; + complete: boolean; + show_instructions?: boolean; + instructions?: InstructionInfo; + }; + + if (complete && !show_instructions) { + // Hide dialog when complete if no instructions + setTimeout(() => { + setProgressDialog(prev => ({ ...prev, visible: false })); + + // Only refresh games list if dialog is closing without instructions + if (!refreshInProgress.current) { + refreshInProgress.current = true; + setTimeout(() => { + loadGames().then(() => { + refreshInProgress.current = false; + }); + }, 100); + } + }, 1000); + } else { + // Update progress dialog + setProgressDialog({ + visible: true, + title, + message, + progress, + showInstructions: show_instructions || false, + instructions + }); + } + }); + + // Listen for scan progress events + const unlistenScanProgress = await listen('scan-progress', (event) => { + const { message, progress } = event.payload as { + message: string; + progress: number; + }; + + console.log("Received scan-progress event:", message, progress); + + // Update scan progress state + setScanProgress({ + message, + progress + }); + }); + + // Listen for individual game updates + const unlistenGameUpdated = await listen('game-updated', (event) => { + console.log("Received game-updated event:", event); + + const updatedGame = event.payload as Game; + + // Update only the specific game in the state + setGames(prevGames => + prevGames.map(game => + game.id === updatedGame.id ? { ...updatedGame, platform: 'Steam' } : game + ) + ); + }); + + return () => { + unlistenProgress(); + unlistenScanProgress(); + unlistenGameUpdated(); + }; + } catch (error) { + console.error("Error setting up event listeners:", error); + return () => {}; + } + }; + + // First set up event listeners, then load games + let unlisten: (() => void) | null = null; + + setupEventListeners() + .then(unlistenFn => { + unlisten = unlistenFn; + return loadGames(); + }) + .catch(error => { + console.error("Failed to initialize:", error); + }); + + return () => { + if (unlisten) { + unlisten(); + } + }; + }, [loadGames]); + + // Debugging for state changes + useEffect(() => { + // Debug state changes + if (games.length > 0) { + // Count native and installed games + const nativeCount = games.filter(g => g.native).length; + const creamInstalledCount = games.filter(g => g.cream_installed).length; + const smokeInstalledCount = games.filter(g => g.smoke_installed).length; + + console.log(`Game state updated: ${games.length} total games, ${nativeCount} native, ${creamInstalledCount} with CreamLinux, ${smokeInstalledCount} with SmokeAPI`); + + // Log any games with unexpected states + const problematicGames = games.filter(g => { + // Native games that have SmokeAPI installed (shouldn't happen) + if (g.native && g.smoke_installed) return true; + + // Non-native games with CreamLinux installed (shouldn't happen) + if (!g.native && g.cream_installed) return true; + + // Non-native games without API files but with SmokeAPI installed (shouldn't happen) + if (!g.native && (!g.api_files || g.api_files.length === 0) && g.smoke_installed) return true; + + return false; + }); + + if (problematicGames.length > 0) { + console.warn("Found games with unexpected states:", problematicGames); + } + } + }, [games]); + + // Set up event listeners for DLC streaming + useEffect(() => { + // Listen for individual DLC found events + const setupDlcEventListeners = async () => { + try { + // This event is emitted for each DLC as it's found + const unlistenDlcFound = await listen('dlc-found', (event) => { + const dlc = JSON.parse(event.payload as string) as { appid: string, name: string }; + + // Add the DLC to the current list with enabled=true + setDlcDialog(prev => ({ + ...prev, + dlcs: [...prev.dlcs, { ...dlc, enabled: true }] + })); + }); + + // When progress is 100%, mark loading as complete and reset fetch state + const unlistenDlcProgress = await listen('dlc-progress', (event) => { + const { message, progress, timeLeft } = event.payload as { + message: string, + progress: number, + timeLeft?: string + }; + + // Update the progress indicator + setDlcDialog(prev => ({ + ...prev, + progress, + progressMessage: message, + timeLeft: timeLeft || '' + })); + + // If progress is 100%, mark loading as complete + if (progress === 100) { + setTimeout(() => { + setDlcDialog(prev => ({ + ...prev, + isLoading: false + })); + + // Reset fetch state + setIsFetchingDlcs(false); + activeDlcFetchId.current = null; + }, 500); + } + }); + + // This event is emitted if there's an error + const unlistenDlcError = await listen('dlc-error', (event) => { + const { error } = event.payload as { error: string }; + console.error('DLC streaming error:', error); + + // Show error in dialog + setDlcDialog(prev => ({ + ...prev, + error, + isLoading: false + })); + }); + + return () => { + unlistenDlcFound(); + unlistenDlcProgress(); + unlistenDlcError(); + }; + } catch (error) { + console.error("Error setting up DLC event listeners:", error); + return () => {}; + } + }; + + const unlisten = setupDlcEventListeners(); + return () => { + unlisten.then(fn => fn()); + }; + }, []); + + // Listen for scan progress events + useEffect(() => { + const listenToScanProgress = async () => { + try { + const unlistenScanProgress = await listen('scan-progress', (event) => { + const { message, progress } = event.payload as { + message: string; + progress: number; + }; + + // Update loading message + setProgressDialog(prev => ({ + ...prev, + visible: true, + title: "Scanning for Games", + message, + progress, + showInstructions: false, + instructions: undefined + })); + + // Auto-close when complete + if (progress >= 100) { + setTimeout(() => { + setProgressDialog(prev => ({ ...prev, visible: false })); + }, 1500); + } + }); + + return unlistenScanProgress; + } catch (error) { + console.error("Error setting up scan progress listener:", error); + return () => {}; + } + }; + + const unlistenPromise = listenToScanProgress(); + return () => { + unlistenPromise.then(unlisten => unlisten()); + }; + }, []); + + const handleCloseProgressDialog = () => { + // Just hide the dialog without refreshing game list + setProgressDialog(prev => ({ ...prev, visible: false })); + + // Only refresh if we need to (instructions didn't trigger update) + if (progressDialog.showInstructions === false && !refreshInProgress.current) { + refreshInProgress.current = true; + setTimeout(() => { + loadGames().then(() => { + refreshInProgress.current = false; + }); + }, 100); + } + }; + + // Function to fetch DLCs for a game with streaming updates + const streamGameDlcs = async (gameId: string): Promise => { + try { + // Set up flag to indicate we're fetching DLCs + setIsFetchingDlcs(true); + activeDlcFetchId.current = gameId; + + // Start streaming DLCs - this won't return DLCs directly + // Instead, it triggers events that we'll listen for + await invoke('stream_game_dlcs', { gameId }); + + return; + } catch (error) { + if (error instanceof DOMException && error.name === 'AbortError') { + console.log('DLC fetching was aborted'); + } else { + console.error('Error starting DLC stream:', error); + throw error; + } + } finally { + // Reset state when done or on error + setIsFetchingDlcs(false); + activeDlcFetchId.current = null; + } + }; + + // Clean up if component unmounts during a fetch + useEffect(() => { + return () => { + // Clean up any ongoing fetch operations + if (dlcFetchController.current) { + dlcFetchController.current.abort(); + dlcFetchController.current = null; + } + }; + }, []); + + // Handle game edit (show DLC management dialog) + const handleGameEdit = async (gameId: string) => { + const game = games.find(g => g.id === gameId); + if (!game || !game.cream_installed) return; + + // Check if we're already fetching DLCs for this game + if (isFetchingDlcs && activeDlcFetchId.current === gameId) { + console.log(`Already fetching DLCs for ${gameId}, ignoring duplicate request`); + return; + } + + try { + // Show dialog immediately with empty DLC list + setDlcDialog({ + visible: true, + gameId, + gameTitle: game.title, + dlcs: [], + enabledDlcs: [] as string[], + isLoading: true, + isEditMode: true, + progress: 0, + progressMessage: 'Reading DLC configuration...', + timeLeft: '', + error: null + }); + + // Try to read all DLCs from the configuration file first (including disabled ones) + try { + const allDlcs = await invoke('get_all_dlcs_command', { gamePath: game.path }) + .catch(() => [] as DlcInfo[]); + + if (allDlcs.length > 0) { + // If we have DLCs from the config file, use them + console.log("Loaded existing DLC configuration:", allDlcs); + + setDlcDialog(prev => ({ + ...prev, + dlcs: allDlcs, + isLoading: false, + progress: 100, + progressMessage: 'Loaded existing DLC configuration' + })); + return; + } + } catch (error) { + console.warn("Could not read existing DLC configuration, falling back to API:", error); + // Continue with API loading if config reading fails + } + + // Mark that we're fetching DLCs for this game + setIsFetchingDlcs(true); + activeDlcFetchId.current = gameId; + + // Create abort controller for fetch operation + dlcFetchController.current = new AbortController(); + + // Start streaming DLCs + await streamGameDlcs(gameId).catch(error => { + if (error.name !== 'AbortError') { + console.error('Error streaming DLCs:', error); + setDlcDialog(prev => ({ + ...prev, + error: `Failed to load DLCs: ${error}`, + isLoading: false + })); + } + }); + + // In parallel, try to get the enabled DLCs + const enabledDlcs = await invoke('get_enabled_dlcs_command', { gamePath: game.path }) + .catch(() => [] as string[]); + + // We'll update the enabled state of DLCs as they come in + setDlcDialog(prev => ({ + ...prev, + enabledDlcs + })); + + } catch (error) { + console.error('Error preparing DLC edit:', error); + setDlcDialog(prev => ({ + ...prev, + error: `Failed to prepare DLC editor: ${error}`, + isLoading: false + })); + } + }; + + // Unified handler for all game actions (install/uninstall cream/smoke) + const handleGameAction = async (gameId: string, action: ActionType) => { + try { + // Find game to get title + const game = games.find(g => g.id === gameId); + if (!game) return; + + // If we're installing CreamLinux, show DLC selection first + if (action === 'install_cream') { + try { + // Show dialog immediately with empty DLC list and loading state + setDlcDialog({ + visible: true, + gameId, + gameTitle: game.title, + dlcs: [], // Start with an empty array + enabledDlcs: [] as string[], + isLoading: true, + isEditMode: false, + progress: 0, + progressMessage: 'Fetching DLC list...', + timeLeft: '', + error: null + }); + + // Start streaming DLCs - only once + await streamGameDlcs(gameId).catch(error => { + console.error('Error streaming DLCs:', error); + setDlcDialog(prev => ({ + ...prev, + error: `Failed to load DLCs: ${error}`, + isLoading: false + })); + }); + + } catch (error) { + console.error('Error fetching DLCs:', error); + + // If DLC fetching fails, close dialog and show error + setDlcDialog(prev => ({ + ...prev, + visible: false, + isLoading: false + })); + + setProgressDialog({ + visible: true, + title: `Error fetching DLCs for ${game.title}`, + message: `Failed to fetch DLCs: ${error}`, + progress: 100, + showInstructions: false, + instructions: undefined + }); + + setTimeout(() => { + setProgressDialog(prev => ({ ...prev, visible: false })); + }, 3000); + } + return; + } + + // For other actions, proceed directly + // Update local state to show installation in progress + setGames(prevGames => prevGames.map(g => + g.id === gameId ? { ...g, installing: true } : g + )); + + // Get title based on action + const isCream = action.includes('cream'); + const isInstall = action.includes('install'); + const product = isCream ? "CreamLinux" : "SmokeAPI"; + const operation = isInstall ? "Installing" : "Uninstalling"; + + // Show progress dialog + setProgressDialog({ + visible: true, + title: `${operation} ${product} for ${game.title}`, + message: isInstall ? 'Downloading required files...' : 'Removing files...', + progress: isInstall ? 0 : 30, + showInstructions: false, + instructions: undefined + }); + + console.log(`Invoking process_game_action for game ${gameId} with action ${action}`); + + // Call the backend with the unified action + const updatedGame = await invoke('process_game_action', { + gameAction: { + game_id: gameId, + action + } + }).catch(err => { + console.error(`Error from process_game_action:`, err); + throw err; + }); + + console.log('Game action completed, updated game:', updatedGame); + + // Update our local state with the result from the backend + if (updatedGame) { + setGames(prevGames => prevGames.map(g => + g.id === gameId ? { ...g, installing: false } : g + )); + } + + } catch (error) { + console.error(`Error processing action ${action} for game ${gameId}:`, error); + + // Show error in progress dialog + setProgressDialog(prev => ({ + ...prev, + message: `Error: ${error}`, + progress: 100 + })); + + // Reset installing state + setGames(prevGames => prevGames.map(game => + game.id === gameId ? { ...game, installing: false } : game + )); + + // Hide dialog after a delay + setTimeout(() => { + setProgressDialog(prev => ({ ...prev, visible: false })); + }, 3000); + } + }; + + // Handle DLC selection dialog close + const handleDlcDialogClose = () => { + // Cancel any in-progress DLC fetching + if (isFetchingDlcs && activeDlcFetchId.current) { + console.log(`Aborting DLC fetch for game ${activeDlcFetchId.current}`); + + // This will signal to the Rust backend that we want to stop the process + // You could implement this on the backend if needed with something like: + invoke('abort_dlc_fetch', { gameId: activeDlcFetchId.current }) + .catch(err => console.error('Error aborting DLC fetch:', err)); + + // Reset state + activeDlcFetchId.current = null; + setIsFetchingDlcs(false); + } + + // Clear controller + if (dlcFetchController.current) { + dlcFetchController.current.abort(); + dlcFetchController.current = null; + } + + // Close dialog + setDlcDialog(prev => ({ ...prev, visible: false })); + }; + + // Handle DLC selection confirmation + const handleDlcConfirm = async (selectedDlcs: DlcInfo[]) => { + // Close the dialog first + setDlcDialog(prev => ({ ...prev, visible: false })); + + const gameId = dlcDialog.gameId; + const game = games.find(g => g.id === gameId); + if (!game) return; + + // Update local state to show installation in progress + setGames(prevGames => prevGames.map(g => + g.id === gameId ? { ...g, installing: true } : g + )); + + try { + if (dlcDialog.isEditMode) { + // If in edit mode, we're updating existing cream_api.ini + // Show progress dialog for editing + setProgressDialog({ + visible: true, + title: `Updating DLCs for ${game.title}`, + message: 'Updating DLC configuration...', + progress: 30, + showInstructions: false, + instructions: undefined + }); + + // Call the backend to update the DLC configuration + await invoke('update_dlc_configuration_command', { + gamePath: game.path, + dlcs: selectedDlcs + }); + + // Update progress dialog for completion + setProgressDialog(prev => ({ + ...prev, + title: `Update Complete: ${game.title}`, + message: 'DLC configuration updated successfully!', + progress: 100 + })); + + // Hide dialog after a delay + setTimeout(() => { + setProgressDialog(prev => ({ ...prev, visible: false })); + // Reset installing state + setGames(prevGames => prevGames.map(g => + g.id === gameId ? { ...g, installing: false } : g + )); + }, 2000); + } else { + // We're doing a fresh install with selected DLCs + // Show progress dialog for installation right away + setProgressDialog({ + visible: true, + title: `Installing CreamLinux for ${game.title}`, + message: 'Processing...', + progress: 0, + showInstructions: false, + instructions: undefined + }); + + // Invoke the installation with the selected DLCs + await invoke('install_cream_with_dlcs_command', { + gameId, + selectedDlcs + }).catch(err => { + console.error(`Error installing CreamLinux with selected DLCs:`, err); + throw err; + }); + + // Note: we don't need to manually close the dialog or update the game state + // because the backend will emit progress events that handle this + } + } catch (error) { + console.error('Error processing DLC selection:', error); + + // Show error in progress dialog + setProgressDialog(prev => ({ + ...prev, + message: `Error: ${error}`, + progress: 100 + })); + + // Reset installing state + setGames(prevGames => prevGames.map(g => + g.id === gameId ? { ...g, installing: false } : g + )); + + // Hide dialog after a delay + setTimeout(() => { + setProgressDialog(prev => ({ ...prev, visible: false })); + }, 3000); + } + }; + + // Update DLCs being streamed with enabled state + useEffect(() => { + if (dlcDialog.enabledDlcs.length > 0) { + setDlcDialog(prev => ({ + ...prev, + dlcs: prev.dlcs.map(dlc => ({ + ...dlc, + enabled: prev.enabledDlcs.length === 0 || prev.enabledDlcs.includes(dlc.appid) + })) + })); + } + }, [dlcDialog.dlcs, dlcDialog.enabledDlcs]); + + // Filter games based on sidebar filter AND search query + const filteredGames = games.filter(game => { + // First filter by the platform/type + const platformMatch = filter === "all" || + (filter === "native" && game.native) || + (filter === "proton" && !game.native); + + // Then filter by search query (if any) + const searchMatch = searchQuery.trim() === '' || + game.title.toLowerCase().includes(searchQuery.toLowerCase()); + + // Both filters must match + return platformMatch && searchMatch; + }); + + // Check if we should show the initial loading screen + if (isInitialLoad) { + return ( + + ); + } + + return ( +
+ {/* Animated background */} + + +
+
+ + {error ? ( +
+

Error Loading Games

+

{error}

+ +
+ ) : ( + + )} +
+ + {/* Progress Dialog */} + + + {/* DLC Selection Dialog */} + +
+ ); +} + +export default App; \ No newline at end of file diff --git a/src/assets/fonts/Roboto.ttf b/src/assets/fonts/Roboto.ttf new file mode 100644 index 0000000..bba55f6 Binary files /dev/null and b/src/assets/fonts/Roboto.ttf differ diff --git a/src/assets/fonts/Satoshi.ttf b/src/assets/fonts/Satoshi.ttf new file mode 100644 index 0000000..976e85c Binary files /dev/null and b/src/assets/fonts/Satoshi.ttf differ diff --git a/src/assets/fonts/WorkSans.ttf b/src/assets/fonts/WorkSans.ttf new file mode 100644 index 0000000..9a82798 Binary files /dev/null and b/src/assets/fonts/WorkSans.ttf differ diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/screenshot.png b/src/assets/screenshot.png new file mode 100644 index 0000000..98847d9 Binary files /dev/null and b/src/assets/screenshot.png differ diff --git a/src/components/ActionButton.tsx b/src/components/ActionButton.tsx new file mode 100644 index 0000000..a084680 --- /dev/null +++ b/src/components/ActionButton.tsx @@ -0,0 +1,46 @@ +// src/components/ActionButton.tsx +import React from 'react'; + +export type ActionType = 'install_cream' | 'uninstall_cream' | 'install_smoke' | 'uninstall_smoke'; + +interface ActionButtonProps { + action: ActionType; + isInstalled: boolean; + isWorking: boolean; + onClick: () => void; + disabled?: boolean; +} + +const ActionButton: React.FC = ({ + action, + isInstalled, + isWorking, + onClick, + disabled = false +}) => { + const getButtonText = () => { + if (isWorking) return "Working..."; + + const isCream = action.includes('cream'); + const product = isCream ? "CreamLinux" : "SmokeAPI"; + + return isInstalled ? `Uninstall ${product}` : `Install ${product}`; + }; + + const getButtonClass = () => { + const baseClass = "action-button"; + return `${baseClass} ${isInstalled ? 'uninstall' : 'install'}`; + }; + + return ( + + ); +}; + +export default ActionButton; \ No newline at end of file diff --git a/src/components/AnimatedBackground.tsx b/src/components/AnimatedBackground.tsx new file mode 100644 index 0000000..5ce2fea --- /dev/null +++ b/src/components/AnimatedBackground.tsx @@ -0,0 +1,127 @@ +// src/components/AnimatedBackground.tsx +import React, { useEffect, useRef } from 'react'; + +const AnimatedBackground: React.FC = () => { + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Set canvas size to match window + const setCanvasSize = () => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }; + + setCanvasSize(); + window.addEventListener('resize', setCanvasSize); + + // Create particles + const particles: Particle[] = []; + const particleCount = 30; + + interface Particle { + x: number; + y: number; + size: number; + speedX: number; + speedY: number; + opacity: number; + color: string; + } + + // Color palette + const colors = [ + 'rgba(74, 118, 196, 0.5)', // primary blue + 'rgba(155, 125, 255, 0.5)', // purple + 'rgba(251, 177, 60, 0.5)', // gold + ]; + + // Create initial particles + for (let i = 0; i < particleCount; i++) { + particles.push({ + x: Math.random() * canvas.width, + y: Math.random() * canvas.height, + size: Math.random() * 3 + 1, + speedX: Math.random() * 0.2 - 0.1, + speedY: Math.random() * 0.2 - 0.1, + opacity: Math.random() * 0.07 + 0.03, + color: colors[Math.floor(Math.random() * colors.length)] + }); + } + + // Animation loop + const animate = () => { + // Clear canvas with transparent black to create fade effect + ctx.fillStyle = 'rgba(15, 15, 15, 0.1)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Update and draw particles + particles.forEach(particle => { + // Update position + particle.x += particle.speedX; + particle.y += particle.speedY; + + // Wrap around edges + if (particle.x < 0) particle.x = canvas.width; + if (particle.x > canvas.width) particle.x = 0; + if (particle.y < 0) particle.y = canvas.height; + if (particle.y > canvas.height) particle.y = 0; + + // Draw particle + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); + ctx.fillStyle = particle.color.replace('0.5', `${particle.opacity}`); + ctx.fill(); + + // Connect particles + particles.forEach(otherParticle => { + const dx = particle.x - otherParticle.x; + const dy = particle.y - otherParticle.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < 100) { + ctx.beginPath(); + ctx.strokeStyle = particle.color.replace('0.5', `${particle.opacity * 0.5}`); + ctx.lineWidth = 0.2; + ctx.moveTo(particle.x, particle.y); + ctx.lineTo(otherParticle.x, otherParticle.y); + ctx.stroke(); + } + }); + }); + + requestAnimationFrame(animate); + }; + + // Start animation + animate(); + + return () => { + window.removeEventListener('resize', setCanvasSize); + }; + }, []); + + return ( + + ); +}; + +export default AnimatedBackground; \ No newline at end of file diff --git a/src/components/AnimatedCheckbox.tsx b/src/components/AnimatedCheckbox.tsx new file mode 100644 index 0000000..e14bf63 --- /dev/null +++ b/src/components/AnimatedCheckbox.tsx @@ -0,0 +1,50 @@ +// src/components/AnimatedCheckbox.tsx +import React from 'react'; + +interface AnimatedCheckboxProps { + checked: boolean; + onChange: () => void; + label?: string; + sublabel?: string; + className?: string; +} + +const AnimatedCheckbox: React.FC = ({ + checked, + onChange, + label, + sublabel, + className = '' +}) => { + return ( + + ); +}; + +export default AnimatedCheckbox; \ No newline at end of file diff --git a/src/components/DlcSelectionDialog.tsx b/src/components/DlcSelectionDialog.tsx new file mode 100644 index 0000000..fe4006f --- /dev/null +++ b/src/components/DlcSelectionDialog.tsx @@ -0,0 +1,242 @@ +// src/components/DlcSelectionDialog.tsx +import React, { useState, useEffect, useMemo } from 'react'; +import AnimatedCheckbox from './AnimatedCheckbox'; + +interface DlcInfo { + appid: string; + name: string; + enabled: boolean; +} + +interface DlcSelectionDialogProps { + visible: boolean; + gameTitle: string; + dlcs: DlcInfo[]; + onClose: () => void; + onConfirm: (selectedDlcs: DlcInfo[]) => void; + isLoading: boolean; + isEditMode?: boolean; + loadingProgress?: number; + estimatedTimeLeft?: string; +} + +const DlcSelectionDialog: React.FC = ({ + visible, + gameTitle, + dlcs, + onClose, + onConfirm, + isLoading, + isEditMode = false, + loadingProgress = 0, + estimatedTimeLeft = '' +}) => { + const [selectedDlcs, setSelectedDlcs] = useState([]); + const [showContent, setShowContent] = useState(false); + const [searchQuery, setSearchQuery] = useState(''); + const [selectAll, setSelectAll] = useState(true); + const [initialized, setInitialized] = useState(false); + + // Initialize selected DLCs when DLC list changes + useEffect(() => { + if (visible && dlcs.length > 0 && !initialized) { + setSelectedDlcs(dlcs); + + // Determine initial selectAll state based on if all DLCs are enabled + const allSelected = dlcs.every(dlc => dlc.enabled); + setSelectAll(allSelected); + + // Mark as initialized so we don't reset selections on subsequent DLC additions + setInitialized(true); + } + }, [visible, dlcs, initialized]); + + // Handle visibility changes + useEffect(() => { + if (visible) { + // Show content immediately for better UX + const timer = setTimeout(() => { + setShowContent(true); + }, 50); + return () => clearTimeout(timer); + } else { + setShowContent(false); + setInitialized(false); // Reset initialized state when dialog closes + } + }, [visible]); + + // Memoize filtered DLCs to avoid unnecessary recalculations + const filteredDlcs = useMemo(() => { + return searchQuery.trim() === '' + ? selectedDlcs + : selectedDlcs.filter(dlc => + dlc.name.toLowerCase().includes(searchQuery.toLowerCase()) || + dlc.appid.includes(searchQuery) + ); + }, [selectedDlcs, searchQuery]); + + // Update DLC selection status + const handleToggleDlc = (appid: string) => { + setSelectedDlcs(prev => prev.map(dlc => + dlc.appid === appid ? { ...dlc, enabled: !dlc.enabled } : dlc + )); + }; + + // Update selectAll state when individual DLC selections change + useEffect(() => { + const allSelected = selectedDlcs.every(dlc => dlc.enabled); + setSelectAll(allSelected); + }, [selectedDlcs]); + + // Handle new DLCs being added while dialog is already open + useEffect(() => { + if (initialized && dlcs.length > selectedDlcs.length) { + // Find new DLCs that aren't in our current selection + const currentAppIds = new Set(selectedDlcs.map(dlc => dlc.appid)); + const newDlcs = dlcs.filter(dlc => !currentAppIds.has(dlc.appid)); + + // Add new DLCs to our selection, maintaining their enabled state + if (newDlcs.length > 0) { + setSelectedDlcs(prev => [...prev, ...newDlcs]); + } + } + }, [dlcs, selectedDlcs, initialized]); + + const handleToggleSelectAll = () => { + const newSelectAllState = !selectAll; + setSelectAll(newSelectAllState); + + setSelectedDlcs(prev => prev.map(dlc => ({ + ...dlc, + enabled: newSelectAllState + }))); + }; + + const handleConfirm = () => { + onConfirm(selectedDlcs); + }; + + // Modified to prevent closing when loading + const handleOverlayClick = (e: React.MouseEvent) => { + // Prevent clicks from propagating through the overlay + e.stopPropagation(); + + // Only allow closing via overlay click if not loading + if (e.target === e.currentTarget && !isLoading) { + onClose(); + } + }; + + // Count selected DLCs + const selectedCount = selectedDlcs.filter(dlc => dlc.enabled).length; + + // Format loading message to show total number of DLCs found + const getLoadingInfoText = () => { + if (isLoading && loadingProgress < 100) { + return ` (Loading more DLCs...)`; + } else if (dlcs.length > 0) { + return ` (Total DLCs: ${dlcs.length})`; + } + return ''; + }; + + if (!visible) return null; + + return ( +
+
+
+

{isEditMode ? 'Edit DLCs' : 'Select DLCs to Enable'}

+
+ {gameTitle} + + {selectedCount} of {selectedDlcs.length} DLCs selected + {getLoadingInfoText()} + +
+
+ +
+ setSearchQuery(e.target.value)} + className="dlc-search-input" + /> +
+ +
+
+ + {isLoading && ( +
+
+
+
+
+ Loading DLCs: {loadingProgress}% + {estimatedTimeLeft && Est. time left: {estimatedTimeLeft}} +
+
+ )} + +
+ {selectedDlcs.length > 0 ? ( +
    + {filteredDlcs.map(dlc => ( +
  • + handleToggleDlc(dlc.appid)} + label={dlc.name} + sublabel={`ID: ${dlc.appid}`} + /> +
  • + ))} + {isLoading && ( +
  • +
    +
  • + )} +
+ ) : ( +
+
+

Loading DLC information...

+
+ )} +
+ +
+ + +
+
+
+ ); +}; + +export default DlcSelectionDialog; \ No newline at end of file diff --git a/src/components/GameItem.tsx b/src/components/GameItem.tsx new file mode 100644 index 0000000..e625685 --- /dev/null +++ b/src/components/GameItem.tsx @@ -0,0 +1,172 @@ +// src/components/GameItem.tsx +import React, { useState, useEffect } from 'react'; +import { findBestGameImage } from '../services/ImageService'; +import { ActionType } from './ActionButton'; + +interface Game { + id: string; + title: string; + path: string; + platform?: string; + native: boolean; + api_files: string[]; + cream_installed?: boolean; + smoke_installed?: boolean; + installing?: boolean; +} + +interface GameItemProps { + game: Game; + onAction: (gameId: string, action: ActionType) => Promise; + onEdit?: (gameId: string) => void; +} + +const GameItem: React.FC = ({ game, onAction, onEdit }) => { + const [imageUrl, setImageUrl] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + + useEffect(() => { + // Function to fetch the game cover/image + const fetchGameImage = async () => { + // First check if we already have it (to prevent flickering on re-renders) + if (imageUrl) return; + + setIsLoading(true); + try { + // Try to find the best available image for this game + const bestImageUrl = await findBestGameImage(game.id); + + if (bestImageUrl) { + setImageUrl(bestImageUrl); + setHasError(false); + } else { + setHasError(true); + } + } catch (error) { + console.error('Error fetching game image:', error); + setHasError(true); + } finally { + setIsLoading(false); + } + }; + + if (game.id) { + fetchGameImage(); + } + }, [game.id, imageUrl]); + + // Determine if we should show CreamLinux buttons (only for native games) + const shouldShowCream = game.native === true; + + // Determine if we should show SmokeAPI buttons (only for non-native games with API files) + const shouldShowSmoke = !game.native && game.api_files && game.api_files.length > 0; + + // Check if this is a Proton game without API files + const isProtonNoApi = !game.native && (!game.api_files || game.api_files.length === 0); + + const handleCreamAction = () => { + if (game.installing) return; + const action: ActionType = game.cream_installed ? 'uninstall_cream' : 'install_cream'; + onAction(game.id, action); + }; + + const handleSmokeAction = () => { + if (game.installing) return; + const action: ActionType = game.smoke_installed ? 'uninstall_smoke' : 'install_smoke'; + onAction(game.id, action); + }; + + // Handle edit button click + const handleEdit = () => { + if (onEdit && game.cream_installed) { + onEdit(game.id); + } + }; + + // Determine background image + const backgroundImage = !isLoading && imageUrl ? + `url(${imageUrl})` : + hasError ? 'linear-gradient(135deg, #232323, #1A1A1A)' : 'linear-gradient(135deg, #232323, #1A1A1A)'; + + return ( +
+
+
+ + {game.native ? 'Native' : 'Proton'} + + {game.cream_installed && ( + CreamLinux + )} + {game.smoke_installed && ( + SmokeAPI + )} +
+ +
+

{game.title}

+
+ +
+ {/* Show CreamLinux button only for native games */} + {shouldShowCream && ( + + )} + + {/* Show SmokeAPI button only for Proton/Windows games with API files */} + {shouldShowSmoke && ( + + )} + + {/* Show message for Proton games without API files */} + {isProtonNoApi && ( +
+ Steam API DLL not found + +
+ )} + + {/* Edit button - only enabled if CreamLinux is installed */} + {game.cream_installed && ( + + )} +
+
+
+ ); +}; + +export default GameItem; \ No newline at end of file diff --git a/src/components/GameList.tsx b/src/components/GameList.tsx new file mode 100644 index 0000000..d8a5321 --- /dev/null +++ b/src/components/GameList.tsx @@ -0,0 +1,92 @@ +// src/components/GameList.tsx +import React, { useState, useEffect, useMemo } from 'react'; +import GameItem from './GameItem'; +import ImagePreloader from './ImagePreloader'; +import { ActionType } from './ActionButton'; + +interface Game { + id: string; + title: string; + path: string; + platform?: string; + native: boolean; + api_files: string[]; + cream_installed?: boolean; + smoke_installed?: boolean; + installing?: boolean; +} + +interface GameListProps { + games: Game[]; + isLoading: boolean; + onAction: (gameId: string, action: ActionType) => Promise; + onEdit?: (gameId: string) => void; +} + +const GameList: React.FC = ({ + games, + isLoading, + onAction, + onEdit +}) => { + const [imagesPreloaded, setImagesPreloaded] = useState(false); + + // Sort games alphabetically by title - using useMemo to avoid re-sorting on each render + const sortedGames = useMemo(() => { + return [...games].sort((a, b) => a.title.localeCompare(b.title)); + }, [games]); + + // Reset preloaded state when games change + useEffect(() => { + setImagesPreloaded(false); + }, [games]); + + // Debug log to help diagnose game states + useEffect(() => { + if (games.length > 0) { + console.log("Games state in GameList:", games.length, "games"); + } + }, [games]); + + if (isLoading) { + return ( +
+
Scanning for games...
+
+ ); + } + + const handlePreloadComplete = () => { + setImagesPreloaded(true); + }; + + return ( +
+

Games ({games.length})

+ + {!imagesPreloaded && games.length > 0 && ( + game.id)} + onComplete={handlePreloadComplete} + /> + )} + + {games.length === 0 ? ( +
No games found
+ ) : ( +
+ {sortedGames.map(game => ( + + ))} +
+ )} +
+ ); +}; + +export default GameList; \ No newline at end of file diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..5d8c306 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,40 @@ +// src/components/Header.tsx +import React from 'react'; + +interface HeaderProps { + onRefresh: () => void; + refreshDisabled?: boolean; + onSearch: (query: string) => void; + searchQuery: string; +} + +const Header: React.FC = ({ + onRefresh, + refreshDisabled = false, + onSearch, + searchQuery +}) => { + return ( +
+

CreamLinux

+
+ + onSearch(e.target.value)} + /> +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/components/ImagePreloader.tsx b/src/components/ImagePreloader.tsx new file mode 100644 index 0000000..1c75fd3 --- /dev/null +++ b/src/components/ImagePreloader.tsx @@ -0,0 +1,48 @@ +// src/components/ImagePreloader.tsx +import React, { useEffect } from 'react'; +import { findBestGameImage } from '../services/ImageService'; + +interface ImagePreloaderProps { + gameIds: string[]; + onComplete?: () => void; +} + +const ImagePreloader: React.FC = ({ gameIds, onComplete }) => { + useEffect(() => { + const preloadImages = async () => { + try { + // Only preload the first batch for performance (10 images max) + const batchToPreload = gameIds.slice(0, 10); + + // Load images in parallel + await Promise.allSettled( + batchToPreload.map(id => findBestGameImage(id)) + ); + + if (onComplete) { + onComplete(); + } + } catch (error) { + console.error("Error preloading images:", error); + // Continue even if there's an error + if (onComplete) { + onComplete(); + } + } + }; + + if (gameIds.length > 0) { + preloadImages(); + } else if (onComplete) { + onComplete(); + } + }, [gameIds, onComplete]); + + return ( +
+ {/* Hidden element, just used for preloading */} +
+ ); +}; + +export default ImagePreloader; \ No newline at end of file diff --git a/src/components/InitialLoadingScreen.tsx b/src/components/InitialLoadingScreen.tsx new file mode 100644 index 0000000..4fcfb2a --- /dev/null +++ b/src/components/InitialLoadingScreen.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +interface InitialLoadingScreenProps { + message: string; + progress: number; +} + +const InitialLoadingScreen: React.FC = ({ + message, + progress +}) => { + return ( +
+
+

CreamLinux

+
+
+
+
+
+
+
+

{message}

+
+
+
+
{Math.round(progress)}%
+
+
+ ); +}; + +export default InitialLoadingScreen; \ No newline at end of file diff --git a/src/components/ProgressDialog.tsx b/src/components/ProgressDialog.tsx new file mode 100644 index 0000000..2bbfa9f --- /dev/null +++ b/src/components/ProgressDialog.tsx @@ -0,0 +1,215 @@ +// src/components/ProgressDialog.tsx +import React, { useState, useEffect } from 'react'; + +interface InstructionInfo { + type: string; + command: string; + game_title: string; + dlc_count?: number; +} + +interface ProgressDialogProps { + title: string; + message: string; + progress: number; // 0-100 + visible: boolean; + showInstructions?: boolean; + instructions?: InstructionInfo; + onClose?: () => void; +} + +const ProgressDialog: React.FC = ({ + title, + message, + progress, + visible, + showInstructions = false, + instructions, + onClose +}) => { + const [copySuccess, setCopySuccess] = useState(false); + const [showContent, setShowContent] = useState(false); + + // Reset copy state when dialog visibility changes + useEffect(() => { + if (!visible) { + setCopySuccess(false); + setShowContent(false); + } else { + // Add a small delay to trigger the entrance animation + const timer = setTimeout(() => { + setShowContent(true); + }, 50); + return () => clearTimeout(timer); + } + }, [visible]); + + if (!visible) return null; + + const handleCopyCommand = () => { + if (instructions?.command) { + navigator.clipboard.writeText(instructions.command); + setCopySuccess(true); + + // Reset the success message after 2 seconds + setTimeout(() => { + setCopySuccess(false); + }, 2000); + } + }; + + const handleClose = () => { + setShowContent(false); + // Delay closing to allow exit animation + setTimeout(() => { + if (onClose) { + onClose(); + } + }, 300); + }; + + // Modified to prevent closing when in progress + const handleOverlayClick = (e: React.MouseEvent) => { + // Always prevent propagation + e.stopPropagation(); + + // Only allow clicking outside to close if we're done processing (100%) + // and showing instructions or if explicitly allowed via a prop + if (e.target === e.currentTarget && progress >= 100 && showInstructions) { + handleClose(); + } + // Otherwise, do nothing - require using the close button + }; + + // Determine if we should show the copy button (for CreamLinux but not SmokeAPI) + const showCopyButton = instructions?.type === 'cream_install' || + instructions?.type === 'cream_uninstall'; + + // Format instruction message based on type + const getInstructionText = () => { + if (!instructions) return null; + + switch (instructions.type) { + case 'cream_install': + return ( + <> +

+ In Steam, set the following launch options for {instructions.game_title}: +

+ {instructions.dlc_count !== undefined && ( +
+ {instructions.dlc_count} DLCs have been enabled! +
+ )} + + ); + case 'cream_uninstall': + return ( +

+ For {instructions.game_title}, open Steam properties and remove the following launch option: +

+ ); + case 'smoke_install': + return ( + <> +

+ SmokeAPI has been installed for {instructions.game_title} +

+ {instructions.dlc_count !== undefined && ( +
+ {instructions.dlc_count} Steam API files have been patched. +
+ )} + + ); + case 'smoke_uninstall': + return ( +

+ SmokeAPI has been uninstalled from {instructions.game_title} +

+ ); + default: + return ( +

+ Done processing {instructions.game_title} +

+ ); + } + }; + + // Determine the CSS class for the command box based on instruction type + const getCommandBoxClass = () => { + return instructions?.type.includes('smoke') ? 'command-box command-box-smoke' : 'command-box'; + }; + + // Determine if close button should be enabled + const isCloseButtonEnabled = showInstructions || progress >= 100; + + return ( +
+
+

{title}

+

{message}

+ +
+
+
+
{Math.round(progress)}%
+ + {showInstructions && instructions && ( +
+

+ {instructions.type.includes('uninstall') + ? 'Uninstallation Instructions' + : 'Installation Instructions'} +

+ {getInstructionText()} + +
+
{instructions.command}
+
+ +
+ {showCopyButton && ( + + )} + + +
+
+ )} + + {/* Show close button even if no instructions */} + {!showInstructions && progress >= 100 && ( +
+ +
+ )} +
+
+ ); +}; + +export default ProgressDialog; \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx new file mode 100644 index 0000000..50e353a --- /dev/null +++ b/src/components/Sidebar.tsx @@ -0,0 +1,37 @@ +// src/components/Sidebar.tsx +import React from 'react'; + +interface SidebarProps { + setFilter: (filter: string) => void; + currentFilter: string; +} + +const Sidebar: React.FC = ({ setFilter, currentFilter }) => { + return ( +
+

Library

+
    +
  • setFilter('all')} + > + All Games +
  • +
  • setFilter('native')} + > + Native +
  • +
  • setFilter('proton')} + > + Proton Required +
  • +
+
+ ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..4aff025 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/services/ImageService.ts b/src/services/ImageService.ts new file mode 100644 index 0000000..d9ee883 --- /dev/null +++ b/src/services/ImageService.ts @@ -0,0 +1,96 @@ +// src/services/ImageService.ts + +/** + * Game image sources from Steam's CDN + */ +export const SteamImageType = { + HEADER: 'header', // 460x215 + CAPSULE: 'capsule_616x353', // 616x353 + LOGO: 'logo', // Game logo with transparency + LIBRARY_HERO: 'library_hero', // 1920x620 + LIBRARY_CAPSULE: 'library_600x900', // 600x900 +} as const; + +export type SteamImageTypeKey = keyof typeof SteamImageType; + +// Cache for images to prevent flickering +const imageCache: Map = new Map(); + +/** +* Builds a Steam CDN URL for game images +* @param appId Steam application ID +* @param type Image type from SteamImageType enum +* @returns URL string for the image +*/ +export const getSteamImageUrl = (appId: string, type: typeof SteamImageType[SteamImageTypeKey]) => { +return `https://cdn.cloudflare.steamstatic.com/steam/apps/${appId}/${type}.jpg`; +}; + +/** +* Checks if an image exists by performing a HEAD request +* @param url Image URL to check +* @returns Promise resolving to a boolean indicating if the image exists +*/ +export const checkImageExists = async (url: string): Promise => { +try { + const response = await fetch(url, { method: 'HEAD' }); + return response.ok; +} catch (error) { + console.error('Error checking image existence:', error); + return false; +} +}; + +/** +* Preloads an image for faster rendering +* @param url URL of image to preload +* @returns Promise that resolves when image is loaded +*/ +const preloadImage = (url: string): Promise => { +return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(url); + img.onerror = reject; + img.src = url; +}); +}; + +/** +* Attempts to find a valid image for a Steam game, trying different image types +* @param appId Steam application ID +* @returns Promise resolving to a valid image URL or null if none found +*/ +export const findBestGameImage = async (appId: string): Promise => { +// Check cache first +if (imageCache.has(appId)) { + return imageCache.get(appId) || null; +} + +// Try these image types in order of preference +const typesToTry = [ + SteamImageType.HEADER, + SteamImageType.CAPSULE, + SteamImageType.LIBRARY_CAPSULE +]; + +for (const type of typesToTry) { + const url = getSteamImageUrl(appId, type); + const exists = await checkImageExists(url); + if (exists) { + try { + // Preload the image to prevent flickering + const preloadedUrl = await preloadImage(url); + // Store in cache + imageCache.set(appId, preloadedUrl); + return preloadedUrl; + } catch { + // If preloading fails, just return the URL + imageCache.set(appId, url); + return url; + } + } +} + +// If we've reached here, no valid image was found +return null; +}; \ No newline at end of file diff --git a/src/styles/_fonts.scss b/src/styles/_fonts.scss new file mode 100644 index 0000000..14099cd --- /dev/null +++ b/src/styles/_fonts.scss @@ -0,0 +1,10 @@ +@font-face { + font-family: 'Satoshi'; + src: url('../assets/fonts/Satoshi.ttf') format('ttf'), + url('../assets/fonts/Roboto.ttf') format('ttf'), + url('../assets/fonts/WorkSans.ttf') format('ttf'); + font-weight: 400; // adjust as needed + font-style: normal; + font-display: swap; + } + \ No newline at end of file diff --git a/src/styles/_layout.scss b/src/styles/_layout.scss new file mode 100644 index 0000000..fa99020 --- /dev/null +++ b/src/styles/_layout.scss @@ -0,0 +1,262 @@ +// src/styles/_layout.scss + +@use './variables' as *; +@use './mixins' as *; + +.app-container { + display: flex; + flex-direction: column; + height: 100vh; + width: 100vw; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--primary-bg); + position: relative; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + radial-gradient(circle at 20% 30%, rgba(var(--primary-color), 0.05) 0%, transparent 70%), + radial-gradient(circle at 80% 70%, rgba(var(--cream-color), 0.05) 0%, transparent 70%); + pointer-events: none; + z-index: var(--z-bg); + } +} + +// Header +.app-header { + @include flex-between; + padding: 1rem 2rem; + background-color: var(--tertiary-bg); + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); + position: relative; + z-index: var(--z-header); + height: var(--header-height); + + h1 { + font-size: 1.5rem; + font-weight: 600; + color: var(--text-primary); + letter-spacing: 0.5px; + @include text-shadow; + } + + &::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, var(--cream-color), var(--primary-color), var(--smoke-color)); + opacity: 0.7; + } + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); + } +} + +.header-controls { + display: flex; + gap: 1rem; + align-items: center; +} + +// Main content +.main-content { + display: flex; + flex: 1; + overflow: hidden; + width: 100%; + position: relative; + z-index: var(--z-elevate); +} + +/* Sidebar */ +.sidebar { + width: var(--sidebar-width); + min-width: var(--sidebar-width); + background-color: var(--secondary-bg); + border-right: 1px solid rgba(255, 255, 255, 0.05); + box-shadow: inset -5px 0 15px rgba(0, 0, 0, 0.2); + padding: 1.5rem 1rem; + @include flex-column; + height: 100%; + overflow-y: auto; + z-index: var(--z-elevate) + 1; + + h2 { + color: var(--text-primary); + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 1rem; + letter-spacing: 0.5px; + opacity: 0.9; + } + + @include custom-scrollbar; +} + +// Game list container +.game-list { + padding: 1.5rem; + flex: 1; + overflow-y: auto; + height: 100%; + width: 100%; + @include custom-scrollbar; + position: relative; + + h2 { + font-size: 1.4rem; + font-weight: 700; + margin-bottom: 1.5rem; + color: var(--text-primary); + letter-spacing: 0.5px; + position: relative; + display: inline-block; + padding-bottom: 0.5rem; + + &:after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background: linear-gradient(90deg, var(--primary-color), transparent); + border-radius: 3px; + } + } +} + +// Game grid +.game-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + width: 100%; + padding: 0.5rem 0.5rem 2rem 0.5rem; + scroll-behavior: smooth; + align-items: stretch; + opacity: 0; + transform: translateY(10px); + animation: fadeIn 0.5s forwards; +} + +// Loading and empty state +.loading-indicator, .no-games-message { + @include flex-center; + height: 250px; + width: 100%; + font-size: 1.2rem; + color: var(--text-secondary); + text-align: center; + border-radius: var(--radius-lg); + background-color: rgba(255, 255, 255, 0.03); + box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(5px); +} + +.loading-indicator { + position: relative; + overflow: hidden; + + &:after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.05), + transparent + ); + animation: loading-shimmer 2s infinite; + } +} + +// Responsive adjustments +@include media-sm { + .game-grid { + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + } +} + +@include media-lg { + .game-grid { + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + } +} + +@include media-xl { + .game-grid { + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + } +} + +// Scroll to top button +.scroll-top-button { + position: fixed; + bottom: 30px; + right: 30px; + width: 44px; + height: 44px; + border-radius: 50%; + @include gradient-bg($primary-color, color-mix(in srgb, black 10%, var(--primary-color))); + color: var(--text-primary); + @include flex-center; + cursor: pointer; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + opacity: 0; + transform: translateY(20px); + @include transition-standard; + z-index: var(--z-header); + + &.visible { + opacity: 1; + transform: translateY(0); + } + + &:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(var(--primary-color), 0.4); + } + + &:active { + transform: translateY(0); + } +} + +// Animation keyframes +@keyframes fadeIn { + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes loading-shimmer { + to { + left: 100%; + } +} \ No newline at end of file diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss new file mode 100644 index 0000000..3f03291 --- /dev/null +++ b/src/styles/_mixins.scss @@ -0,0 +1,107 @@ +// src/styles/_mixins.scss + +@use './variables' as *; + +// src/styles/_mixins.scss + +// Basic flex helpers +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin flex-between { + display: flex; + align-items: center; + justify-content: space-between; +} + +@mixin flex-column { + display: flex; + flex-direction: column; +} + +// Glass effect for overlay +@mixin glass-overlay($opacity: 0.7) { + background-color: rgba(var(--primary-bg), var(--opacity)); + backdrop-filter: blur(8px); + border: 1px solid rgba(255, 255, 255, 0.05); +} + +@mixin gradient-bg($start-color, $end-color, $direction: 135deg) { + background: linear-gradient($direction, $start-color, $end-color); +} + +// Basic transition +@mixin transition-standard { + transition: all var(--duration-normal) var(--easing-ease-out); +} + +@mixin shadow-standard { + box-shadow: var(--shadow-standard); +} + +@mixin shadow-hover { + box-shadow: var(--shadow-hover);; +} + +@mixin text-shadow { + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7); +} + +// Simple animation for hover +@mixin hover-lift { + &:hover { + transform: translateY(-5px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5); + } +} + +// Responsive mixins +@mixin media-sm { + @media (min-width: 576px) { @content; } +} + +@mixin media-md { + @media (min-width: 768px) { @content; } +} + +@mixin media-lg { + @media (min-width: 992px) { @content; } +} + +@mixin media-xl { + @media (min-width: 1200px) { @content; } +} + +// Card base styling +@mixin card { + background-color: var(--secondary-bg); + border-radius: var(--radius-sm); + @include shadow; + overflow: hidden; + position: relative; +} + +// Custom scrollbar +@mixin custom-scrollbar { + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-track { + background: rgba(var(--primary-bg), 0.5); + border-radius: 10px; + } + + &::-webkit-scrollbar-thumb { + background: var(--primary-color); + border-radius: 10px; + border: 2px solid var(--primary-bg); + } + + &::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, white 10%, var(--primary-color)); + } +} \ No newline at end of file diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss new file mode 100644 index 0000000..693b443 --- /dev/null +++ b/src/styles/_reset.scss @@ -0,0 +1,65 @@ +// src/styles/_reset.scss + +@use './variables' as *; +@use './mixins' as *; +@use './fonts' as *; +// src/styles/_reset.scss + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + height: 100%; + width: 100%; + overflow: hidden; +} + +body { + font-family: 'Roboto'; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: var(--primary-bg); + color: var(--text-primary); + /* Prevent text selection by default */ + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; +} + +#root { + height: 100%; + width: 100%; +} + +button { + background: none; + border: none; + cursor: pointer; + font-family: inherit; + + &:focus { + outline: none; + } +} + +a { + color: inherit; + text-decoration: none; +} + +ul, ol { + list-style: none; +} + +input, button, textarea, select { + font: inherit; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: inherit; + font-size: inherit; +} \ No newline at end of file diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss new file mode 100644 index 0000000..676c252 --- /dev/null +++ b/src/styles/_variables.scss @@ -0,0 +1,116 @@ +// src/styles/_variables.scss + +@use './fonts' as *; + +// Color palette +:root { + // Primary colors + --primary-color: #ffc896; + --secondary-color: #ffb278; + + // Background + --primary-bg: #0f0f0f; + --secondary-bg: #151515; + --tertiary-bg: #121212; + --elevated-bg: #1a1a1a; + --disabled: #5E5E5E; + + // Text + --text-primary: #f0f0f0; + --text-secondary: #c8c8c8; + --text-soft: #afafaf; + --text-heavy: #1a1a1a; + --text-muted: #4b4b4b; + + // Borders + --border-dark: #1a1a1a; + --border-soft: #282828; + --border: #323232; + + // Status colors - more vibrant + --success: #8cc893; + --warning: #ffc896; + --danger: #d96b6b; + --info: #80b4ff; + + --success-light: #b0e0a9; + --warning-light: #ffdcb9; + --danger-light: #e69691; + --info-light: #a8d2ff; + + --success-soft: rgba(176, 224, 169, 0.15); + --warning-soft: rgba(247, 200, 111, 0.15); + --danger-soft: rgba(230, 150, 145, 0.15); + --info-soft: rgba(168, 210, 255, 0.15); + + // Feature colors + --native: #8cc893; + --proton: #ffc896; + --cream: #80b4ff; + --smoke: #fff096; + + --modal-backdrop: rgba(30, 30, 30, 0.95); + + // Animation durations + --duration-fast: 100ms; + --duration-normal: 200ms; + --duration-slow: 300ms; + + // Animation easings + --easing-ease-out: cubic-bezier(0, 0, 0.2, 1); + --easing-ease-in: cubic-bezier(0.4, 0, 1, 1); + --easing-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --easing-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); + + // Layout values + --header-height: 64px; + --sidebar-width: 250px; + --card-height: 200px; + + // Border radius + --radius-sm: 6px; + --radius-md: 8px; + --radius-lg: 12px; + + // Font weights + --thin: 100; + --extralight: 200; + --light: 300; + --normal: 400; + --medium: 500; + --semibold: 600; + --bold: 700; + --extrabold: 800; + + --family: 'Satoshi'; + + // Shadows + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -2px rgba(0, 0, 0, 0.3); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -4px rgba(0, 0, 0, 0.3); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 8px 10px -6px rgba(0, 0, 0, 0.3); + --shadow-inner: inset 0 2px 4px rgba(0, 0, 0, 0.3); + --shadow-standard: 0 10px 25px rgba(0, 0, 0, 0.5); + --shadow-hover: 0 15px 30px rgba(0, 0, 0, 0.7); + + // Z-index levels + //--z-index-bg: 0; + //--z-index-content: 1; + //--z-index-header: 100; + //--z-index-modal: 1000; + //--z-index-tooltip: 1500; + + // Z-index levels + --z-bg: 0; + --z-elevate: 1; + --z-header: 100; + --z-modal: 1000; + --z-tooltip: 1500; +} + +$success-color: #55e07a; +$danger-color: #ff5252; +$primary-color: #4a76c4; +$cream-color: #9b7dff; +$smoke-color: #fbb13c; +$warning-color: #fbb13c; \ No newline at end of file diff --git a/src/styles/components/_animated_checkbox.scss b/src/styles/components/_animated_checkbox.scss new file mode 100644 index 0000000..c376a37 --- /dev/null +++ b/src/styles/components/_animated_checkbox.scss @@ -0,0 +1,98 @@ +// src/styles/components/_animated_checkbox.scss + +@use '../variables' as *; +@use '../mixins' as *; + +.animated-checkbox { + display: flex; + align-items: center; + cursor: pointer; + width: 100%; + position: relative; + + &:hover .checkbox-custom { + border-color: rgba(255, 255, 255, 0.3); + } +} + +.checkbox-original { + position: absolute; + opacity: 0; + height: 0; + width: 0; +} + +.checkbox-custom { + width: 22px; + height: 22px; + background-color: rgba(255, 255, 255, 0.05); + border: 2px solid var(--border-soft, #323232); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s var(--easing-bounce); + margin-right: 15px; + flex-shrink: 0; + position: relative; + + &.checked { + background-color: var(--primary-color, #ffc896); + border-color: var(--primary-color, #ffc896); + box-shadow: 0 0 10px rgba(255, 200, 150, 0.2); + } +} + +.checkmark-icon { + width: 18px; + height: 18px; +} + +.checkmark { + stroke-dasharray: 30; + stroke-dashoffset: 30; + opacity: 0; + transition: stroke-dashoffset 0.3s ease; + + &.checked { + stroke-dashoffset: 0; + opacity: 1; + animation: checkmarkAnimation 0.3s cubic-bezier(0.65, 0, 0.45, 1) forwards; + } +} + +.checkbox-content { + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; // Ensures text-overflow works properly +} + +.checkbox-label { + font-size: 15px; + font-weight: 500; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.checkbox-sublabel { + font-size: 12px; + color: var(--text-muted); +} + +// Animation for the checkmark +@keyframes checkmarkAnimation { + 0% { + stroke-dashoffset: 30; + opacity: 0; + } + 40% { + opacity: 1; + } + 100% { + stroke-dashoffset: 0; + opacity: 1; + } +} \ No newline at end of file diff --git a/src/styles/components/_background.scss b/src/styles/components/_background.scss new file mode 100644 index 0000000..8d6a3a3 --- /dev/null +++ b/src/styles/components/_background.scss @@ -0,0 +1,16 @@ +// src/styles/_components/_background.scss + +@use '../variables' as *; +@use '../mixins' as *; +@use 'sass:color'; + +.animated-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: var(--z-bg); + opacity: 0.4; +} \ No newline at end of file diff --git a/src/styles/components/_dialog.scss b/src/styles/components/_dialog.scss new file mode 100644 index 0000000..08ca926 --- /dev/null +++ b/src/styles/components/_dialog.scss @@ -0,0 +1,249 @@ +// src/styles/_components/_dialog.scss + +@use '../variables' as *; +@use '../mixins' as *; + +/* Progress Dialog */ +.progress-dialog-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: var(--modal-backdrop); + backdrop-filter: blur(5px); + @include flex-center; + z-index: var(--z-modal); + opacity: 0; + animation: modal-appear 0.2s ease-out; + cursor: pointer; + + &.visible { + opacity: 1; + } + + @keyframes modal-appear { + 0% { opacity: 0; transform: scale(0.95); } + 100% { opacity: 1; transform: scale(1); } + } +} + +.progress-dialog { + background-color: var(--elevated-bg); + border-radius: 8px; + padding: 1.5rem; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3); /* shadow-glow */ + width: 450px; + max-width: 90vw; + border: 1px solid var(--border-soft); + opacity: 0; + cursor: default; + + &.dialog-visible { + transform: scale(1); + opacity: 1; + } + + &.with-instructions { + width: 500px; + } + + h3 { + font-weight: 700; + margin-bottom: 1rem; + color: var(--text-primary); + } + + p { + margin-bottom: 1rem; + color: var(--text-secondary); + } +} + +// Progress bar +.progress-bar-container { + height: 8px; + background-color: var(--border-soft); + border-radius: 4px; + overflow: hidden; + margin-bottom: 0.5rem; +} + +.progress-bar { + height: 100%; + background-color: var(--primary-color); + border-radius: 4px; + transition: width 0.3s ease; + background: var(--primary-color); + box-shadow: 0px 0px 6px rgba(245, 150, 130, 0.3); +} + +.progress-percentage { + text-align: right; + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 1rem; +} + +/* Instruction container in progress dialog */ +.instruction-container { + margin-top: 1.5rem; + padding-top: 1rem; + border-top: 1px solid var(--border-soft); + + h4 { + font-weight: 700; + margin-bottom: 1rem; + color: var(--text-primary); + } +} + +.instruction-text { + line-height: 1.6; + margin-bottom: 1rem; + color: var(--text-secondary); +} + +.dlc-count { + display: inline-block; + margin-bottom: 0.75rem; + padding: 0.4rem 0.8rem; + background-color: var(--info-soft); + color: var(--info); + border-radius: 4px; + font-size: 0.8rem; + + &::before { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background-color: var(--info); + margin-right: 8px; + } +} + +.command-box { + background-color: var(--border-dark); + border: 1px solid var(--border-soft); + border-radius: 4px; + padding: 1rem; + margin-bottom: 1.2rem; + font-family: monospace; + position: relative; + overflow: hidden; + + &.command-box-smoke { + font-size: 0.9rem; + overflow-wrap: break-word; + word-break: break-word; + white-space: pre-wrap; + width: 100%; + max-width: 100%; + } +} + +.selectable-text { + font-size: 0.9rem; + line-height: 1.5; + user-select: text; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + cursor: text; + margin: 0; + color: var(--text-primary); + word-break: break-word; + white-space: pre-wrap; +} + +.action-buttons { + display: flex; + gap: 0.5rem; + justify-content: flex-end; +} + +.copy-button, .close-button { + padding: 0.6rem 1.2rem; + border-radius: var(--radius-sm); + font-weight: 600; + letter-spacing: 0.5px; + @include transition-standard; + border: none; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); +} + +.copy-button { + background-color: var(--primary-color); + color: white; + + &:hover { + background-color: var(--primary-color); + transform: translateY(-2px) scale(1.02); /* hover-lift */ + box-shadow: 0 6px 14px var(--info-soft); + } +} + +.close-button { + background-color: var(--border-soft); + color: var(--text-primary); + + &:hover { + background-color: var(--border); + transform: translateY(-2px) scale(1.02); /* hover-lift */ + box-shadow: 0 6px 14px rgba(0, 0, 0, 0.3); + } +} + +// Error message styling +.error-message { + @include flex-column; + align-items: center; + justify-content: center; + padding: 2rem; + margin: 2rem auto; + max-width: 600px; + border-radius: var(--radius-lg); + background-color: rgba(var(--danger), 0.05); + border: 1px solid rgb(var(--danger), 0.2); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); + backdrop-filter: blur(5px); + text-align: center; + + h3 { + color: var(--danger); + font-weight: 700; + margin-bottom: 1rem; + } + + p { + margin-bottom: 1.5rem; + color: var(--text-secondary); + white-space: pre-wrap; + word-break: break-word; + } + + button { + background-color: var(--primary-color); + color: var(--text-primary); + border: none; + padding: 0.7rem 1.5rem; + border-radius: var(--radius-sm); + font-weight: 600; + letter-spacing: 0.5px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + @include transition-standard; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 6px 14px rgba(var(--primary-color), 0.4); + } + } +} + +// Animation for progress bar +@keyframes progress-shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } +} \ No newline at end of file diff --git a/src/styles/components/_dlc_dialog.scss b/src/styles/components/_dlc_dialog.scss new file mode 100644 index 0000000..49bd559 --- /dev/null +++ b/src/styles/components/_dlc_dialog.scss @@ -0,0 +1,314 @@ +// src/styles/components/_dlc_dialog.scss + +@use '../variables' as *; +@use '../mixins' as *; + +.dlc-dialog-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: var(--modal-backdrop); + backdrop-filter: blur(5px); + @include flex-center; + z-index: var(--z-modal); + opacity: 0; + cursor: pointer; + + &.visible { + opacity: 1; + animation: modal-appear 0.2s ease-out; + } +} + +.dlc-selection-dialog { + background-color: var(--elevated-bg); + border-radius: 8px; + width: 650px; + max-width: 90vw; + max-height: 80vh; + border: 1px solid var(--border-soft); + box-shadow: 0px 10px 25px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; + cursor: default; + opacity: 0; + transform: scale(0.95); + + &.dialog-visible { + transform: scale(1); + opacity: 1; + transition: transform 0.2s var(--easing-bounce), opacity 0.2s ease-out; + } +} + +.dlc-dialog-header { + padding: 1.5rem; + border-bottom: 1px solid var(--border-soft); + + h3 { + font-size: 1.2rem; + font-weight: 700; + margin-bottom: 0.5rem; + color: var(--text-primary); + } +} + +.dlc-game-info { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.5rem; + + .game-title { + font-weight: 500; + color: var(--text-secondary); + } + + .dlc-count { + font-size: 0.9rem; + padding: 0.3rem 0.6rem; + background-color: var(--info-soft); + color: var(--info); + border-radius: 4px; + } +} + +.dlc-dialog-search { + padding: 0.75rem 1.5rem; + background-color: rgba(0, 0, 0, 0.1); + border-bottom: 1px solid var(--border-soft); + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; +} + +.dlc-search-input { + flex: 1; + background-color: var(--border-dark); + border: 1px solid var(--border-soft); + border-radius: 4px; + color: var(--text-primary); + padding: 0.6rem 1rem; + font-size: 0.9rem; + @include transition-standard; + + &:focus { + border-color: var(--primary-color); + outline: none; + box-shadow: 0px 0px 6px rgba(245, 150, 130, 0.2); + } + + &::placeholder { + color: var(--text-muted); + } +} + +.select-all-container { + display: flex; + align-items: center; + min-width: 100px; + + // Custom styling for the select all checkbox + :global(.animated-checkbox) { + margin-left: auto; + } + + :global(.checkbox-label) { + font-size: 0.9rem; + color: var(--text-secondary); + } +} + +.dlc-loading-progress { + padding: 0.75rem 1.5rem; + background-color: rgba(0, 0, 0, 0.05); + border-bottom: 1px solid var(--border-soft); + + .progress-bar-container { + height: 6px; + background-color: var(--border-soft); + border-radius: 3px; + overflow: hidden; + margin-bottom: 0.5rem; + } + + .progress-bar { + height: 100%; + background-color: var(--primary-color); + border-radius: 3px; + transition: width 0.3s ease; + background: var(--primary-color); + box-shadow: 0px 0px 6px rgba(128, 181, 255, 0.3); + } + + .loading-details { + display: flex; + justify-content: space-between; + font-size: 0.8rem; + color: var(--text-secondary); + + .time-left { + color: var(--text-muted); + } + } +} + +.dlc-list-container { + flex: 1; + overflow-y: auto; + min-height: 200px; + @include custom-scrollbar; +} + +.dlc-list { + padding: 0.5rem 0; +} + +.dlc-item { + padding: 0.75rem 1.5rem; + border-bottom: 1px solid var(--border-soft); + @include transition-standard; + + &:hover { + background-color: rgba(255, 255, 255, 0.03); + } + + &:last-child { + border-bottom: none; + } + + &.dlc-item-loading { + height: 30px; + display: flex; + align-items: center; + justify-content: center; + + .loading-pulse { + width: 70%; + height: 20px; + background: linear-gradient(90deg, + var(--border-soft) 0%, + var(--border) 50%, + var(--border-soft) 100%); + background-size: 200% 100%; + border-radius: 4px; + animation: loading-pulse 1.5s infinite; + } + } + + // Enhanced styling for the checkbox component inside dlc-item + :global(.animated-checkbox) { + width: 100%; + + .checkbox-label { + color: var(--text-primary); + font-weight: 500; + transition: color 0.15s ease; + } + + .checkbox-sublabel { + color: var(--text-muted); + } + + // Optional hover effect + &:hover { + .checkbox-label { + color: var(--primary-color); + } + + .checkbox-custom { + border-color: var(--primary-color, #ffc896); + transform: scale(1.05); + } + } + } +} + +.dlc-loading { + height: 200px; + @include flex-center; + flex-direction: column; + gap: 1rem; + + .loading-spinner { + width: 40px; + height: 40px; + border: 3px solid rgba(255, 255, 255, 0.1); + border-top-color: var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; + } + + p { + color: var(--text-secondary); + } +} + +.no-dlcs-message { + height: 200px; + @include flex-center; + color: var(--text-secondary); +} + +.dlc-dialog-actions { + padding: 1rem 1.5rem; + border-top: 1px solid var(--border-soft); + display: flex; + justify-content: flex-end; + gap: 1rem; +} + +.cancel-button, .confirm-button { + padding: 0.6rem 1.2rem; + border-radius: var(--radius-sm); + font-weight: 600; + letter-spacing: 0.5px; + @include transition-standard; + border: none; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); +} + +.cancel-button { + background-color: var(--border-soft); + color: var(--text-primary); + + &:hover { + background-color: var(--border); + transform: translateY(-2px); + box-shadow: 0 6px 14px rgba(0, 0, 0, 0.3); + } +} + +.confirm-button { + background-color: var(--primary-color); + color: white; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 6px 14px var(--info-soft); + } + + &:disabled { + opacity: 0.7; + cursor: not-allowed; + transform: none; + } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes modal-appear { + 0% { opacity: 0; transform: scale(0.95); } + 100% { opacity: 1; transform: scale(1); } +} + +@keyframes loading-pulse { + 0% { background-position: 200% 50%; } + 100% { background-position: 0% 50%; } +} \ No newline at end of file diff --git a/src/styles/components/_gamecard.scss b/src/styles/components/_gamecard.scss new file mode 100644 index 0000000..4c78e90 --- /dev/null +++ b/src/styles/components/_gamecard.scss @@ -0,0 +1,287 @@ +// src/styles/components/_gamecard.scss + +@use '../variables' as *; +@use '../mixins' as *; + +.game-item-card { + position: relative; + height: var(--card-height); + border-radius: var(--radius-lg); + overflow: hidden; + will-change: opacity, transform; + @include shadow-standard; + @include transition-standard; + transform-origin: center; + + // Simple image loading animation + opacity: 0; + animation: fadeIn 0.5s forwards; +} + +// Hover effects for the card +.game-item-card:hover { + transform: translateY(-8px) scale(1.02); + @include shadow-hover; + z-index: 5; + + .status-badge.native { + box-shadow: 0 0 10px rgba(85, 224, 122, 0.5) + } + + .status-badge.proton { + box-shadow: 0 0 10px rgba(255, 201, 150, 0.5); + } + + .status-badge.cream { + box-shadow: 0 0 10px rgba(128, 181, 255, 0.5); + } + + .status-badge.smoke { + box-shadow: 0 0 10px rgba(255, 239, 150, 0.5); + } +} + +// Special styling for cards with different statuses +.game-item-card:has(.status-badge.cream) { + box-shadow: var(--shadow-standard), 0 0 15px rgba(128, 181, 255, 0.15); +} + +.game-item-card:has(.status-badge.smoke) { + box-shadow: var(--shadow-standard), 0 0 15px rgba(255, 239, 150, 0.15); +} + +// Simple clean overlay +.game-item-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(to bottom, + rgba(0, 0, 0, 0.5) 0%, + rgba(0, 0, 0, 0.6) 50%, + rgba(0, 0, 0, 0.8) 100% + ); + @include flex-column; + justify-content: space-between; + padding: 1rem; + box-sizing: border-box; + font-weight: var(--bold); + font-family: var(--family); + -webkit-font-smoothing: subpixel-antialiased; + text-rendering: geometricPrecision; + color: var(--text-heavy);; + z-index: 1; +} + +.game-badges { + display: flex; + justify-content: flex-end; + gap: 0.4rem; + margin-bottom: 0.5rem; + position: relative; + z-index: 2; +} + +.status-badge { + display: inline-block; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.75rem; + font-weight: var(--bold); + font-family: var(--family); + -webkit-font-smoothing: subpixel-antialiased; + text-rendering: geometricPrecision; + color: var(--text-heavy);; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); + @include transition-standard; + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.status-badge.native { + background-color: var(--native); + color: var(--text-heavy); +} +.status-badge.proton { + background-color: var(--proton); + color: var(--text-heavy); +} + +.status-badge.cream { + background-color: var(--cream); + color: var(--text-heavy); +} + +.status-badge.smoke { + background-color: var(--smoke); + color: var(--text-heavy); +} + +.game-title { + padding: 0; + position: relative; +} + +.game-title h3 { + color: var(--text-primary); + font-size: 1.6rem; + font-weight: var(--bold); + margin: 0; + -webkit-font-smoothing: subpixel-antialiased; + text-rendering: geometricPrecision; + transform: translateZ(0); // or + will-change: opacity, transform; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.game-actions { + display: flex; + gap: 0.5rem; + position: relative; + z-index: 3; +} + +.action-button { + flex: 1; + padding: 0.5rem; + border: none; + border-radius: var(--radius-sm); + cursor: pointer; + font-weight: var(--bold); + -webkit-font-smoothing: subpixel-antialiased; + text-rendering: geometricPrecision; + color: var(--text-heavy); + min-width: 0; + white-space: nowrap; + @include transition-standard; +} + +.action-button.install { + background-color: var(--success); +} + +.action-button.install:hover { + background-color: var(--success-light); + transform: translateY(-2px) scale(1.02); + box-shadow: 0px 0px 12px rgba(140, 200, 147, 0.3); +} + +.action-button.uninstall { + background-color: var(--danger); +} + +.action-button.uninstall:hover { + background-color: var(--danger-light); + transform: translateY(-2px) scale(1.02); + box-shadow: 0px 0px 12px rgba(217, 107, 107, 0.3) +} + +.action-button:active { + transform: scale(0.97); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); +} + +.action-button:disabled { + opacity: 0.7; + cursor: not-allowed; + background-color: var(--disabled); + transform: none; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + position: relative; + overflow: hidden; +} + +.action-button:disabled::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 50%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent); + animation: button-loading 1.5s infinite; +} + +.edit-button { + padding: 0 0.7rem; + background-color: rgba(255, 255, 255, 0.2); + font-weight: var(--bold); + -webkit-font-smoothing: subpixel-antialiased; + text-rendering: geometricPrecision; + color: var(--text-primary); + border-radius: var(--radius-sm); + cursor: pointer; + letter-spacing: 1px; + @include transition-standard; +} + +.edit-button:hover { + background-color: rgba(255, 255, 255, 0.3); + transform: translateY(-2px); + box-shadow: 0 7px 15px rgba(0, 0, 0, 0.3); +} + +.edit-button:active { + transform: translateY(0); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.api-not-found-message { + display: flex; + align-items: center; + justify-content: space-between; + background-color: rgba(255, 100, 100, 0.2); + border: 1px solid rgba(255, 100, 100, 0.3); + border-radius: var(--radius-sm); + padding: 0.4rem 0.8rem; + width: 100%; + font-size: 0.85rem; + color: var(--text-primary); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + + span { + flex: 1; + } + + .rescan-button { + background-color: var(--warning); + color: var(--text-heavy); + border: none; + border-radius: var(--radius-sm); + padding: 0.2rem 0.6rem; + font-size: 0.75rem; + font-weight: var(--bold); + margin-left: 0.5rem; + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + background-color: var(--warning-light); + transform: translateY(-2px); + } + + &:active { + transform: translateY(0); + } + } +} + +// Apply staggered delay to cards +@for $i from 1 through 12 { + .game-grid .game-item-card:nth-child(#{$i}) { + animation-delay: #{$i * 0.05}s; + } +} + +// Simple animations +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes button-loading { + to { left: 100%; } +} \ No newline at end of file diff --git a/src/styles/components/_header.scss b/src/styles/components/_header.scss new file mode 100644 index 0000000..1015d60 --- /dev/null +++ b/src/styles/components/_header.scss @@ -0,0 +1,82 @@ +// src/styles/_components/_header.scss + +@use '../variables' as *; +@use '../mixins' as *; + +.app-container { + display: flex; + flex-direction: column; + height: 100vh; + width: 100vw; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--primary-bg); + position: relative; + } + +// Header +.app-header { + @include flex-between; + padding: 1rem 2rem; + background-color: var(--tertiary-bg); + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4); + position: relative; + z-index: var(--z-header); + height: var(--header-height); + + h1 { + font-size: 1.5rem; + font-weight: 600; + color: var(--text-primary); + letter-spacing: 0.5px; + @include text-shadow; + } + } + + .header-controls { + display: flex; + gap: 1rem; + align-items: center; + } + + .refresh-button { + background-color: var(--primary-color); + color: var(--text-primary); + border: none; + border-radius: 4px; + padding: 0.6rem 1.2rem; + font-weight: var(--bold); + letter-spacing: 0.5px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + transition: all 0.2s ease; + } + + .refresh-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 14px rgba(245, 150, 130, 0.3); + background-color: var(--primary-color); + } + + .refresh-button:active { + transform: translateY(0); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); + } + + .search-input { + padding: 0.5rem 1rem; + border: 1px solid var(--border-soft); + border-radius: 4px; + min-width: 200px; + background-color: var(--border-dark); + color: var(--text-primary); + } + + .search-input:focus { + border-color: var(--primary-color); + outline: none; + box-shadow: 0px 0px 6px rgba(245, 150, 130, 0.2); +} \ No newline at end of file diff --git a/src/styles/components/_loading_screen.scss b/src/styles/components/_loading_screen.scss new file mode 100644 index 0000000..6d7223f --- /dev/null +++ b/src/styles/components/_loading_screen.scss @@ -0,0 +1,100 @@ +.initial-loading-screen { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: var(--primary-bg); + display: flex; + align-items: center; + justify-content: center; + z-index: var(--z-modal) + 1; + + .loading-content { + text-align: center; + padding: 2rem; + max-width: 500px; + width: 90%; + + h1 { + font-size: 2.5rem; + margin-bottom: 2rem; + font-weight: var(--bold); + color: var(--primary-color); + text-shadow: 0 2px 10px rgba(var(--primary-color), 0.4); + } + + .loading-animation { + margin-bottom: 2rem; + } + + .loading-circles { + display: flex; + justify-content: center; + gap: 1rem; + margin-bottom: 1rem; + + .circle { + width: 20px; + height: 20px; + border-radius: 50%; + animation: bounce 1.4s infinite ease-in-out both; + + &.circle-1 { + background-color: var(--primary-color); + animation-delay: -0.32s; + } + + &.circle-2 { + background-color: var(--cream-color); + animation-delay: -0.16s; + } + + &.circle-3 { + background-color: var(--smoke-color); + } + } + } + + .loading-message { + font-size: 1.1rem; + color: var(--text-secondary); + margin-bottom: 1.5rem; + min-height: 3rem; + } + + .progress-bar-container { + height: 8px; + background-color: var(--border-soft); + border-radius: 4px; + overflow: hidden; + margin-bottom: 0.5rem; + } + + .progress-bar { + height: 100%; + background-color: var(--primary-color); + border-radius: 4px; + transition: width 0.5s ease; + background: linear-gradient(to right, var(--cream-color), var(--primary-color), var(--smoke-color)); + box-shadow: 0px 0px 10px rgba(255, 200, 150, 0.4); + } + + .progress-percentage { + text-align: right; + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 1rem; + } + } +} + +// Animation for the bouncing circles +@keyframes bounce { + 0%, 80%, 100% { + transform: scale(0); + } + 40% { + transform: scale(1.0); + } +} \ No newline at end of file diff --git a/src/styles/components/_sidebar.scss b/src/styles/components/_sidebar.scss new file mode 100644 index 0000000..474f653 --- /dev/null +++ b/src/styles/components/_sidebar.scss @@ -0,0 +1,206 @@ +// src/styles/_components/_sidebar.scss + +@use '../variables' as *; +@use '../mixins' as *; + +.filter-list { + list-style: none; + margin-bottom: 1.5rem; + + li { + @include transition-standard; + border-radius: var(--radius-sm); + padding: 0.7rem 1rem; + margin-bottom: 0.3rem; + font-weight: 500; + cursor: pointer; + + &:hover { + background-color: rgba(255, 255, 255, 0.07); + } + + &.active { + @include gradient-bg($primary-color, color-mix(in srgb, black 10%, var(--primary-color))); + box-shadow: 0 4px 10px rgba(var(--primary-color), 0.3); + } + } +} + +// Custom select dropdown styling +.custom-select { + position: relative; + display: inline-block; + + .select-selected { + background-color: rgba(255, 255, 255, 0.07); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--radius-sm); + color: var(--text-primary); + padding: 0.6rem 1rem; + font-size: 0.9rem; + cursor: pointer; + @include transition-standard; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + min-width: 150px; + + &:after { + content: '⯆'; + font-size: 0.7rem; + opacity: 0.7; + } + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } + } + + .select-items { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: var(--secondary-bg); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: var(--radius-sm); + margin-top: 5px; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + z-index: 10; + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease; + + &.show { + max-height: 300px; + } + + .select-item { + padding: 0.5rem 1rem; + cursor: pointer; + @include transition-standard; + + &:hover { + background-color: rgba(255, 255, 255, 0.07); + } + + &.selected { + background-color: var(--primary-color); + color: var(--text-primary); + } + } + } +} + +// App logo styles +.app-logo { + display: flex; + align-items: center; + gap: 10px; + + svg { + width: 28px; + height: 28px; + fill: var(--text-primary); + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)); + } +} + +// Tooltip styles +.tooltip { + position: relative; + display: inline-block; + + &:hover .tooltip-content { + visibility: visible; + opacity: 1; + transform: translateY(0); + } + + .tooltip-content { + visibility: hidden; + width: 200px; + background-color: var(--secondary-bg); + color: var(--text-primary); + text-align: center; + border-radius: var(--radius-sm); + padding: 8px; + position: absolute; + z-index: var(--z-tooltip); + bottom: 125%; + left: 50%; + margin-left: -100px; + opacity: 0; + transform: translateY(10px); + transition: opacity 0.3s, transform 0.3s; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.1); + font-size: 0.8rem; + pointer-events: none; + + &::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: var(--secondary-bg) transparent transparent transparent; + } + } +} + +// Header controls +.refresh-button { + background-color: var(--primary-color); + color: var(--text-heavy); + border: none; + border-radius: var(--radius-sm); + padding: 0.6rem 1.2rem; + font-weight: var(--bold); + letter-spacing: 0.5px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + @include transition-standard; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 6px 14px rgba(245, 150, 130, 0.3); + background-color: var(--primary-color); + } + + &:active { + transform: translateY(0); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); + } +} + +.search-input:focus { + border-color: var(--primary-color); + outline: none; + box-shadow: 0px 0px 6px rgba(245, 150, 130, 0.2); +} + +.search-input { + background-color: var(--border-dark); + border: 1px solid var(--border-soft); + border-radius: 4px; + color: var(--text-primary); + padding: 0.6rem 1rem; + font-size: 0.9rem; + @include transition-standard; + box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.2); + min-width: 200px; + + &:focus { + border-color: var(--primary-color); + background-color: rgba(255, 255, 255, 0.1); + outline: none; + box-shadow: 0 0 0 2px rgba(var(--primary-color), 0.3), inset 0 2px 5px rgba(0, 0, 0, 0.2); + } + + &::placeholder { + color: rgba(255, 255, 255, 0.4); + } +} \ No newline at end of file diff --git a/src/styles/main.scss b/src/styles/main.scss new file mode 100644 index 0000000..3f6accc --- /dev/null +++ b/src/styles/main.scss @@ -0,0 +1,21 @@ +// src/styles/main.scss + +// Import variables and mixins first +@use './variables' as *; +@use './mixins' as *; +@use './fonts' as *; + +// Reset +@use './reset'; + +// Layout +@use './layout'; + +// Components +@use './components/gamecard'; +@use './components/dialog'; +@use './components/background'; +@use './components/sidebar'; +@use './components/dlc_dialog'; +@use './components/loading_screen'; +@use './components/animated_checkbox'; \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..358ca9b --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..4105660 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +// Removed unused import: loadEnv + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + + // Vite options tailored for Tauri development + clearScreen: false, + server: { + port: 1420, + strictPort: true, + }, + envPrefix: ['VITE_', 'TAURI_'], + build: { + // Tauri supports es2021 + target: ['es2021', 'chrome105', 'safari13'], + // Don't minify for debug builds + minify: 'esbuild', + // Produce sourcemaps for debug builds + sourcemap: true, + }, +}); \ No newline at end of file