commit cde3011f64d9f28a4ed3f61bfcc0f979e0fc6ca0 Author: SuperKali Date: Fri Dec 12 07:47:07 2025 +0100 First upload diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..4140290 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,201 @@ +name: Build and Release + +on: + push: + branches: [main, master] + tags: + - 'v*' + pull_request: + branches: [main, master] + workflow_dispatch: + +# Cancel previous runs on the same branch +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + # Minimum Node.js version (matches package.json engines) + NODE_VERSION: '20' + +jobs: + build-linux: + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + arch: amd64 + os: ubuntu-22.04 + - target: aarch64-unknown-linux-gnu + arch: arm64 + os: ubuntu-22.04-arm + + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf \ + libssl-dev \ + libgtk-3-dev \ + squashfs-tools + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Install npm dependencies + run: npm ci + + - name: Build frontend + run: npm run build + + - name: Install Tauri CLI + run: cargo install tauri-cli --version "^2" --locked + + - name: Build Tauri app + run: cargo tauri build --bundles deb + + - name: Upload Linux artifacts + uses: actions/upload-artifact@v4 + with: + name: linux-${{ matrix.arch }} + path: | + src-tauri/target/release/bundle/deb/*.deb + if-no-files-found: error + + build-macos: + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-apple-darwin + arch: x64 + os: macos-13 + - target: aarch64-apple-darwin + arch: arm64 + os: macos-14 + + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Install npm dependencies + run: npm ci + + - name: Build frontend + run: npm run build + + - name: Install Tauri CLI + run: cargo install tauri-cli --version "^2" --locked + + - name: Build Tauri app + run: cargo tauri build --bundles dmg,app + + - name: Upload macOS artifacts + uses: actions/upload-artifact@v4 + with: + name: macos-${{ matrix.arch }} + path: | + src-tauri/target/release/bundle/dmg/*.dmg + src-tauri/target/release/bundle/macos/*.app + if-no-files-found: error + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + + - name: Install npm dependencies + run: npm ci + + - name: Build frontend + run: npm run build + + - name: Install Tauri CLI + run: cargo install tauri-cli --version "^2" --locked + + - name: Build Tauri app + run: cargo tauri build --bundles msi,nsis + + - name: Upload Windows artifacts + uses: actions/upload-artifact@v4 + with: + name: windows-x64 + path: | + src-tauri/target/release/bundle/msi/*.msi + src-tauri/target/release/bundle/nsis/*.exe + if-no-files-found: error + + release: + needs: [build-linux, build-macos, build-windows] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Display structure of downloaded files + run: ls -la artifacts/**/* + + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + draft: true + generate_release_notes: true + files: | + artifacts/**/*.deb + artifacts/**/*.dmg + artifacts/**/*.msi + artifacts/**/*.exe diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8d52c2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Dependencies +node_modules + +# Build outputs +dist +dist-ssr +/releases/ +/dist-releases/ + +# Local env files +*.local +.env +.env.* + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +!.vscode/settings.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# OS files +.DS_Store +Thumbs.db + +# Tauri +src-tauri/target/ +src-tauri/Cargo.lock + +# Test coverage +coverage/ + +# Temporary files +*.tmp +*.temp +.cache diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb05def --- /dev/null +++ b/README.md @@ -0,0 +1,257 @@ +

+ + Armbian logo + +

+ +

Armbian Imager

+ +

+ The official tool for flashing Armbian OS to your single-board computer +

+ +

+ Release + License + Armbian +

+ +

+ Why? • + Features • + Download • + How it works • + Development +

+ +--- + +## Why Armbian Imager? + +Getting started with single-board computers shouldn't be complicated. Yet, for years, the process of flashing an OS image involved: + +- **Hunting for the right image** across multiple download pages +- **Manually verifying checksums** to ensure file integrity +- **Using generic tools** that don't understand SBC specifics +- **Risk of bricking your main drive** with poorly designed software + +**Armbian Imager changes everything.** + +We built this tool because the Armbian community deserves a first-class experience. With 185+ supported boards from 70+ manufacturers, finding and flashing the right image should be effortless—and now it is. + +### The Vision + +Inspired by the simplicity of [Raspberry Pi Imager](https://github.com/raspberrypi/rpi-imager), we wanted to bring that same polished experience to the broader SBC ecosystem. But we didn't just copy—we innovated: + +- **Native performance** with Rust and Tauri (not Electron's 200MB+ overhead) +- **Touch ID support** on macOS for seamless authentication +- **Real board photos** scraped directly from armbian.com +- **Smart filtering** by kernel type, desktop environment, and release channel + +## Features + +| Feature | Description | +|---------|-------------| +| **185+ Boards** | Browse every Armbian-supported SBC, organized by manufacturer | +| **Smart Filtering** | Filter by stable/nightly, desktop/server, kernel variant | +| **Safe by Design** | System disks are automatically excluded—no accidents | +| **Verified Writes** | Read-back verification ensures your flash is perfect | +| **Custom Images** | Use your own `.img` or `.img.xz` files | +| **Touch ID** | Authenticate with biometrics on macOS | +| **Light/Dark Mode** | Follows your system preference | +| **Tiny Footprint** | ~15MB app size vs 200MB+ for Electron alternatives | + +## Download + + + + + + + + + + + + +

macOS

Windows

Linux
Intel & Apple Silicon
.dmg
x64
.msi / .exe
x64 & ARM64
.deb
+ +## How It Works + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ │ +│ 1. SELECT 2. SELECT 3. SELECT 4. FLASH │ +│ MANUFACTURER BOARD IMAGE & VERIFY │ +│ │ +│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌───────┐ │ +│ │ Orange │ → │ Pi 5 │ → │ Bookworm│ → │ ██ │ │ +│ │ Pi │ │ │ │ Desktop │ │ ████ │ │ +│ │ Khadas │ │ Pi 4 │ │ Minimal │ │ ████ │ │ +│ │ Radxa │ │ Zero 3 │ │ Nightly │ │ 100% │ │ +│ └─────────┘ └─────────┘ └─────────┘ └───────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +1. **Select Manufacturer** — Choose from 70+ SBC manufacturers or load a custom image +2. **Select Board** — Pick your board with real photos from armbian.com +3. **Select Image** — Choose desktop/server, kernel variant, stable/nightly +4. **Flash** — Download, decompress, write, and verify automatically + +## Tech Stack + +Built with modern technologies for optimal performance: + +| Layer | Technology | Why | +|-------|------------|-----| +| **UI** | React 19 + TypeScript | Type-safe, component-based UI | +| **Bundler** | Vite 7 | Lightning-fast HMR and builds | +| **Framework** | Tauri 2 | Native performance, tiny bundle | +| **Backend** | Rust | Memory-safe, blazing fast I/O | +| **Async** | Tokio | Efficient concurrent downloads | + +### Why Tauri over Electron? + +| Metric | Armbian Imager (Tauri) | Typical Electron App | +|--------|------------------------|---------------------| +| App Size | ~15 MB | 150-200 MB | +| RAM Usage | ~50 MB | 200-400 MB | +| Startup | < 1 second | 2-5 seconds | +| Native Feel | ✅ Uses system webview | ❌ Bundles Chromium | + +## Platform Support + +| Platform | Architecture | Status | Notes | +|----------|-------------|--------|-------| +| macOS | Intel x64 | ✅ | Full support | +| macOS | Apple Silicon | ✅ | Native ARM64 + Touch ID | +| Windows | x64 | ✅ | Admin elevation via UAC | +| Linux | x64 | ✅ | pkexec for privileges | +| Linux | ARM64 | ✅ | Native ARM64 build | + +## Development + +### Prerequisites + +- **Node.js 20+** (LTS recommended) +- **Rust 1.77+** (install via [rustup](https://rustup.rs)) +- **Platform tools**: Xcode (macOS), Visual Studio Build Tools (Windows), or build-essential (Linux) + +### Quick Start + +```bash +# Clone +git clone https://github.com/armbian/armbian-imager.git +cd armbian-imager + +# Install dependencies +npm install + +# Run in development mode +npm run tauri:dev + +# Build for production +npm run tauri:build +``` + +### Available Scripts + +```bash +# Development +npm run dev # Frontend only (Vite) +npm run tauri:dev # Full app with hot reload + +# Production +npm run build # Build frontend +npm run tauri:build # Build distributable + +# Utilities +npm run lint # ESLint +npm run clean # Clean all build artifacts +``` + +### Build Scripts + +```bash +# Platform-specific builds (output in releases/) +./scripts/build-macos.sh [--clean] [--dev] # macOS ARM64 + x64 +./scripts/build-linux.sh [--clean] [--dev] # Linux x64 + ARM64 +./scripts/build-all.sh [--clean] [--dev] # All platforms +``` + +
+Project Structure + +``` +armbian-imager/ +├── src/ # React Frontend +│ ├── components/ # UI Components +│ │ ├── Header.tsx # Progress steps header +│ │ ├── HomePage.tsx # Main wizard interface +│ │ ├── ManufacturerModal.tsx # Manufacturer selection +│ │ ├── BoardModal.tsx # Board selection +│ │ ├── ImageModal.tsx # Image selection +│ │ ├── DeviceModal.tsx # Device selection +│ │ └── FlashProgress/ # Flash operation UI +│ ├── hooks/ # React Hooks +│ ├── config/ # Configuration +│ ├── styles/ # Modular CSS +│ └── assets/ # Images and logos +│ +├── src-tauri/ # Rust Backend +│ ├── src/ +│ │ ├── commands/ # Tauri IPC handlers +│ │ ├── devices/ # Platform device detection +│ │ ├── flash/ # Platform flash implementation +│ │ ├── images/ # Image management +│ │ └── utils/ # Utilities +│ ├── icons/ # App icons +│ └── tauri.conf.json # Tauri configuration +│ +├── scripts/ # Build scripts +└── .github/workflows/ # CI/CD +``` + +
+ +## Data Sources + +| Data | Source | +|------|--------| +| Board List | [github.armbian.com/all-images.json](https://github.armbian.com/all-images.json) | +| Board Photos | Scraped from [armbian.com](https://www.armbian.com) board pages | +| Checksums | Embedded in image metadata | + +## Contributing + +We welcome contributions! Whether it's: + +- 🐛 **Bug reports** — Found an issue? [Open a ticket](https://github.com/armbian/armbian-imager/issues) +- 💡 **Feature requests** — Have an idea? Let's discuss it +- 🔧 **Pull requests** — Code improvements are always welcome +- 📖 **Documentation** — Help others get started + +### Development Workflow + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## License + +This project is part of the [Armbian](https://www.armbian.com) ecosystem and is licensed under the GPLv2. + +## Acknowledgments + +- **[Raspberry Pi Imager](https://github.com/raspberrypi/rpi-imager)** — The inspiration for this project +- **[Tauri](https://tauri.app/)** — The framework that makes native apps accessible +- **[Armbian Community](https://forum.armbian.com)** — For years of amazing work on SBC support + +--- + +

+ Made with ❤️ by the Armbian community +

diff --git a/build/Dockerfile.arm64-build b/build/Dockerfile.arm64-build new file mode 100644 index 0000000..f8be7a5 --- /dev/null +++ b/build/Dockerfile.arm64-build @@ -0,0 +1,21 @@ +FROM --platform=linux/arm64 rust:1.87-slim-bookworm + +# Install build dependencies +RUN apt-get update && apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf build-essential curl wget file libssl-dev libgtk-3-dev squashfs-tools pkg-config nodejs npm && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Copy project +COPY . . + +# Install npm dependencies +RUN npm install + +# Build frontend +RUN npm run build + +# Build Tauri app for deb +RUN cargo install tauri-cli --version "^2" +RUN cargo tauri build --bundles deb + +CMD ["ls", "-la", "src-tauri/target/release/bundle/deb/"] diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/index.html b/index.html new file mode 100644 index 0000000..7ca469e --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Armbian Imager + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..34e7098 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3466 @@ +{ + "name": "armbian-imager", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "armbian-imager", + "version": "0.1.0", + "dependencies": { + "@tauri-apps/api": "^2.9.1", + "lucide-react": "^0.560.0", + "react": "^19.2.2", + "react-dom": "^19.2.2" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tauri-apps/cli": "^2.9.6", + "@types/node": "^24.10.3", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.49.0", + "vite": "^7.2.4" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "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.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "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.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "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.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "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.1", + "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.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "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.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "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.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tauri-apps/api": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.1.tgz", + "integrity": "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.9.6.tgz", + "integrity": "sha512-3xDdXL5omQ3sPfBfdC8fCtDKcnyV7OqyzQgfyT5P3+zY6lcPqIYKQBvUasNvppi21RSdfhy44ttvJmftb0PCDw==", + "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.9.6", + "@tauri-apps/cli-darwin-x64": "2.9.6", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.6", + "@tauri-apps/cli-linux-arm64-gnu": "2.9.6", + "@tauri-apps/cli-linux-arm64-musl": "2.9.6", + "@tauri-apps/cli-linux-riscv64-gnu": "2.9.6", + "@tauri-apps/cli-linux-x64-gnu": "2.9.6", + "@tauri-apps/cli-linux-x64-musl": "2.9.6", + "@tauri-apps/cli-win32-arm64-msvc": "2.9.6", + "@tauri-apps/cli-win32-ia32-msvc": "2.9.6", + "@tauri-apps/cli-win32-x64-msvc": "2.9.6" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.9.6.tgz", + "integrity": "sha512-gf5no6N9FCk1qMrti4lfwP77JHP5haASZgVbBgpZG7BUepB3fhiLCXGUK8LvuOjP36HivXewjg72LTnPDScnQQ==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.9.6.tgz", + "integrity": "sha512-oWh74WmqbERwwrwcueJyY6HYhgCksUc6NT7WKeXyrlY/FPmNgdyQAgcLuTSkhRFuQ6zh4Np1HZpOqCTpeZBDcw==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.9.6.tgz", + "integrity": "sha512-/zde3bFroFsNXOHN204DC2qUxAcAanUjVXXSdEGmhwMUZeAQalNj5cz2Qli2elsRjKN/hVbZOJj0gQ5zaYUjSg==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.9.6.tgz", + "integrity": "sha512-pvbljdhp9VOo4RnID5ywSxgBs7qiylTPlK56cTk7InR3kYSTJKYMqv/4Q/4rGo/mG8cVppesKIeBMH42fw6wjg==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.9.6.tgz", + "integrity": "sha512-02TKUndpodXBCR0oP//6dZWGYcc22Upf2eP27NvC6z0DIqvkBBFziQUcvi2n6SrwTRL0yGgQjkm9K5NIn8s6jw==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.9.6.tgz", + "integrity": "sha512-fmp1hnulbqzl1GkXl4aTX9fV+ubHw2LqlLH1PE3BxZ11EQk+l/TmiEongjnxF0ie4kV8DQfDNJ1KGiIdWe1GvQ==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.9.6.tgz", + "integrity": "sha512-vY0le8ad2KaV1PJr+jCd8fUF9VOjwwQP/uBuTJvhvKTloEwxYA/kAjKK9OpIslGA9m/zcnSo74czI6bBrm2sYA==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.9.6.tgz", + "integrity": "sha512-TOEuB8YCFZTWVDzsO2yW0+zGcoMiPPwcUgdnW1ODnmgfwccpnihDRoks+ABT1e3fHb1ol8QQWsHSCovb3o2ENQ==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.9.6.tgz", + "integrity": "sha512-ujmDGMRc4qRLAnj8nNG26Rlz9klJ0I0jmZs2BPpmNNf0gM/rcVHhqbEkAaHPTBVIrtUdf7bGvQAD2pyIiUrBHQ==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.9.6.tgz", + "integrity": "sha512-S4pT0yAJgFX8QRCyKA1iKjZ9Q/oPjCZf66A/VlG5Yw54Nnr88J1uBpmenINbXxzyhduWrIXBaUbEY1K80ZbpMg==", + "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.9.6", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.9.6.tgz", + "integrity": "sha512-ldWuWSSkWbKOPjQMJoYVj9wLHcOniv7diyI5UAJ4XsBdtaFB0pKHQsqw/ItUma0VXGC7vB4E9fZjivmxur60aw==", + "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.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/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": "24.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.3.tgz", + "integrity": "sha512-gqkrWUsS8hcm0r44yn7/xZeV1ERva/nLgrLxFRUGb7aoNMIJfZJ3AC261zDQuOAKC7MiXai1WCpYc48jAHoShQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz", + "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/type-utils": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "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.49.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.49.0.tgz", + "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "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 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.49.0.tgz", + "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.49.0", + "@typescript-eslint/types": "^8.49.0", + "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": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz", + "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz", + "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==", + "dev": true, + "license": "MIT", + "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 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz", + "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "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 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz", + "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==", + "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.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz", + "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.49.0", + "@typescript-eslint/tsconfig-utils": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/visitor-keys": "8.49.0", + "debug": "^4.3.4", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.1.0" + }, + "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 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "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.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.49.0.tgz", + "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.49.0", + "@typescript-eslint/types": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0" + }, + "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 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz", + "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.49.0", + "eslint-visitor-keys": "^4.2.1" + }, + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.53", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "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/baseline-browser-mapping": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz", + "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/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.30001760", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "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/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.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "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.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "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.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.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": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "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.24", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", + "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "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.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "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.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "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-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/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/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/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.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "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/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/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.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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/lucide-react": { + "version": "0.560.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.560.0.tgz", + "integrity": "sha512-NwKoUA/aBShsdL8WE5lukV2F/tjHzQRlonQs7fkNGI1sCT0Ay4a9Ap3ST2clUUkcY+9eQ0pBe2hybTQd2fmyDA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/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": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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/react": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.2.tgz", + "integrity": "sha512-BdOGOY8OKRBcgoDkwqA8Q5XvOIhoNx/Sh6BnGJlet2Abt0X5BK0BDrqGyQgLhAVjD2nAg5f6o01u/OPUhG022Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.2.tgz", + "integrity": "sha512-fhyD2BLrew6qYf4NNtHff1rLXvzR25rq49p+FeqByOazc6TcSi2n8EYulo5C1PbH+1uBW++5S1SG7FcUU6mlDg==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.2" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "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/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "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/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/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/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.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.49.0.tgz", + "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.49.0", + "@typescript-eslint/parser": "8.49.0", + "@typescript-eslint/typescript-estree": "8.49.0", + "@typescript-eslint/utils": "8.49.0" + }, + "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 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "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/vite": { + "version": "7.2.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.7.tgz", + "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "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/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" + } + }, + "node_modules/zod": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c374334 --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "armbian-imager", + "private": true, + "version": "1.0.0", + "description": "Flash Armbian OS images to SD cards and USB drives", + "author": "Armbian", + "maintainers": [ + "SuperKali " + ], + "type": "module", + "engines": { + "node": ">=20.19.0" + }, + "scripts": { + "--dev": "Development commands", + "dev": "vite", + "build:dev": "tsc -b && vite build --mode development", + "tauri:dev": "tauri dev", + "tauri:build:dev": "tauri build --debug", + + "--prod": "Production commands", + "build": "tsc -b && vite build", + "build:prod": "tsc -b && vite build --mode production", + "tauri:build": "tauri build", + "tauri:build:prod": "tauri build", + + "--other": "Other commands", + "lint": "eslint .", + "preview": "vite preview", + "clean": "rm -rf node_modules dist src-tauri/target", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2.9.1", + "lucide-react": "^0.560.0", + "react": "^19.2.2", + "react-dom": "^19.2.2" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tauri-apps/cli": "^2.9.6", + "@types/node": "^24.10.3", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.49.0", + "vite": "^7.2.4" + } +} diff --git a/public/armbian-icon.png b/public/armbian-icon.png new file mode 100644 index 0000000..b4183f3 Binary files /dev/null and b/public/armbian-icon.png differ diff --git a/scripts/build-all.sh b/scripts/build-all.sh new file mode 100755 index 0000000..1eb9baa --- /dev/null +++ b/scripts/build-all.sh @@ -0,0 +1,289 @@ +#!/bin/bash +set -e + +# Build script for Armbian Imager - all platforms +# Run from project root: ./scripts/build-all.sh + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +OUTPUT_DIR="$PROJECT_DIR/releases" + +cd "$PROJECT_DIR" + +# Create output directory (don't clean - allow incremental builds) +mkdir -p "$OUTPUT_DIR" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE} Armbian Imager - Multi-Platform Build ${NC}" +echo -e "${BLUE}========================================${NC}" + +# Check prerequisites +echo -e "\n${YELLOW}Checking prerequisites...${NC}" + +if ! command -v cargo &> /dev/null; then + echo -e "${RED}Error: Rust/Cargo not found. Install from https://rustup.rs${NC}" + exit 1 +fi + +if ! command -v npm &> /dev/null; then + echo -e "${RED}Error: npm not found. Install Node.js first.${NC}" + exit 1 +fi + +# Install tauri-cli if needed +if ! cargo tauri --version &> /dev/null 2>&1; then + echo -e "${YELLOW}Installing tauri-cli...${NC}" + cargo install tauri-cli --version "^2" +fi + +# Build frontend first +echo -e "\n${YELLOW}Building frontend (mode: $VITE_MODE)...${NC}" +npm ci +npm run build -- --mode "$VITE_MODE" + +# ============================================ +# macOS Builds (Native) +# ============================================ +build_macos() { + echo -e "\n${GREEN}========================================${NC}" + echo -e "${GREEN} Building for macOS${NC}" + echo -e "${GREEN}========================================${NC}" + + # Disable GUI interactions for hdiutil/DMG creation + export CI=true + export NONINTERACTIVE=1 + + # Determine current architecture + CURRENT_ARCH=$(uname -m) + if [ "$CURRENT_ARCH" = "arm64" ]; then + NATIVE_TARGET="aarch64-apple-darwin" + NATIVE_NAME="arm64" + OTHER_TARGET="x86_64-apple-darwin" + OTHER_NAME="x64" + else + NATIVE_TARGET="x86_64-apple-darwin" + NATIVE_NAME="x64" + OTHER_TARGET="aarch64-apple-darwin" + OTHER_NAME="arm64" + fi + + # Build native architecture + echo -e "\n${YELLOW}Building macOS $NATIVE_NAME (native)...${NC}" + cargo tauri build $TAURI_BUILD_FLAGS --bundles dmg 2>&1 | tee /tmp/tauri-build.log + + # Copy and rename native DMG immediately + echo -e "\n${YELLOW}Copying $NATIVE_NAME artifacts to releases...${NC}" + for f in src-tauri/target/release/bundle/dmg/*.dmg; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .dmg) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_macos_${NATIVE_NAME}.dmg" + fi + done + + # Build other architecture + echo -e "\n${YELLOW}Building macOS $OTHER_NAME (cross-compile)...${NC}" + rustup target add "$OTHER_TARGET" 2>/dev/null || true + cargo tauri build $TAURI_BUILD_FLAGS --target "$OTHER_TARGET" --bundles dmg 2>&1 | tee /tmp/tauri-build.log + + # Copy and rename cross-compiled DMG immediately + echo -e "\n${YELLOW}Copying $OTHER_NAME artifacts to releases...${NC}" + for f in src-tauri/target/"$OTHER_TARGET"/release/bundle/dmg/*.dmg; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .dmg) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_macos_${OTHER_NAME}.dmg" + fi + done + + echo -e "${GREEN}macOS builds complete!${NC}" + echo -e "${GREEN}Files in: $OUTPUT_DIR${NC}" + ls -la "$OUTPUT_DIR"/*.dmg 2>/dev/null || true +} + +# ============================================ +# Linux Builds (via Docker) +# ============================================ +build_linux() { + echo -e "\n${GREEN}========================================${NC}" + echo -e "${GREEN} Building for Linux (via Docker)${NC}" + echo -e "${GREEN}========================================${NC}" + + if ! command -v docker &> /dev/null; then + echo -e "${RED}Docker not found, skipping Linux builds${NC}" + return + fi + + # Linux x86_64 + echo -e "\n${YELLOW}Building Linux x86_64...${NC}" + docker run --rm -v "$PROJECT_DIR":/app -w /app \ + --platform linux/amd64 \ + debian:bookworm-slim \ + bash -c ' + apt-get update && apt-get install -y \ + curl build-essential \ + libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \ + librsvg2-dev patchelf libssl-dev libgtk-3-dev \ + squashfs-tools pkg-config nodejs npm && \ + curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + . "$HOME/.cargo/env" && \ + npm ci && npm run build && \ + cargo install tauri-cli --version "^2" --locked && \ + cargo tauri build --bundles deb || true + ' + + # Copy x64 artifacts + for f in src-tauri/target/release/bundle/deb/*.deb; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .deb) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_x64.deb" + fi + done + for f in src-tauri/target/release/bundle/appimage/*.AppImage; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .AppImage) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_x64.AppImage" + fi + done + + # Clean target for arm64 build + rm -rf src-tauri/target/release/bundle + + # Linux ARM64 + echo -e "\n${YELLOW}Building Linux ARM64 (this will be slow via emulation)...${NC}" + docker run --rm -v "$PROJECT_DIR":/app -w /app \ + --platform linux/arm64 \ + debian:bookworm-slim \ + bash -c ' + apt-get update && apt-get install -y \ + curl build-essential \ + libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \ + librsvg2-dev patchelf libssl-dev libgtk-3-dev \ + squashfs-tools pkg-config nodejs npm && \ + curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + . "$HOME/.cargo/env" && \ + npm ci && npm run build && \ + cargo install tauri-cli --version "^2" --locked && \ + cargo tauri build --bundles deb || true + ' + + # Copy ARM64 artifacts + for f in src-tauri/target/release/bundle/deb/*.deb; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .deb) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_arm64.deb" + fi + done + + echo -e "${GREEN}Linux builds complete!${NC}" +} + +# ============================================ +# Windows Build - Not supported locally +# ============================================ +build_windows() { + echo -e "\n${YELLOW}========================================${NC}" + echo -e "${YELLOW} Windows Build${NC}" + echo -e "${YELLOW}========================================${NC}" + echo -e "" + echo -e "${RED}Windows builds are not supported locally from macOS/Linux.${NC}" + echo -e "" + echo -e "Tauri requires native Windows tools (WebView2, MSVC) that cannot" + echo -e "be cross-compiled. Use one of these options instead:" + echo -e "" + echo -e " ${GREEN}1. GitHub Actions (recommended)${NC}" + echo -e " Push a tag like 'v0.1.0' to trigger automatic builds" + echo -e " See: .github/workflows/build.yml" + echo -e "" + echo -e " ${GREEN}2. Build on Windows directly${NC}" + echo -e " Run on a Windows machine or VM:" + echo -e " cargo tauri build --bundles msi,nsis" + echo -e "" +} + +# ============================================ +# Main +# ============================================ + +# Parse arguments +BUILD_MACOS=false +BUILD_LINUX=false +BUILD_WINDOWS=false +BUILD_ALL=false +DO_CLEAN=false +BUILD_MODE="production" # Default to production + +if [ $# -eq 0 ]; then + BUILD_ALL=true +fi + +while [[ $# -gt 0 ]]; do + case $1 in + --macos) BUILD_MACOS=true; shift ;; + --linux) BUILD_LINUX=true; shift ;; + --windows) BUILD_WINDOWS=true; shift ;; + --all) BUILD_ALL=true; shift ;; + --clean) DO_CLEAN=true; shift ;; + --dev) BUILD_MODE="development"; shift ;; + --prod) BUILD_MODE="production"; shift ;; + *) + echo "Usage: $0 [--macos] [--linux] [--windows] [--all] [--clean] [--dev] [--prod]" + echo "" + echo " Platforms:" + echo " --macos Build for macOS (x64 and ARM64)" + echo " --linux Build for Linux (x64 and ARM64, requires Docker)" + echo " --windows Build for Windows (x64, requires Docker)" + echo " --all Build all platforms (default if no args)" + echo "" + echo " Build mode:" + echo " --dev Development build (debug, context menu enabled)" + echo " --prod Production build (optimized, context menu disabled) [default]" + echo "" + echo " Other:" + echo " --clean Clean build artifacts before building" + exit 1 + ;; + esac +done + +# Set build flags based on mode +if [ "$BUILD_MODE" = "development" ]; then + echo -e "${YELLOW}Building in DEVELOPMENT mode${NC}" + TAURI_BUILD_FLAGS="--debug" + VITE_MODE="development" +else + echo -e "${GREEN}Building in PRODUCTION mode${NC}" + TAURI_BUILD_FLAGS="" + VITE_MODE="production" +fi + +# Clean if requested +if $DO_CLEAN; then + echo -e "${YELLOW}Cleaning build artifacts...${NC}" + rm -rf "$OUTPUT_DIR" + rm -rf "$PROJECT_DIR/src-tauri/target" + rm -rf "$PROJECT_DIR/dist" +fi + +if $BUILD_ALL; then + BUILD_MACOS=true + BUILD_LINUX=true + BUILD_WINDOWS=true +fi + +# Run builds +$BUILD_MACOS && build_macos +$BUILD_LINUX && build_linux +$BUILD_WINDOWS && build_windows + +# Summary +echo -e "\n${BLUE}========================================${NC}" +echo -e "${BLUE} Build Complete!${NC}" +echo -e "${BLUE}========================================${NC}" +echo -e "\nArtifacts in: ${GREEN}$OUTPUT_DIR${NC}\n" +ls -la "$OUTPUT_DIR" diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh new file mode 100755 index 0000000..916b46c --- /dev/null +++ b/scripts/build-linux.sh @@ -0,0 +1,165 @@ +#!/bin/bash +set -e + +# Quick Linux build script - builds via Docker +cd "$(dirname "$0")/.." + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# Parse arguments +BUILD_MODE="production" +BUILD_ARCH="all" # all, x64, arm64 + +CLEAN_BUILD=false + +while [[ $# -gt 0 ]]; do + case $1 in + --dev) BUILD_MODE="development"; shift ;; + --prod) BUILD_MODE="production"; shift ;; + --x64) BUILD_ARCH="x64"; shift ;; + --arm64) BUILD_ARCH="arm64"; shift ;; + --clean) CLEAN_BUILD=true; shift ;; + *) + echo "Usage: $0 [--dev] [--prod] [--x64] [--arm64] [--clean]" + echo "" + echo " Build mode:" + echo " --dev Development build (debug symbols, context menu enabled)" + echo " --prod Production build (optimized) [default]" + echo "" + echo " Architecture:" + echo " --x64 Build only x86_64" + echo " --arm64 Build only ARM64" + echo " (default: build both)" + echo "" + echo " Options:" + echo " --clean Clean build artifacts before building" + exit 1 + ;; + esac +done + +# Clean if requested +if [ "$CLEAN_BUILD" = true ]; then + echo -e "${YELLOW}Cleaning build artifacts...${NC}" + rm -rf src-tauri/target + rm -rf dist + rm -rf releases +fi + +# Check Docker +if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker not found. Install Docker first.${NC}" + exit 1 +fi + +# Set build flags +if [ "$BUILD_MODE" = "development" ]; then + echo -e "${YELLOW}Building in DEVELOPMENT mode${NC}" + TAURI_FLAGS="--debug" + VITE_MODE="development" +else + echo -e "${GREEN}Building in PRODUCTION mode${NC}" + TAURI_FLAGS="" + VITE_MODE="production" +fi + +# Output directory +OUTPUT_DIR="./releases" +mkdir -p "$OUTPUT_DIR" + +# Build x64 +build_x64() { + echo -e "\n${GREEN}========================================${NC}" + echo -e "${GREEN} Building Linux x86_64${NC}" + echo -e "${GREEN}========================================${NC}" + + docker run --rm -v "$(pwd)":/app -w /app \ + --platform linux/amd64 \ + rust:bookworm \ + bash -c " + apt-get update && apt-get install -y \ + libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \ + librsvg2-dev patchelf libssl-dev libgtk-3-dev \ + squashfs-tools pkg-config nodejs npm && \ + npm ci && \ + npm run build -- --mode $VITE_MODE && \ + cargo install tauri-cli --version '^2' --locked && \ + cargo tauri build $TAURI_FLAGS --bundles deb || true + " + + # Copy artifacts + for f in src-tauri/target/release/bundle/deb/*.deb; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .deb) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_x64.deb" + fi + done + for f in src-tauri/target/release/bundle/appimage/*.AppImage; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .AppImage) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_x64.AppImage" + fi + done + + echo -e "${GREEN}Linux x64 build complete!${NC}" +} + +# Build ARM64 +build_arm64() { + echo -e "\n${GREEN}========================================${NC}" + echo -e "${GREEN} Building Linux ARM64${NC}" + echo -e "${GREEN}========================================${NC}" + + docker run --rm -v "$(pwd)":/app -w /app \ + --platform linux/arm64 \ + rust:bookworm \ + bash -c " + apt-get update && apt-get install -y \ + libwebkit2gtk-4.1-dev libayatana-appindicator3-dev \ + librsvg2-dev patchelf libssl-dev libgtk-3-dev \ + squashfs-tools pkg-config nodejs npm && \ + npm ci && \ + npm run build -- --mode $VITE_MODE && \ + cargo install tauri-cli --version '^2' --locked && \ + cargo tauri build $TAURI_FLAGS --bundles deb || true + " + + # Copy artifacts + for f in src-tauri/target/release/bundle/deb/*.deb; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .deb) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_arm64.deb" + fi + done + for f in src-tauri/target/release/bundle/appimage/*.AppImage; do + if [ -f "$f" ]; then + BASENAME=$(basename "$f" .AppImage) + cp -v "$f" "$OUTPUT_DIR/${BASENAME}_linux_arm64.AppImage" + fi + done + + echo -e "${GREEN}Linux ARM64 build complete!${NC}" +} + +# Run builds based on architecture selection +case $BUILD_ARCH in + x64) build_x64 ;; + arm64) build_arm64 ;; + all) + build_x64 + # Clean between builds + rm -rf src-tauri/target/release/bundle + build_arm64 + ;; +esac + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN} Linux Build Complete!${NC}" +echo -e "${GREEN}========================================${NC}" +echo -e "Output directory: $OUTPUT_DIR" +ls -la "$OUTPUT_DIR"/*.deb "$OUTPUT_DIR"/*.AppImage 2>/dev/null || true diff --git a/scripts/build-macos.sh b/scripts/build-macos.sh new file mode 100755 index 0000000..18b0ffa --- /dev/null +++ b/scripts/build-macos.sh @@ -0,0 +1,97 @@ +#!/bin/bash +set -e + +# Quick macOS build script - builds both ARM64 and x86_64 +cd "$(dirname "$0")/.." + +# Parse arguments +BUILD_MODE="production" +CLEAN_BUILD=false + +while [[ $# -gt 0 ]]; do + case $1 in + --dev) BUILD_MODE="development"; shift ;; + --prod) BUILD_MODE="production"; shift ;; + --clean) CLEAN_BUILD=true; shift ;; + *) + echo "Usage: $0 [--dev] [--prod] [--clean]" + echo " --dev Development build (debug symbols, context menu enabled)" + echo " --prod Production build (optimized) [default]" + echo " --clean Clean build artifacts before building" + exit 1 + ;; + esac +done + +# Clean if requested +if [ "$CLEAN_BUILD" = true ]; then + echo "Cleaning build artifacts..." + rm -rf src-tauri/target + rm -rf dist + rm -rf releases +fi + +# Set build flags +if [ "$BUILD_MODE" = "development" ]; then + echo "Building in DEVELOPMENT mode" + TAURI_FLAGS="--debug" + VITE_MODE="development" +else + echo "Building in PRODUCTION mode" + TAURI_FLAGS="" + VITE_MODE="production" +fi + +# Disable GUI interactions +export CI=true +export NONINTERACTIVE=1 + +# Output directory +OUTPUT_DIR="./releases" +rm -rf "$OUTPUT_DIR" +mkdir -p "$OUTPUT_DIR" + +echo "Building frontend (mode: $VITE_MODE)..." +npm run build -- --mode "$VITE_MODE" + +echo "" +echo "=========================================" +echo "Building macOS ARM64 (Apple Silicon)..." +echo "=========================================" +cargo tauri build $TAURI_FLAGS --bundles dmg 2>&1 || { + echo "DMG failed, trying app bundle only..." + cargo tauri build $TAURI_FLAGS --bundles app +} + +# Copy ARM64 artifacts +for f in src-tauri/target/release/bundle/dmg/*.dmg; do + [ -f "$f" ] && cp -v "$f" "$OUTPUT_DIR/$(basename "${f%.dmg}")_macos_arm64.dmg" +done +for f in src-tauri/target/release/bundle/macos/*.app; do + [ -d "$f" ] && cp -rv "$f" "$OUTPUT_DIR/$(basename "${f%.app}")_macos_arm64.app" +done + +echo "" +echo "=========================================" +echo "Building macOS x86_64 (Intel)..." +echo "=========================================" +rustup target add x86_64-apple-darwin 2>/dev/null || true +cargo tauri build $TAURI_FLAGS --target x86_64-apple-darwin --bundles dmg 2>&1 || { + echo "DMG failed, trying app bundle only..." + cargo tauri build $TAURI_FLAGS --target x86_64-apple-darwin --bundles app +} + +# Copy x64 artifacts +for f in src-tauri/target/x86_64-apple-darwin/release/bundle/dmg/*.dmg; do + [ -f "$f" ] && cp -v "$f" "$OUTPUT_DIR/$(basename "${f%.dmg}")_macos_x64.dmg" +done +for f in src-tauri/target/x86_64-apple-darwin/release/bundle/macos/*.app; do + [ -d "$f" ] && cp -rv "$f" "$OUTPUT_DIR/$(basename "${f%.app}")_macos_x64.app" +done + +echo "" +echo "=========================================" +echo "Build complete!" +echo "=========================================" +echo "Output directory: $OUTPUT_DIR" +ls -la "$OUTPUT_DIR/" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..128fb04 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "armbian-imager" +version = "1.0.0" +description = "Armbian Imager - Flash Armbian OS images to SD cards and USB drives" +authors = ["Armbian", "SuperKali "] +edition = "2021" +rust-version = "1.77.2" + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = ["macos-private-api"] } +tauri-plugin-shell = "2" +tauri-plugin-dialog = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["full"] } +reqwest = { version = "0.12", features = ["json", "stream"] } +futures-util = "0.3" +xz2 = "0.1" +sha2 = "0.10" +hex = "0.4" +scraper = "0.22" +dirs = "5" +once_cell = "1.21.3" +chrono = "0.4" + +[target.'cfg(target_os = "linux")'.dependencies] +# Linux-specific dependencies for block device access + +[target.'cfg(target_os = "macos")'.dependencies] +# macOS-specific dependencies +libc = "0.2" +security-framework = "2.11" +security-framework-sys = "2.11" + +[target.'cfg(target_os = "windows")'.dependencies] +# Windows-specific dependencies + +[features] +default = ["custom-protocol"] +custom-protocol = ["tauri/custom-protocol"] + +[profile.release] +panic = "abort" +codegen-units = 1 +lto = true +opt-level = "s" +strip = true diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/dmg-background.png b/src-tauri/dmg-background.png new file mode 100644 index 0000000..4286a3f Binary files /dev/null and b/src-tauri/dmg-background.png differ diff --git a/src-tauri/dmg-background@2x.png b/src-tauri/dmg-background@2x.png new file mode 100644 index 0000000..3545832 Binary files /dev/null and b/src-tauri/dmg-background@2x.png differ diff --git a/src-tauri/entitlements.plist b/src-tauri/entitlements.plist new file mode 100644 index 0000000..85acc5a --- /dev/null +++ b/src-tauri/entitlements.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.device.usb + + com.apple.security.files.user-selected.read-write + + + diff --git a/src-tauri/gen/schemas/acl-manifests.json b/src-tauri/gen/schemas/acl-manifests.json new file mode 100644 index 0000000..35f90a7 --- /dev/null +++ b/src-tauri/gen/schemas/acl-manifests.json @@ -0,0 +1 @@ +{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-ask","allow-confirm","allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope.","commands":{"allow":["ask"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope.","commands":{"allow":["confirm"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope.","commands":{"allow":[],"deny":["ask"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope.","commands":{"allow":[],"deny":["confirm"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}} \ No newline at end of file diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src-tauri/gen/schemas/capabilities.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src-tauri/gen/schemas/desktop-schema.json b/src-tauri/gen/schemas/desktop-schema.json new file mode 100644 index 0000000..fcf88e0 --- /dev/null +++ b/src-tauri/gen/schemas/desktop-schema.json @@ -0,0 +1,2630 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", + "type": "string", + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", + "type": "string", + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." + }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." + }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." + }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." + }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." + }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." + }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", + "type": "string", + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", + "type": "string", + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", + "type": "string", + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", + "type": "string", + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", + "type": "string", + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", + "type": "string", + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", + "type": "string", + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", + "type": "string", + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." + }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." + }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope." + }, + { + "description": "Enables the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope." + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope." + }, + { + "description": "Denies the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope." + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/gen/schemas/linux-schema.json b/src-tauri/gen/schemas/linux-schema.json new file mode 100644 index 0000000..f827fe1 --- /dev/null +++ b/src-tauri/gen/schemas/linux-schema.json @@ -0,0 +1,2564 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", + "type": "string", + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", + "type": "string", + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." + }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." + }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." + }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." + }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." + }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." + }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", + "type": "string", + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", + "type": "string", + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", + "type": "string", + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", + "type": "string", + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", + "type": "string", + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", + "type": "string", + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", + "type": "string", + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", + "type": "string", + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." + }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." + }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/gen/schemas/macOS-schema.json b/src-tauri/gen/schemas/macOS-schema.json new file mode 100644 index 0000000..fcf88e0 --- /dev/null +++ b/src-tauri/gen/schemas/macOS-schema.json @@ -0,0 +1,2630 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CapabilityFile", + "description": "Capability formats accepted in a capability file.", + "anyOf": [ + { + "description": "A single capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "A list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + }, + { + "description": "A list of capabilities.", + "type": "object", + "required": [ + "capabilities" + ], + "properties": { + "capabilities": { + "description": "The list of capabilities.", + "type": "array", + "items": { + "$ref": "#/definitions/Capability" + } + } + } + } + ], + "definitions": { + "Capability": { + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", + "type": "string" + }, + "description": { + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + }, + "uniqueItems": true + }, + "platforms": { + "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "allOf": [ + { + "if": { + "properties": { + "identifier": { + "anyOf": [ + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + } + } + }, + "then": { + "properties": { + "allow": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + }, + "deny": { + "items": { + "title": "ShellScopeEntry", + "description": "Shell scope entry.", + "anyOf": [ + { + "type": "object", + "required": [ + "cmd", + "name" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "cmd": { + "description": "The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", + "type": "string" + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellScopeEntryAllowedArgs" + } + ] + }, + "name": { + "description": "The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.", + "type": "string" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + } + } + }, + { + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ], + "required": [ + "identifier" + ] + } + ] + }, + "Identifier": { + "description": "Permission identifier", + "oneOf": [ + { + "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`", + "type": "string", + "const": "core:default", + "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`" + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`", + "type": "string", + "const": "core:app:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`" + }, + { + "description": "Enables the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-hide", + "markdownDescription": "Enables the app_hide command without any pre-configured scope." + }, + { + "description": "Enables the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-app-show", + "markdownDescription": "Enables the app_show command without any pre-configured scope." + }, + { + "description": "Enables the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-bundle-type", + "markdownDescription": "Enables the bundle_type command without any pre-configured scope." + }, + { + "description": "Enables the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-default-window-icon", + "markdownDescription": "Enables the default_window_icon command without any pre-configured scope." + }, + { + "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-fetch-data-store-identifiers", + "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Enables the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-identifier", + "markdownDescription": "Enables the identifier command without any pre-configured scope." + }, + { + "description": "Enables the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-name", + "markdownDescription": "Enables the name command without any pre-configured scope." + }, + { + "description": "Enables the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-register-listener", + "markdownDescription": "Enables the register_listener command without any pre-configured scope." + }, + { + "description": "Enables the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-data-store", + "markdownDescription": "Enables the remove_data_store command without any pre-configured scope." + }, + { + "description": "Enables the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-remove-listener", + "markdownDescription": "Enables the remove_listener command without any pre-configured scope." + }, + { + "description": "Enables the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-app-theme", + "markdownDescription": "Enables the set_app_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-set-dock-visibility", + "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-tauri-version", + "markdownDescription": "Enables the tauri_version command without any pre-configured scope." + }, + { + "description": "Enables the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:allow-version", + "markdownDescription": "Enables the version command without any pre-configured scope." + }, + { + "description": "Denies the app_hide command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-hide", + "markdownDescription": "Denies the app_hide command without any pre-configured scope." + }, + { + "description": "Denies the app_show command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-app-show", + "markdownDescription": "Denies the app_show command without any pre-configured scope." + }, + { + "description": "Denies the bundle_type command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-bundle-type", + "markdownDescription": "Denies the bundle_type command without any pre-configured scope." + }, + { + "description": "Denies the default_window_icon command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-default-window-icon", + "markdownDescription": "Denies the default_window_icon command without any pre-configured scope." + }, + { + "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-fetch-data-store-identifiers", + "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope." + }, + { + "description": "Denies the identifier command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-identifier", + "markdownDescription": "Denies the identifier command without any pre-configured scope." + }, + { + "description": "Denies the name command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-name", + "markdownDescription": "Denies the name command without any pre-configured scope." + }, + { + "description": "Denies the register_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-register-listener", + "markdownDescription": "Denies the register_listener command without any pre-configured scope." + }, + { + "description": "Denies the remove_data_store command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-data-store", + "markdownDescription": "Denies the remove_data_store command without any pre-configured scope." + }, + { + "description": "Denies the remove_listener command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-remove-listener", + "markdownDescription": "Denies the remove_listener command without any pre-configured scope." + }, + { + "description": "Denies the set_app_theme command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-app-theme", + "markdownDescription": "Denies the set_app_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_dock_visibility command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-set-dock-visibility", + "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope." + }, + { + "description": "Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-tauri-version", + "markdownDescription": "Denies the tauri_version command without any pre-configured scope." + }, + { + "description": "Denies the version command without any pre-configured scope.", + "type": "string", + "const": "core:app:deny-version", + "markdownDescription": "Denies the version command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`", + "type": "string", + "const": "core:event:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`" + }, + { + "description": "Enables the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit", + "markdownDescription": "Enables the emit command without any pre-configured scope." + }, + { + "description": "Enables the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-emit-to", + "markdownDescription": "Enables the emit_to command without any pre-configured scope." + }, + { + "description": "Enables the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-listen", + "markdownDescription": "Enables the listen command without any pre-configured scope." + }, + { + "description": "Enables the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:allow-unlisten", + "markdownDescription": "Enables the unlisten command without any pre-configured scope." + }, + { + "description": "Denies the emit command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit", + "markdownDescription": "Denies the emit command without any pre-configured scope." + }, + { + "description": "Denies the emit_to command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-emit-to", + "markdownDescription": "Denies the emit_to command without any pre-configured scope." + }, + { + "description": "Denies the listen command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-listen", + "markdownDescription": "Denies the listen command without any pre-configured scope." + }, + { + "description": "Denies the unlisten command without any pre-configured scope.", + "type": "string", + "const": "core:event:deny-unlisten", + "markdownDescription": "Denies the unlisten command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`", + "type": "string", + "const": "core:image:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`" + }, + { + "description": "Enables the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-bytes", + "markdownDescription": "Enables the from_bytes command without any pre-configured scope." + }, + { + "description": "Enables the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-from-path", + "markdownDescription": "Enables the from_path command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-rgba", + "markdownDescription": "Enables the rgba command without any pre-configured scope." + }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:allow-size", + "markdownDescription": "Enables the size command without any pre-configured scope." + }, + { + "description": "Denies the from_bytes command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-bytes", + "markdownDescription": "Denies the from_bytes command without any pre-configured scope." + }, + { + "description": "Denies the from_path command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-from-path", + "markdownDescription": "Denies the from_path command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the rgba command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-rgba", + "markdownDescription": "Denies the rgba command without any pre-configured scope." + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "core:image:deny-size", + "markdownDescription": "Denies the size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`", + "type": "string", + "const": "core:menu:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`" + }, + { + "description": "Enables the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-append", + "markdownDescription": "Enables the append command without any pre-configured scope." + }, + { + "description": "Enables the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-create-default", + "markdownDescription": "Enables the create_default command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-insert", + "markdownDescription": "Enables the insert command without any pre-configured scope." + }, + { + "description": "Enables the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-checked", + "markdownDescription": "Enables the is_checked command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-items", + "markdownDescription": "Enables the items command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-popup", + "markdownDescription": "Enables the popup command without any pre-configured scope." + }, + { + "description": "Enables the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-prepend", + "markdownDescription": "Enables the prepend command without any pre-configured scope." + }, + { + "description": "Enables the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove", + "markdownDescription": "Enables the remove command without any pre-configured scope." + }, + { + "description": "Enables the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-remove-at", + "markdownDescription": "Enables the remove_at command without any pre-configured scope." + }, + { + "description": "Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-accelerator", + "markdownDescription": "Enables the set_accelerator command without any pre-configured scope." + }, + { + "description": "Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-app-menu", + "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-help-menu-for-nsapp", + "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-window-menu", + "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-as-windows-menu-for-nsapp", + "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Enables the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-checked", + "markdownDescription": "Enables the set_checked command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-set-text", + "markdownDescription": "Enables the set_text command without any pre-configured scope." + }, + { + "description": "Enables the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:allow-text", + "markdownDescription": "Enables the text command without any pre-configured scope." + }, + { + "description": "Denies the append command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-append", + "markdownDescription": "Denies the append command without any pre-configured scope." + }, + { + "description": "Denies the create_default command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-create-default", + "markdownDescription": "Denies the create_default command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the insert command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-insert", + "markdownDescription": "Denies the insert command without any pre-configured scope." + }, + { + "description": "Denies the is_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-checked", + "markdownDescription": "Denies the is_checked command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the items command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-items", + "markdownDescription": "Denies the items command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the popup command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-popup", + "markdownDescription": "Denies the popup command without any pre-configured scope." + }, + { + "description": "Denies the prepend command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-prepend", + "markdownDescription": "Denies the prepend command without any pre-configured scope." + }, + { + "description": "Denies the remove command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove", + "markdownDescription": "Denies the remove command without any pre-configured scope." + }, + { + "description": "Denies the remove_at command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-remove-at", + "markdownDescription": "Denies the remove_at command without any pre-configured scope." + }, + { + "description": "Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-accelerator", + "markdownDescription": "Denies the set_accelerator command without any pre-configured scope." + }, + { + "description": "Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-app-menu", + "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-help-menu-for-nsapp", + "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-window-menu", + "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-as-windows-menu-for-nsapp", + "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope." + }, + { + "description": "Denies the set_checked command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-checked", + "markdownDescription": "Denies the set_checked command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-set-text", + "markdownDescription": "Denies the set_text command without any pre-configured scope." + }, + { + "description": "Denies the text command without any pre-configured scope.", + "type": "string", + "const": "core:menu:deny-text", + "markdownDescription": "Denies the text command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`", + "type": "string", + "const": "core:path:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`" + }, + { + "description": "Enables the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-basename", + "markdownDescription": "Enables the basename command without any pre-configured scope." + }, + { + "description": "Enables the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-dirname", + "markdownDescription": "Enables the dirname command without any pre-configured scope." + }, + { + "description": "Enables the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-extname", + "markdownDescription": "Enables the extname command without any pre-configured scope." + }, + { + "description": "Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-is-absolute", + "markdownDescription": "Enables the is_absolute command without any pre-configured scope." + }, + { + "description": "Enables the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-join", + "markdownDescription": "Enables the join command without any pre-configured scope." + }, + { + "description": "Enables the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-normalize", + "markdownDescription": "Enables the normalize command without any pre-configured scope." + }, + { + "description": "Enables the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve", + "markdownDescription": "Enables the resolve command without any pre-configured scope." + }, + { + "description": "Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:allow-resolve-directory", + "markdownDescription": "Enables the resolve_directory command without any pre-configured scope." + }, + { + "description": "Denies the basename command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-basename", + "markdownDescription": "Denies the basename command without any pre-configured scope." + }, + { + "description": "Denies the dirname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-dirname", + "markdownDescription": "Denies the dirname command without any pre-configured scope." + }, + { + "description": "Denies the extname command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-extname", + "markdownDescription": "Denies the extname command without any pre-configured scope." + }, + { + "description": "Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-is-absolute", + "markdownDescription": "Denies the is_absolute command without any pre-configured scope." + }, + { + "description": "Denies the join command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-join", + "markdownDescription": "Denies the join command without any pre-configured scope." + }, + { + "description": "Denies the normalize command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-normalize", + "markdownDescription": "Denies the normalize command without any pre-configured scope." + }, + { + "description": "Denies the resolve command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve", + "markdownDescription": "Denies the resolve command without any pre-configured scope." + }, + { + "description": "Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "const": "core:path:deny-resolve-directory", + "markdownDescription": "Denies the resolve_directory command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`", + "type": "string", + "const": "core:resources:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`" + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:resources:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`", + "type": "string", + "const": "core:tray:default", + "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-show-menu-on-left-click`" + }, + { + "description": "Enables the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-get-by-id", + "markdownDescription": "Enables the get_by_id command without any pre-configured scope." + }, + { + "description": "Enables the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-new", + "markdownDescription": "Enables the new command without any pre-configured scope." + }, + { + "description": "Enables the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-remove-by-id", + "markdownDescription": "Enables the remove_by_id command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-icon-as-template", + "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Enables the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-menu", + "markdownDescription": "Enables the set_menu command without any pre-configured scope." + }, + { + "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-show-menu-on-left-click", + "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-temp-dir-path", + "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-tooltip", + "markdownDescription": "Enables the set_tooltip command without any pre-configured scope." + }, + { + "description": "Enables the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:allow-set-visible", + "markdownDescription": "Enables the set_visible command without any pre-configured scope." + }, + { + "description": "Denies the get_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-get-by-id", + "markdownDescription": "Denies the get_by_id command without any pre-configured scope." + }, + { + "description": "Denies the new command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-new", + "markdownDescription": "Denies the new command without any pre-configured scope." + }, + { + "description": "Denies the remove_by_id command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-remove-by-id", + "markdownDescription": "Denies the remove_by_id command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-icon-as-template", + "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope." + }, + { + "description": "Denies the set_menu command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-menu", + "markdownDescription": "Denies the set_menu command without any pre-configured scope." + }, + { + "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-show-menu-on-left-click", + "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope." + }, + { + "description": "Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-temp-dir-path", + "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-tooltip", + "markdownDescription": "Denies the set_tooltip command without any pre-configured scope." + }, + { + "description": "Denies the set_visible command without any pre-configured scope.", + "type": "string", + "const": "core:tray:deny-set-visible", + "markdownDescription": "Denies the set_visible command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`", + "type": "string", + "const": "core:webview:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`" + }, + { + "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-clear-all-browsing-data", + "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Enables the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview", + "markdownDescription": "Enables the create_webview command without any pre-configured scope." + }, + { + "description": "Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-create-webview-window", + "markdownDescription": "Enables the create_webview_window command without any pre-configured scope." + }, + { + "description": "Enables the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-get-all-webviews", + "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-internal-toggle-devtools", + "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Enables the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-print", + "markdownDescription": "Enables the print command without any pre-configured scope." + }, + { + "description": "Enables the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-reparent", + "markdownDescription": "Enables the reparent command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-background-color", + "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-focus", + "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-position", + "markdownDescription": "Enables the set_webview_position command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-size", + "markdownDescription": "Enables the set_webview_size command without any pre-configured scope." + }, + { + "description": "Enables the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-zoom", + "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Enables the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-close", + "markdownDescription": "Enables the webview_close command without any pre-configured scope." + }, + { + "description": "Enables the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-hide", + "markdownDescription": "Enables the webview_hide command without any pre-configured scope." + }, + { + "description": "Enables the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-position", + "markdownDescription": "Enables the webview_position command without any pre-configured scope." + }, + { + "description": "Enables the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-show", + "markdownDescription": "Enables the webview_show command without any pre-configured scope." + }, + { + "description": "Enables the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-webview-size", + "markdownDescription": "Enables the webview_size command without any pre-configured scope." + }, + { + "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-clear-all-browsing-data", + "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope." + }, + { + "description": "Denies the create_webview command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview", + "markdownDescription": "Denies the create_webview command without any pre-configured scope." + }, + { + "description": "Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-create-webview-window", + "markdownDescription": "Denies the create_webview_window command without any pre-configured scope." + }, + { + "description": "Denies the get_all_webviews command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-get-all-webviews", + "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-internal-toggle-devtools", + "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope." + }, + { + "description": "Denies the print command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-print", + "markdownDescription": "Denies the print command without any pre-configured scope." + }, + { + "description": "Denies the reparent command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-reparent", + "markdownDescription": "Denies the reparent command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-background-color", + "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-focus", + "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-position", + "markdownDescription": "Denies the set_webview_position command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-size", + "markdownDescription": "Denies the set_webview_size command without any pre-configured scope." + }, + { + "description": "Denies the set_webview_zoom command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-zoom", + "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope." + }, + { + "description": "Denies the webview_close command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-close", + "markdownDescription": "Denies the webview_close command without any pre-configured scope." + }, + { + "description": "Denies the webview_hide command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-hide", + "markdownDescription": "Denies the webview_hide command without any pre-configured scope." + }, + { + "description": "Denies the webview_position command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-position", + "markdownDescription": "Denies the webview_position command without any pre-configured scope." + }, + { + "description": "Denies the webview_show command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-show", + "markdownDescription": "Denies the webview_show command without any pre-configured scope." + }, + { + "description": "Denies the webview_size command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-webview-size", + "markdownDescription": "Denies the webview_size command without any pre-configured scope." + }, + { + "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`", + "type": "string", + "const": "core:window:default", + "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-internal-toggle-maximize`" + }, + { + "description": "Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-available-monitors", + "markdownDescription": "Enables the available_monitors command without any pre-configured scope." + }, + { + "description": "Enables the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-center", + "markdownDescription": "Enables the center command without any pre-configured scope." + }, + { + "description": "Enables the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-close", + "markdownDescription": "Enables the close command without any pre-configured scope." + }, + { + "description": "Enables the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-create", + "markdownDescription": "Enables the create command without any pre-configured scope." + }, + { + "description": "Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-current-monitor", + "markdownDescription": "Enables the current_monitor command without any pre-configured scope." + }, + { + "description": "Enables the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-cursor-position", + "markdownDescription": "Enables the cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-destroy", + "markdownDescription": "Enables the destroy command without any pre-configured scope." + }, + { + "description": "Enables the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-get-all-windows", + "markdownDescription": "Enables the get_all_windows command without any pre-configured scope." + }, + { + "description": "Enables the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-hide", + "markdownDescription": "Enables the hide command without any pre-configured scope." + }, + { + "description": "Enables the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-position", + "markdownDescription": "Enables the inner_position command without any pre-configured scope." + }, + { + "description": "Enables the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-inner-size", + "markdownDescription": "Enables the inner_size command without any pre-configured scope." + }, + { + "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-internal-toggle-maximize", + "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-always-on-top", + "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-closable", + "markdownDescription": "Enables the is_closable command without any pre-configured scope." + }, + { + "description": "Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-decorated", + "markdownDescription": "Enables the is_decorated command without any pre-configured scope." + }, + { + "description": "Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-enabled", + "markdownDescription": "Enables the is_enabled command without any pre-configured scope." + }, + { + "description": "Enables the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-focused", + "markdownDescription": "Enables the is_focused command without any pre-configured scope." + }, + { + "description": "Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-fullscreen", + "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximizable", + "markdownDescription": "Enables the is_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-maximized", + "markdownDescription": "Enables the is_maximized command without any pre-configured scope." + }, + { + "description": "Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimizable", + "markdownDescription": "Enables the is_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-minimized", + "markdownDescription": "Enables the is_minimized command without any pre-configured scope." + }, + { + "description": "Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-resizable", + "markdownDescription": "Enables the is_resizable command without any pre-configured scope." + }, + { + "description": "Enables the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-is-visible", + "markdownDescription": "Enables the is_visible command without any pre-configured scope." + }, + { + "description": "Enables the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-maximize", + "markdownDescription": "Enables the maximize command without any pre-configured scope." + }, + { + "description": "Enables the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-minimize", + "markdownDescription": "Enables the minimize command without any pre-configured scope." + }, + { + "description": "Enables the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-monitor-from-point", + "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Enables the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-position", + "markdownDescription": "Enables the outer_position command without any pre-configured scope." + }, + { + "description": "Enables the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-outer-size", + "markdownDescription": "Enables the outer_size command without any pre-configured scope." + }, + { + "description": "Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-primary-monitor", + "markdownDescription": "Enables the primary_monitor command without any pre-configured scope." + }, + { + "description": "Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-request-user-attention", + "markdownDescription": "Enables the request_user_attention command without any pre-configured scope." + }, + { + "description": "Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-scale-factor", + "markdownDescription": "Enables the scale_factor command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-bottom", + "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-always-on-top", + "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Enables the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-background-color", + "markdownDescription": "Enables the set_background_color command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-count", + "markdownDescription": "Enables the set_badge_count command without any pre-configured scope." + }, + { + "description": "Enables the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-badge-label", + "markdownDescription": "Enables the set_badge_label command without any pre-configured scope." + }, + { + "description": "Enables the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-closable", + "markdownDescription": "Enables the set_closable command without any pre-configured scope." + }, + { + "description": "Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-content-protected", + "markdownDescription": "Enables the set_content_protected command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-grab", + "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-icon", + "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-position", + "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-cursor-visible", + "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-decorations", + "markdownDescription": "Enables the set_decorations command without any pre-configured scope." + }, + { + "description": "Enables the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-effects", + "markdownDescription": "Enables the set_effects command without any pre-configured scope." + }, + { + "description": "Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-enabled", + "markdownDescription": "Enables the set_enabled command without any pre-configured scope." + }, + { + "description": "Enables the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focus", + "markdownDescription": "Enables the set_focus command without any pre-configured scope." + }, + { + "description": "Enables the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-focusable", + "markdownDescription": "Enables the set_focusable command without any pre-configured scope." + }, + { + "description": "Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-fullscreen", + "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-icon", + "markdownDescription": "Enables the set_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-ignore-cursor-events", + "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-max-size", + "markdownDescription": "Enables the set_max_size command without any pre-configured scope." + }, + { + "description": "Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-maximizable", + "markdownDescription": "Enables the set_maximizable command without any pre-configured scope." + }, + { + "description": "Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-min-size", + "markdownDescription": "Enables the set_min_size command without any pre-configured scope." + }, + { + "description": "Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-minimizable", + "markdownDescription": "Enables the set_minimizable command without any pre-configured scope." + }, + { + "description": "Enables the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-overlay-icon", + "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Enables the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-position", + "markdownDescription": "Enables the set_position command without any pre-configured scope." + }, + { + "description": "Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-progress-bar", + "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-resizable", + "markdownDescription": "Enables the set_resizable command without any pre-configured scope." + }, + { + "description": "Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-shadow", + "markdownDescription": "Enables the set_shadow command without any pre-configured scope." + }, + { + "description": "Enables the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-simple-fullscreen", + "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Enables the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size", + "markdownDescription": "Enables the set_size command without any pre-configured scope." + }, + { + "description": "Enables the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-size-constraints", + "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-skip-taskbar", + "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Enables the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-theme", + "markdownDescription": "Enables the set_theme command without any pre-configured scope." + }, + { + "description": "Enables the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title", + "markdownDescription": "Enables the set_title command without any pre-configured scope." + }, + { + "description": "Enables the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-title-bar-style", + "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-set-visible-on-all-workspaces", + "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Enables the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-show", + "markdownDescription": "Enables the show command without any pre-configured scope." + }, + { + "description": "Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-dragging", + "markdownDescription": "Enables the start_dragging command without any pre-configured scope." + }, + { + "description": "Enables the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-start-resize-dragging", + "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Enables the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-theme", + "markdownDescription": "Enables the theme command without any pre-configured scope." + }, + { + "description": "Enables the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-title", + "markdownDescription": "Enables the title command without any pre-configured scope." + }, + { + "description": "Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-toggle-maximize", + "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unmaximize", + "markdownDescription": "Enables the unmaximize command without any pre-configured scope." + }, + { + "description": "Enables the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:allow-unminimize", + "markdownDescription": "Enables the unminimize command without any pre-configured scope." + }, + { + "description": "Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-available-monitors", + "markdownDescription": "Denies the available_monitors command without any pre-configured scope." + }, + { + "description": "Denies the center command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-center", + "markdownDescription": "Denies the center command without any pre-configured scope." + }, + { + "description": "Denies the close command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-close", + "markdownDescription": "Denies the close command without any pre-configured scope." + }, + { + "description": "Denies the create command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-create", + "markdownDescription": "Denies the create command without any pre-configured scope." + }, + { + "description": "Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-current-monitor", + "markdownDescription": "Denies the current_monitor command without any pre-configured scope." + }, + { + "description": "Denies the cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-cursor-position", + "markdownDescription": "Denies the cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the destroy command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-destroy", + "markdownDescription": "Denies the destroy command without any pre-configured scope." + }, + { + "description": "Denies the get_all_windows command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-get-all-windows", + "markdownDescription": "Denies the get_all_windows command without any pre-configured scope." + }, + { + "description": "Denies the hide command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-hide", + "markdownDescription": "Denies the hide command without any pre-configured scope." + }, + { + "description": "Denies the inner_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-position", + "markdownDescription": "Denies the inner_position command without any pre-configured scope." + }, + { + "description": "Denies the inner_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-inner-size", + "markdownDescription": "Denies the inner_size command without any pre-configured scope." + }, + { + "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-internal-toggle-maximize", + "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the is_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-always-on-top", + "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the is_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-closable", + "markdownDescription": "Denies the is_closable command without any pre-configured scope." + }, + { + "description": "Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-decorated", + "markdownDescription": "Denies the is_decorated command without any pre-configured scope." + }, + { + "description": "Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-enabled", + "markdownDescription": "Denies the is_enabled command without any pre-configured scope." + }, + { + "description": "Denies the is_focused command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-focused", + "markdownDescription": "Denies the is_focused command without any pre-configured scope." + }, + { + "description": "Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-fullscreen", + "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximizable", + "markdownDescription": "Denies the is_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-maximized", + "markdownDescription": "Denies the is_maximized command without any pre-configured scope." + }, + { + "description": "Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimizable", + "markdownDescription": "Denies the is_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-minimized", + "markdownDescription": "Denies the is_minimized command without any pre-configured scope." + }, + { + "description": "Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-resizable", + "markdownDescription": "Denies the is_resizable command without any pre-configured scope." + }, + { + "description": "Denies the is_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-is-visible", + "markdownDescription": "Denies the is_visible command without any pre-configured scope." + }, + { + "description": "Denies the maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-maximize", + "markdownDescription": "Denies the maximize command without any pre-configured scope." + }, + { + "description": "Denies the minimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-minimize", + "markdownDescription": "Denies the minimize command without any pre-configured scope." + }, + { + "description": "Denies the monitor_from_point command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-monitor-from-point", + "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope." + }, + { + "description": "Denies the outer_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-position", + "markdownDescription": "Denies the outer_position command without any pre-configured scope." + }, + { + "description": "Denies the outer_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-outer-size", + "markdownDescription": "Denies the outer_size command without any pre-configured scope." + }, + { + "description": "Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-primary-monitor", + "markdownDescription": "Denies the primary_monitor command without any pre-configured scope." + }, + { + "description": "Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-request-user-attention", + "markdownDescription": "Denies the request_user_attention command without any pre-configured scope." + }, + { + "description": "Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-scale-factor", + "markdownDescription": "Denies the scale_factor command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-bottom", + "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope." + }, + { + "description": "Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-always-on-top", + "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope." + }, + { + "description": "Denies the set_background_color command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-background-color", + "markdownDescription": "Denies the set_background_color command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_count command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-count", + "markdownDescription": "Denies the set_badge_count command without any pre-configured scope." + }, + { + "description": "Denies the set_badge_label command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-badge-label", + "markdownDescription": "Denies the set_badge_label command without any pre-configured scope." + }, + { + "description": "Denies the set_closable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-closable", + "markdownDescription": "Denies the set_closable command without any pre-configured scope." + }, + { + "description": "Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-content-protected", + "markdownDescription": "Denies the set_content_protected command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-grab", + "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-icon", + "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-position", + "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope." + }, + { + "description": "Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-cursor-visible", + "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope." + }, + { + "description": "Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-decorations", + "markdownDescription": "Denies the set_decorations command without any pre-configured scope." + }, + { + "description": "Denies the set_effects command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-effects", + "markdownDescription": "Denies the set_effects command without any pre-configured scope." + }, + { + "description": "Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-enabled", + "markdownDescription": "Denies the set_enabled command without any pre-configured scope." + }, + { + "description": "Denies the set_focus command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focus", + "markdownDescription": "Denies the set_focus command without any pre-configured scope." + }, + { + "description": "Denies the set_focusable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-focusable", + "markdownDescription": "Denies the set_focusable command without any pre-configured scope." + }, + { + "description": "Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-fullscreen", + "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-icon", + "markdownDescription": "Denies the set_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-ignore-cursor-events", + "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope." + }, + { + "description": "Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-max-size", + "markdownDescription": "Denies the set_max_size command without any pre-configured scope." + }, + { + "description": "Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-maximizable", + "markdownDescription": "Denies the set_maximizable command without any pre-configured scope." + }, + { + "description": "Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-min-size", + "markdownDescription": "Denies the set_min_size command without any pre-configured scope." + }, + { + "description": "Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-minimizable", + "markdownDescription": "Denies the set_minimizable command without any pre-configured scope." + }, + { + "description": "Denies the set_overlay_icon command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-overlay-icon", + "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope." + }, + { + "description": "Denies the set_position command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-position", + "markdownDescription": "Denies the set_position command without any pre-configured scope." + }, + { + "description": "Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-progress-bar", + "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope." + }, + { + "description": "Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-resizable", + "markdownDescription": "Denies the set_resizable command without any pre-configured scope." + }, + { + "description": "Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-shadow", + "markdownDescription": "Denies the set_shadow command without any pre-configured scope." + }, + { + "description": "Denies the set_simple_fullscreen command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-simple-fullscreen", + "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope." + }, + { + "description": "Denies the set_size command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size", + "markdownDescription": "Denies the set_size command without any pre-configured scope." + }, + { + "description": "Denies the set_size_constraints command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-size-constraints", + "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope." + }, + { + "description": "Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-skip-taskbar", + "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope." + }, + { + "description": "Denies the set_theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-theme", + "markdownDescription": "Denies the set_theme command without any pre-configured scope." + }, + { + "description": "Denies the set_title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title", + "markdownDescription": "Denies the set_title command without any pre-configured scope." + }, + { + "description": "Denies the set_title_bar_style command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-title-bar-style", + "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope." + }, + { + "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-set-visible-on-all-workspaces", + "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope." + }, + { + "description": "Denies the show command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-show", + "markdownDescription": "Denies the show command without any pre-configured scope." + }, + { + "description": "Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-dragging", + "markdownDescription": "Denies the start_dragging command without any pre-configured scope." + }, + { + "description": "Denies the start_resize_dragging command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-start-resize-dragging", + "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope." + }, + { + "description": "Denies the theme command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-theme", + "markdownDescription": "Denies the theme command without any pre-configured scope." + }, + { + "description": "Denies the title command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-title", + "markdownDescription": "Denies the title command without any pre-configured scope." + }, + { + "description": "Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-toggle-maximize", + "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope." + }, + { + "description": "Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unmaximize", + "markdownDescription": "Denies the unmaximize command without any pre-configured scope." + }, + { + "description": "Denies the unminimize command without any pre-configured scope.", + "type": "string", + "const": "core:window:deny-unminimize", + "markdownDescription": "Denies the unminimize command without any pre-configured scope." + }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope." + }, + { + "description": "Enables the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope." + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope." + }, + { + "description": "Denies the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope." + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, + { + "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", + "type": "string", + "const": "shell:default", + "markdownDescription": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`" + }, + { + "description": "Enables the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-execute", + "markdownDescription": "Enables the execute command without any pre-configured scope." + }, + { + "description": "Enables the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-kill", + "markdownDescription": "Enables the kill command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-spawn", + "markdownDescription": "Enables the spawn command without any pre-configured scope." + }, + { + "description": "Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:allow-stdin-write", + "markdownDescription": "Enables the stdin_write command without any pre-configured scope." + }, + { + "description": "Denies the execute command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-execute", + "markdownDescription": "Denies the execute command without any pre-configured scope." + }, + { + "description": "Denies the kill command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-kill", + "markdownDescription": "Denies the kill command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the spawn command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-spawn", + "markdownDescription": "Denies the spawn command without any pre-configured scope." + }, + { + "description": "Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "const": "shell:deny-stdin-write", + "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + } + ] + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, + "ShellScopeEntryAllowedArg": { + "description": "A command argument allowed to be executed by the webview API.", + "anyOf": [ + { + "description": "A non-configurable argument that is passed to the command in the order it was specified.", + "type": "string" + }, + { + "description": "A variable that is set while calling the command from the webview API.", + "type": "object", + "required": [ + "validator" + ], + "properties": { + "raw": { + "description": "Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.", + "default": false, + "type": "boolean" + }, + "validator": { + "description": "[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellScopeEntryAllowedArgs": { + "description": "A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", + "anyOf": [ + { + "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", + "type": "boolean" + }, + { + "description": "A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellScopeEntryAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..a044d6c 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..217c5ce Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..589c48c Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/64x64.png b/src-tauri/icons/64x64.png new file mode 100644 index 0000000..e5ee058 Binary files /dev/null and b/src-tauri/icons/64x64.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..5cd1b2e Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..2dd49b3 Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..769609d Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..90a5a1a Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..52696bf Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..6e13203 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..5c04df3 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..a6e8533 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..c961d92 Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..d9f0e50 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..2ffbf24 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..cabc0d5 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..49821f2 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..ca0bdf6 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..29e6d74 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..a82b5b2 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..7115bea Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..cef44f6 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..24eb8bb Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..e73e645 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..a652768 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..194dc6b Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..70cb5d3 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..b6987c0 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..1901060 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..79bad62 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/values/ic_launcher_background.xml b/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 0000000..ea9c223 --- /dev/null +++ b/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #fff + \ No newline at end of file diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..949fc3d Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..7e8cfd7 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..ed3af3e Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000..ed445f0 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000..b3ec0d8 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 0000000..b3ec0d8 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 0000000..f228e2b Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 0000000..0a816f9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000..25afd94 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000..25afd94 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000..e2d6d77 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000..b3ec0d8 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000..a6f2e34 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 0000000..a6f2e34 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 0000000..ee079f9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 0000000..4f33f6f Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 0000000..ee079f9 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 0000000..82b0060 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 0000000..7a14d0d Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 0000000..57863eb Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000..a9719f7 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/src/commands/board_queries.rs b/src-tauri/src/commands/board_queries.rs new file mode 100644 index 0000000..d175556 --- /dev/null +++ b/src-tauri/src/commands/board_queries.rs @@ -0,0 +1,60 @@ +//! Board and image queries module +//! +//! Handles fetching and filtering board/image data. + +use tauri::State; + +use crate::devices::{get_block_devices as devices_get_block_devices, BlockDevice}; +use crate::images::{ + extract_images, fetch_all_images, filter_images_for_board, get_unique_boards, BoardInfo, + ImageInfo, +}; + +use super::state::AppState; + +/// Get list of available boards +#[tauri::command] +pub async fn get_boards(state: State<'_, AppState>) -> Result, String> { + // Fetch images if not cached + let mut json_guard = state.images_json.lock().await; + if json_guard.is_none() { + let json = fetch_all_images().await?; + *json_guard = Some(json); + } + + let json = json_guard.as_ref().unwrap(); + let images = extract_images(json); + Ok(get_unique_boards(&images)) +} + +/// Get images available for a specific board +#[tauri::command] +pub async fn get_images_for_board( + board_slug: String, + preapp_filter: Option, + kernel_filter: Option, + variant_filter: Option, + stable_only: bool, + state: State<'_, AppState>, +) -> Result, String> { + let json_guard = state.images_json.lock().await; + let json = json_guard + .as_ref() + .ok_or("Images not loaded. Call get_boards first.")?; + + let images = extract_images(json); + Ok(filter_images_for_board( + &images, + &board_slug, + preapp_filter.as_deref(), + kernel_filter.as_deref(), + variant_filter.as_deref(), + stable_only, + )) +} + +/// Get available block devices +#[tauri::command] +pub async fn get_block_devices() -> Result, String> { + devices_get_block_devices() +} diff --git a/src-tauri/src/commands/custom_image.rs b/src-tauri/src/commands/custom_image.rs new file mode 100644 index 0000000..14a7c10 --- /dev/null +++ b/src-tauri/src/commands/custom_image.rs @@ -0,0 +1,89 @@ +//! Custom image handling module +//! +//! Handles selection and processing of user-provided custom images. + +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use tauri::State; + +use crate::decompress::{decompress_local_file, needs_decompression}; + +use super::state::AppState; + +/// Custom image info returned when user selects a local file +#[derive(Debug, Serialize, Deserialize)] +pub struct CustomImageInfo { + pub path: String, + pub name: String, + pub size: u64, +} + +/// Check if a custom image needs decompression +#[tauri::command] +pub async fn check_needs_decompression(image_path: String) -> Result { + let path = PathBuf::from(&image_path); + Ok(needs_decompression(&path)) +} + +/// Decompress a custom image file +/// Returns the path to the decompressed file +#[tauri::command] +pub async fn decompress_custom_image( + image_path: String, + state: State<'_, AppState>, +) -> Result { + let path = PathBuf::from(&image_path); + let download_state = state.download_state.clone(); + + // Reset state for progress tracking + download_state.reset(); + + // Run decompression in a blocking task + let result = tokio::task::spawn_blocking(move || decompress_local_file(&path, &download_state)) + .await + .map_err(|e| format!("Task failed: {}", e))?; + + result.map(|p| p.to_string_lossy().to_string()) +} + +/// Select a custom image file using native file picker +#[tauri::command] +pub async fn select_custom_image( + window: tauri::Window, +) -> Result, String> { + use tauri_plugin_dialog::DialogExt; + + let file_path = window + .dialog() + .file() + .add_filter( + "Disk Images", + &["img", "iso", "xz", "gz", "zip", "zst", "bz2", "raw"], + ) + .add_filter("All Files", &["*"]) + .set_title("Select Disk Image") + .blocking_pick_file(); + + match file_path { + Some(file_path) => { + let path_buf = file_path + .as_path() + .ok_or_else(|| "Invalid path: not a valid file path".to_string())?; + let metadata = std::fs::metadata(path_buf) + .map_err(|e| format!("Failed to read file info: {}", e))?; + + let name = path_buf + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown") + .to_string(); + + Ok(Some(CustomImageInfo { + path: path_buf.to_string_lossy().to_string(), + name, + size: metadata.len(), + })) + } + None => Ok(None), + } +} diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs new file mode 100644 index 0000000..592d120 --- /dev/null +++ b/src-tauri/src/commands/mod.rs @@ -0,0 +1,13 @@ +//! Commands module +//! +//! Tauri command handlers organized by responsibility. + +pub mod board_queries; +pub mod custom_image; +pub mod operations; +pub mod progress; +pub mod scraping; +mod state; + +// Re-export state for use in main.rs +pub use state::AppState; diff --git a/src-tauri/src/commands/operations.rs b/src-tauri/src/commands/operations.rs new file mode 100644 index 0000000..4281e23 --- /dev/null +++ b/src-tauri/src/commands/operations.rs @@ -0,0 +1,68 @@ +//! Core operations module +//! +//! Handles download and flash operations. + +use std::path::PathBuf; +use tauri::State; + +use crate::config; +use crate::download::download_image as do_download; +use crate::flash::{flash_image as do_flash, request_authorization}; +use crate::utils::get_cache_dir; + +use super::state::AppState; + +/// Request write authorization before starting the flash process +/// This shows the authorization dialog (Touch ID on macOS) BEFORE downloading +/// Returns true if authorized, false if user cancelled +#[tauri::command] +pub async fn request_write_authorization(device_path: String) -> Result { + request_authorization(&device_path) +} + +/// Start downloading an image +#[tauri::command] +pub async fn download_image( + file_url: String, + state: State<'_, AppState>, +) -> Result { + let download_dir = get_cache_dir(config::app::NAME).join("images"); + + let download_state = state.download_state.clone(); + let result = do_download(&file_url, &download_dir, download_state).await?; + + Ok(result.to_string_lossy().to_string()) +} + +/// Start flashing an image to a device +#[tauri::command] +pub async fn flash_image( + image_path: String, + device_path: String, + verify: bool, + state: State<'_, AppState>, +) -> Result<(), String> { + let path = PathBuf::from(&image_path); + let flash_state = state.flash_state.clone(); + + do_flash(&path, &device_path, flash_state, verify).await +} + +/// Delete a downloaded image file +#[tauri::command] +pub async fn delete_downloaded_image(image_path: String) -> Result<(), String> { + let path = PathBuf::from(&image_path); + + // Safety check: only delete files in our cache directory + let cache_dir = get_cache_dir(config::app::NAME); + + if !path.starts_with(&cache_dir) { + return Err("Cannot delete files outside cache directory".to_string()); + } + + if path.exists() { + std::fs::remove_file(&path).map_err(|e| format!("Failed to delete image: {}", e))?; + } + + Ok(()) +} diff --git a/src-tauri/src/commands/progress.rs b/src-tauri/src/commands/progress.rs new file mode 100644 index 0000000..a41299f --- /dev/null +++ b/src-tauri/src/commands/progress.rs @@ -0,0 +1,103 @@ +//! Progress tracking module +//! +//! Handles download and flash progress reporting. + +use serde::{Deserialize, Serialize}; +use tauri::State; + +use super::state::AppState; + +/// Download progress information +#[derive(Debug, Serialize, Deserialize)] +pub struct DownloadProgress { + pub total_bytes: u64, + pub downloaded_bytes: u64, + pub is_decompressing: bool, + pub progress_percent: f64, + pub error: Option, +} + +/// Flash progress information +#[derive(Debug, Serialize, Deserialize)] +pub struct FlashProgress { + pub total_bytes: u64, + pub written_bytes: u64, + pub verified_bytes: u64, + pub is_verifying: bool, + pub progress_percent: f64, + pub error: Option, +} + +/// Get current download progress +#[tauri::command] +pub async fn get_download_progress(state: State<'_, AppState>) -> Result { + let ds = &state.download_state; + + let total = ds.total_bytes.load(std::sync::atomic::Ordering::SeqCst); + let downloaded = ds.downloaded_bytes.load(std::sync::atomic::Ordering::SeqCst); + let is_decompressing = ds.is_decompressing.load(std::sync::atomic::Ordering::SeqCst); + + let progress = if total > 0 { + (downloaded as f64 / total as f64) * 100.0 + } else { + 0.0 + }; + + let error = ds.error.lock().await.clone(); + + Ok(DownloadProgress { + total_bytes: total, + downloaded_bytes: downloaded, + is_decompressing, + progress_percent: progress, + error, + }) +} + +/// Get current flash progress +#[tauri::command] +pub async fn get_flash_progress(state: State<'_, AppState>) -> Result { + let fs = &state.flash_state; + + let total = fs.total_bytes.load(std::sync::atomic::Ordering::SeqCst); + let written = fs.written_bytes.load(std::sync::atomic::Ordering::SeqCst); + let verified = fs.verified_bytes.load(std::sync::atomic::Ordering::SeqCst); + let is_verifying = fs.is_verifying.load(std::sync::atomic::Ordering::SeqCst); + + let progress = if is_verifying { + if total > 0 { + (verified as f64 / total as f64) * 100.0 + } else { + 0.0 + } + } else if total > 0 { + (written as f64 / total as f64) * 100.0 + } else { + 0.0 + }; + + let error = fs.error.lock().await.clone(); + + Ok(FlashProgress { + total_bytes: total, + written_bytes: written, + verified_bytes: verified, + is_verifying, + progress_percent: progress, + error, + }) +} + +/// Cancel current operation +#[tauri::command] +pub async fn cancel_operation(state: State<'_, AppState>) -> Result<(), String> { + state + .download_state + .is_cancelled + .store(true, std::sync::atomic::Ordering::SeqCst); + state + .flash_state + .is_cancelled + .store(true, std::sync::atomic::Ordering::SeqCst); + Ok(()) +} diff --git a/src-tauri/src/commands/scraping.rs b/src-tauri/src/commands/scraping.rs new file mode 100644 index 0000000..4eacd57 --- /dev/null +++ b/src-tauri/src/commands/scraping.rs @@ -0,0 +1,132 @@ +//! Board image scraping module +//! +//! Handles fetching board images from armbian.com website. + +use crate::config; +use crate::utils::create_short_timeout_client; + +/// Get board image URL by scraping armbian.com +#[tauri::command] +pub async fn get_board_image_url(board_slug: String) -> Result, String> { + let url = format!("{}{}/", config::urls::BOARD_IMAGES_BASE, board_slug); + + let client = create_short_timeout_client()?; + let response = client.get(&url).send().await; + + match response { + Ok(resp) if resp.status().is_success() => { + let html = resp + .text() + .await + .map_err(|e| format!("Failed to read response: {}", e))?; + + // Method 1: Extract from JSON-LD schema (most reliable) + if let Some(image_url) = extract_image_from_json_ld(&html) { + return Ok(Some(image_url)); + } + + // Method 2: Parse HTML to find og:image meta tag + let document = scraper::Html::parse_document(&html); + let og_selector = scraper::Selector::parse("meta[property='og:image']").ok(); + if let Some(sel) = og_selector { + if let Some(meta) = document.select(&sel).next() { + if let Some(content) = meta.value().attr("content") { + if content.contains("cdn.armbian.com") { + return Ok(Some(content.to_string())); + } + } + } + } + + // Method 3: Find img with cdn.armbian.com src + let img_selector = scraper::Selector::parse("img[src*='cdn.armbian.com']").ok(); + if let Some(sel) = img_selector { + if let Some(img) = document.select(&sel).next() { + if let Some(src) = img.value().attr("src") { + return Ok(Some(src.to_string())); + } + } + } + + // Method 4: Find any image in article/content area + let content_selector = + scraper::Selector::parse("article img, .entry-content img, .post-content img") + .ok(); + if let Some(sel) = content_selector { + if let Some(img) = document.select(&sel).next() { + if let Some(src) = img.value().attr("src") { + if src.contains("cdn.armbian.com") || src.contains("wp-content") { + return Ok(Some(src.to_string())); + } + } + } + } + + Ok(None) + } + _ => Ok(None), + } +} + +/// Extract image URL from JSON-LD schema in HTML +fn extract_image_from_json_ld(html: &str) -> Option { + let document = scraper::Html::parse_document(html); + let selector = scraper::Selector::parse("script[type='application/ld+json']").ok()?; + + for script in document.select(&selector) { + let json_text = script.inner_html(); + if let Ok(json) = serde_json::from_str::(&json_text) { + // Check for primaryImageOfPage + if let Some(img) = json.get("primaryImageOfPage") { + if let Some(url) = img.get("url").and_then(|u| u.as_str()) { + if url.contains("cdn.armbian.com") { + return Some(url.to_string()); + } + } + } + // Check for image field directly + if let Some(img) = json.get("image") { + if let Some(url) = img.as_str() { + if url.contains("cdn.armbian.com") { + return Some(url.to_string()); + } + } + if let Some(url) = img.get("url").and_then(|u| u.as_str()) { + if url.contains("cdn.armbian.com") { + return Some(url.to_string()); + } + } + } + // Check @graph array + if let Some(graph) = json.get("@graph").and_then(|g| g.as_array()) { + for item in graph { + if let Some(img) = item.get("primaryImageOfPage") { + if let Some(id) = img.get("@id").and_then(|i| i.as_str()) { + // Find the ImageObject with this id + for inner_item in graph { + if inner_item.get("@id").and_then(|i| i.as_str()) == Some(id) { + if let Some(url) = + inner_item.get("url").and_then(|u| u.as_str()) + { + if url.contains("cdn.armbian.com") { + return Some(url.to_string()); + } + } + } + } + } + } + // Direct image in graph item + if item.get("@type").and_then(|t| t.as_str()) == Some("ImageObject") { + if let Some(url) = item.get("url").and_then(|u| u.as_str()) { + if url.contains("cdn.armbian.com") { + return Some(url.to_string()); + } + } + } + } + } + } + } + None +} diff --git a/src-tauri/src/commands/state.rs b/src-tauri/src/commands/state.rs new file mode 100644 index 0000000..7daa688 --- /dev/null +++ b/src-tauri/src/commands/state.rs @@ -0,0 +1,26 @@ +//! Application state module +//! +//! Defines the shared application state used across commands. + +use std::sync::Arc; +use tokio::sync::Mutex; + +use crate::download::DownloadState; +use crate::flash::FlashState; + +/// Application state shared across all commands +pub struct AppState { + pub images_json: Mutex>, + pub download_state: Arc, + pub flash_state: Arc, +} + +impl Default for AppState { + fn default() -> Self { + Self { + images_json: Mutex::new(None), + download_state: Arc::new(DownloadState::new()), + flash_state: Arc::new(FlashState::new()), + } + } +} diff --git a/src-tauri/src/config/mod.rs b/src-tauri/src/config/mod.rs new file mode 100644 index 0000000..bbbd102 --- /dev/null +++ b/src-tauri/src/config/mod.rs @@ -0,0 +1,87 @@ +//! Application configuration and constants +//! +//! Centralizes all hard-coded values, URLs, and configuration options. +//! Some constants are defined for future use and consistency. + +#![allow(dead_code)] + +/// Application metadata +pub mod app { + /// Application name used for cache directories + pub const NAME: &str = "armbian-imager"; + + /// Application display name + pub const DISPLAY_NAME: &str = "Armbian Imager"; + + /// User agent for HTTP requests + pub const USER_AGENT: &str = "Armbian-Imager/1.0"; +} + +/// API endpoints and URLs +pub mod urls { + /// Armbian all-images JSON endpoint + pub const ALL_IMAGES: &str = "https://github.armbian.com/all-images.json"; + + /// Base URL for board images + pub const BOARD_IMAGES_BASE: &str = "https://www.armbian.com/"; +} + +/// Download and decompression settings +pub mod download { + /// Download buffer size (1 MB) + pub const BUFFER_SIZE: usize = 1024 * 1024; + + /// Decompression buffer size (8 MB) + pub const DECOMPRESS_BUFFER_SIZE: usize = 8 * 1024 * 1024; + + /// Chunk size for streaming writes (4 MB) + pub const CHUNK_SIZE: usize = 4 * 1024 * 1024; +} + +/// Flash operation settings +pub mod flash { + /// Write chunk size (4 MB) + pub const CHUNK_SIZE: usize = 4 * 1024 * 1024; + + /// Quick erase size - zeros written before flashing (10 MB) + pub const QUICK_ERASE_SIZE: usize = 10 * 1024 * 1024; + + /// Erase chunk size (1 MB) + pub const ERASE_CHUNK_SIZE: usize = 1024 * 1024; + + /// Progress log interval (percentage points) + pub const LOG_INTERVAL_PERCENT: u64 = 6; +} + +/// Device detection settings +pub mod devices { + /// Minimum device size to show (1 GB) - filters out small partitions + pub const MIN_SIZE_BYTES: u64 = 1024 * 1024 * 1024; + + /// Maximum device size for removable media (2 TB) + pub const MAX_SIZE_BYTES: u64 = 2 * 1024 * 1024 * 1024 * 1024; +} + +/// HTTP client settings +pub mod http { + /// Connection timeout in seconds + pub const CONNECT_TIMEOUT_SECS: u64 = 30; + + /// Request timeout in seconds + pub const REQUEST_TIMEOUT_SECS: u64 = 300; + + /// Short timeout for quick requests like board info (10 seconds) + pub const SHORT_TIMEOUT_SECS: u64 = 10; +} + +/// Image filtering constants +pub mod images { + /// Filter value for empty preinstalled application + pub const EMPTY_FILTER: &str = "__EMPTY__"; + + /// Stable repository identifier + pub const STABLE_REPO: &str = "archive"; + + /// Temporary download file suffix + pub const DOWNLOAD_SUFFIX: &str = ".downloading"; +} diff --git a/src-tauri/src/data/board_names.json b/src-tauri/src/data/board_names.json new file mode 100644 index 0000000..afbbd53 --- /dev/null +++ b/src-tauri/src/data/board_names.json @@ -0,0 +1,314 @@ +{ + "9tripod-x3568-v4": "9Tripod X3568 V4", + "aml-a311d-cc": "Amlogic A311D-CC", + "aml-c400-plus": "Amlogic C400 Plus", + "aml-s805-mxq": "Amlogic S805 MXQ", + "aml-s9xx-box": "Amlogic S9XX Box", + "aml-t95z-plus": "Amlogic T95Z Plus", + "armsom-aim7-io": "ArmSom AIM7-IO", + "armsom-cm5-io": "ArmSom CM5-IO", + "armsom-cm5-rpi-cm4-io": "ArmSom CM5 RPi CM4-IO", + "armsom-forge1": "ArmSom Forge1", + "armsom-sige1": "ArmSom Sige1", + "armsom-sige3": "ArmSom Sige3", + "armsom-sige5": "ArmSom Sige5", + "armsom-sige7": "ArmSom Sige7", + "armsom-w3": "ArmSom W3", + "avaota-a1": "Avaota A1", + "bananapi": "Banana Pi", + "bananapicm4io": "Banana Pi CM4-IO", + "bananapif3": "Banana Pi F3", + "bananapim1plus": "Banana Pi M1 Plus", + "bananapim2plus": "Banana Pi M2 Plus", + "bananapim2pro": "Banana Pi M2 Pro", + "bananapim2s": "Banana Pi M2S", + "bananapim2ultra": "Banana Pi M2 Ultra", + "bananapim2zero": "Banana Pi M2 Zero", + "bananapim3": "Banana Pi M3", + "bananapim4berry": "Banana Pi M4 Berry", + "bananapim4zero": "Banana Pi M4 Zero", + "bananapim5": "Banana Pi M5", + "bananapim5pro": "Banana Pi M5 Pro", + "bananapim64": "Banana Pi M64", + "bananapim7": "Banana Pi M7", + "bananapipro": "Banana Pi Pro", + "bananapir2": "Banana Pi R2", + "bananapir2pro": "Banana Pi R2 Pro", + "bananapir4": "Banana Pi R4", + "beaglebone-ai64": "BeagleBone AI-64", + "beagleplay": "BeaglePlay", + "beagley-ai": "BeagleY-AI", + "beelinkx2": "Beelink X2", + "bestv-r3300-l": "BesTV R3300-L", + "bigtreetech-cb1": "BigTreeTech CB1", + "bigtreetech-cb2": "BigTreeTech CB2", + "cainiao-cniot-core": "Cainiao CNIOT Core", + "cherryba-m1": "CherryBa M1", + "clearfogbase": "ClearFog Base", + "clearfogpro": "ClearFog Pro", + "clockworkpi-a06": "ClockworkPi A06", + "cm3588-nas": "CM3588 NAS", + "coolpi-cm5": "CoolPi CM5", + "coolpi-genbook": "CoolPi GenBook", + "cubieboard": "Cubieboard", + "cubieboard2": "Cubieboard 2", + "cubietruck": "Cubietruck", + "cubox-i": "CuBox-i", + "cyber-aib-rk3588": "Cyber AIB RK3588", + "dshanpi-a1": "DShanPi A1", + "dshanpi-r1": "DShanPi R1", + "dusun-dsom-010r": "Dusun DSOM-010R", + "espressobin": "EspressoBin", + "fine3399": "Fine3399", + "firefly-itx-3588j": "Firefly ITX-3588J", + "firefly-rk3399": "Firefly RK3399", + "fxblox-rk1": "FxBlox RK1", + "gateway-gz80x": "Gateway GZ80X", + "h96-tvbox-3566": "H96 TV Box 3566", + "helios4": "Helios4", + "helios64": "Helios64", + "hikey960": "HiKey 960", + "hinlink-h28k": "HinLink H28K", + "hinlink-h66k": "HinLink H66K", + "hinlink-h68k": "HinLink H68K", + "hinlink-h88k": "HinLink H88K", + "hinlink-hnas": "HinLink HNAS", + "hinlink-ht2": "HinLink HT2", + "imb3588": "IMB3588", + "indiedroid-nova": "Indiedroid Nova", + "inovato-quadra": "Inovato Quadra", + "jethubj100": "JetHub J100", + "jethubj200": "JetHub J200", + "jethubj80": "JetHub J80", + "jetson-nano": "Jetson Nano", + "jp-tvbox-3566": "JP TV Box 3566", + "khadas-edge": "Khadas Edge", + "khadas-edge2": "Khadas Edge2", + "khadas-vim1": "Khadas VIM1", + "khadas-vim1s": "Khadas VIM1S", + "khadas-vim2": "Khadas VIM2", + "khadas-vim3": "Khadas VIM3", + "khadas-vim3l": "Khadas VIM3L", + "khadas-vim4": "Khadas VIM4", + "kickpik2b": "KickPi K2B", + "lafrite": "La Frite", + "lckfb-taishanpi": "LCKFB TaiShanPi", + "leez-p710": "Leez P710", + "lepotato": "Le Potato", + "lime": "Lime", + "lime-a33": "Lime A33", + "lime-a64": "Lime A64", + "lime2": "Lime2", + "longanpi-3h": "LonganPi 3H", + "longanpi-4b": "LonganPi 4B", + "lubancat2": "LubanCat2", + "luckfox-core3566": "LuckFox Core3566", + "luckfox-lyra-plus": "LuckFox Lyra Plus", + "luckfox-lyra-ultra-w": "LuckFox Lyra Ultra W", + "luckfox-lyra-zero-w": "LuckFox Lyra Zero W", + "luckfox-pico-max": "LuckFox Pico Max", + "luckfox-pico-mini": "LuckFox Pico Mini", + "mangopi-m28k": "MangoPi M28K", + "mba8mpxl": "MBA8MPXL", + "mba8mpxl-ras314": "MBA8MPXL RAS314", + "mekotronics-r58-4x4": "Mekotronics R58 4x4", + "mekotronics-r58-minipc": "Mekotronics R58 MiniPC", + "mekotronics-r58hd": "Mekotronics R58HD", + "mekotronics-r58x": "Mekotronics R58X", + "mekotronics-r58x-4g": "Mekotronics R58X 4G", + "mekotronics-r58x-pro": "Mekotronics R58X Pro", + "melea1000": "Melea1000", + "mixtile-blade3": "Mixtile Blade 3", + "mixtile-edge2": "Mixtile Edge2", + "mk808c": "MK808C", + "mksklipad50": "MKS KLIPAD50", + "mkspi": "MKSPI", + "nanopct4": "NanoPC-T4", + "nanopct6": "NanoPC-T6", + "nanopct6-lts": "NanoPC-T6 LTS", + "nanopi-m5": "NanoPi M5", + "nanopi-m6": "NanoPi M6", + "nanopi-r1": "NanoPi R1", + "nanopi-r1s-h5": "NanoPi R1S-H5", + "nanopi-r2c": "NanoPi R2C", + "nanopi-r2s": "NanoPi R2S", + "nanopi-r2s-plus": "NanoPi R2S Plus", + "nanopi-r3s": "NanoPi R3S", + "nanopi-r3s-lts": "NanoPi R3S LTS", + "nanopi-r4s": "NanoPi R4S", + "nanopi-r4se": "NanoPi R4SE", + "nanopi-r5c": "NanoPi R5C", + "nanopi-r5s": "NanoPi R5S", + "nanopi-r6c": "NanoPi R6C", + "nanopi-r6s": "NanoPi R6S", + "nanopi-r76s": "NanoPi R76S", + "nanopia64": "NanoPi A64", + "nanopiair": "NanoPi Air", + "nanopiduo": "NanoPi Duo", + "nanopiduo2": "NanoPi Duo2", + "nanopik1plus": "NanoPi K1 Plus", + "nanopik2-s905": "NanoPi K2-S905", + "nanopim4": "NanoPi M4", + "nanopim4v2": "NanoPi M4V2", + "nanopineo": "NanoPi NEO", + "nanopineo2": "NanoPi NEO2", + "nanopineo2black": "NanoPi NEO2 Black", + "nanopineo3": "NanoPi NEO3", + "nanopineo4": "NanoPi NEO4", + "nanopineocore2": "NanoPi NEO Core2", + "nanopineoplus2": "NanoPi NEO Plus2", + "odroidc1": "ODROID-C1", + "odroidc2": "ODROID-C2", + "odroidc4": "ODROID-C4", + "odroidhc4": "ODROID-HC4", + "odroidm1": "ODROID-M1", + "odroidm1s": "ODROID-M1S", + "odroidm2": "ODROID-M2", + "odroidn2": "ODROID-N2", + "odroidn2l": "ODROID-N2L", + "odroidxu4": "ODROID-XU4", + "olimex-a20-olinuxino-micro": "Olimex A20 OLinuXino Micro", + "olimex-teres-a64": "Olimex TERES-A64", + "olinux-som-a13": "OLinux SoM A13", + "onecloud": "OneCloud", + "oneplus-kebab": "OnePlus 8T (Kebab)", + "orangepi-r1": "Orange Pi R1", + "orangepi-r1plus": "Orange Pi R1 Plus", + "orangepi-r1plus-lts": "Orange Pi R1 Plus LTS", + "orangepi-rk3399": "Orange Pi RK3399", + "orangepi2": "Orange Pi 2", + "orangepi3": "Orange Pi 3", + "orangepi3-lts": "Orange Pi 3 LTS", + "orangepi3b": "Orange Pi 3B", + "orangepi4": "Orange Pi 4", + "orangepi4-lts": "Orange Pi 4 LTS", + "orangepi5": "Orange Pi 5", + "orangepi5-max": "Orange Pi 5 Max", + "orangepi5-plus": "Orange Pi 5 Plus", + "orangepi5-ultra": "Orange Pi 5 Ultra", + "orangepi5b": "Orange Pi 5B", + "orangepi5pro": "Orange Pi 5 Pro", + "orangepilite": "Orange Pi Lite", + "orangepilite2": "Orange Pi Lite 2", + "orangepione": "Orange Pi One", + "orangepioneplus": "Orange Pi One Plus", + "orangepipc": "Orange Pi PC", + "orangepipc2": "Orange Pi PC2", + "orangepipcplus": "Orange Pi PC Plus", + "orangepiplus": "Orange Pi Plus", + "orangepiplus2e": "Orange Pi Plus 2E", + "orangepiprime": "Orange Pi Prime", + "orangepiwin": "Orange Pi Win", + "orangepizero": "Orange Pi Zero", + "orangepizero2": "Orange Pi Zero 2", + "orangepizero2w": "Orange Pi Zero 2W", + "orangepizero3": "Orange Pi Zero 3", + "orangepizeroplus": "Orange Pi Zero Plus", + "orangepizeroplus2-h3": "Orange Pi Zero Plus 2 H3", + "orangepizeroplus2-h5": "Orange Pi Zero Plus 2 H5", + "panther-x2": "Panther X2", + "pcduino3": "pcDuino3", + "phytiumpi": "PhytiumPi", + "pine64": "Pine64", + "pine64so": "Pine64-SO", + "pinebook-a64": "Pinebook A64", + "pinebook-pro": "Pinebook Pro", + "pinecube": "PineCube", + "pineh64": "Pine H64", + "pineh64-b": "Pine H64 Model B", + "pocketbeagle2": "PocketBeagle 2", + "pocketchip-sd": "PocketCHIP SD", + "qemu-uboot-arm64": "QEMU U-Boot ARM64", + "qemu-uboot-x86": "QEMU U-Boot x86", + "qemu-uefi-x86": "QEMU UEFI x86", + "quartz64a": "Quartz64 Model A", + "quartz64b": "Quartz64 Model B", + "radxa-cm4-io": "Radxa CM4-IO", + "radxa-cm5-io": "Radxa CM5-IO", + "radxa-cubie-a5e": "Radxa Cubie A5E", + "radxa-dragon-q6a": "Radxa Dragon Q6A", + "radxa-e20c": "Radxa E20C", + "radxa-e25": "Radxa E25", + "radxa-e52c": "Radxa E52C", + "radxa-e54c": "Radxa E54C", + "radxa-nio-12l": "Radxa NIO 12L", + "radxa-rock-4d": "Radxa Rock 4D", + "radxa-zero": "Radxa Zero", + "radxa-zero2": "Radxa Zero 2", + "radxa-zero3": "Radxa Zero 3", + "recore": "Recore", + "renegade": "Renegade", + "retro-lite-cm5": "Retro Lite CM5", + "retroidpocket-rp5": "Retroid Pocket RP5", + "retroidpocket-rpmini": "Retroid Pocket Mini", + "rk322x-box": "RK322X Box", + "rk3318-box": "RK3318 Box", + "rk3328-heltec": "RK3328 Heltec", + "roc-rk3399-pc": "ROC-RK3399-PC", + "rock-2a": "Rock 2A", + "rock-2f": "Rock 2F", + "rock-3a": "Rock 3A", + "rock-3c": "Rock 3C", + "rock-4se": "Rock 4SE", + "rock-5-cm-rpi-cm4-io": "Rock 5 CM RPi CM4-IO", + "rock-5-cmio": "Rock 5 CMIO", + "rock-5-itx": "Rock 5 ITX", + "rock-5a": "Rock 5A", + "rock-5b": "Rock 5B", + "rock-5b-plus": "Rock 5B Plus", + "rock-5c": "Rock 5C", + "rock-5t": "Rock 5T", + "rock-s0": "Rock S0", + "rock64": "Rock64", + "rockpi-4a": "Rock Pi 4A", + "rockpi-4b": "Rock Pi 4B", + "rockpi-4bplus": "Rock Pi 4B Plus", + "rockpi-4c": "Rock Pi 4C", + "rockpi-4cplus": "Rock Pi 4C Plus", + "rockpi-e": "Rock Pi E", + "rockpi-n10": "Rock Pi N10", + "rockpi-s": "Rock Pi S", + "rockpro64": "RockPro64", + "rpi4b": "Raspberry Pi 4B", + "sakurapi-rk3308b": "SakuraPi RK3308B", + "sk-am62b": "SK-AM62B", + "sk-am62p": "SK-AM62P", + "sk-am64b": "SK-AM64B", + "sk-am68": "SK-AM68", + "sk-am69": "SK-AM69", + "sk-tda4vm": "SK-TDA4VM", + "smart-am40": "Smart AM40", + "station-m1": "Station M1", + "station-m2": "Station M2", + "station-m3": "Station M3", + "station-p1": "Station P1", + "station-p2": "Station P2", + "sunvell-r69": "Sunvell R69", + "sweet-potato": "Sweet Potato", + "tanix-tx6": "Tanix TX6", + "thinkpad-x13s": "ThinkPad X13s", + "tinker-edge-r": "Tinker Edge R", + "tinkerboard": "Tinker Board", + "tinkerboard-2": "Tinker Board 2", + "tmds62levm": "TMDS62LEVM", + "tritium-h3": "Tritium H3", + "tritium-h5": "Tritium H5", + "turing-rk1": "Turing RK1", + "udoo": "UDOO", + "uefi-arm64": "UEFI ARM64", + "uefi-loong64": "UEFI LoongArch64", + "uefi-riscv64": "UEFI RISC-V64", + "uefi-x86": "UEFI x86", + "visionfive2": "VisionFive 2", + "x96-mate": "X96 Mate", + "x96q": "X96Q", + "xiaobao-nas": "XiaoBao NAS", + "xiaomi-elish": "Xiaomi Pad 5 Pro (Elish)", + "xpressreal-t3": "XPressReal T3", + "xt-q8l-v10": "XT-Q8L-V10", + "youyeetoo-r1-v3": "Youyeetoo R1 V3", + "youyeetoo-yy3588": "Youyeetoo YY3588", + "yy3568": "YY3568", + "z28pro": "Z28 Pro", + "zeropi": "ZeroPi" +} diff --git a/src-tauri/src/decompress.rs b/src-tauri/src/decompress.rs new file mode 100644 index 0000000..f0c9401 --- /dev/null +++ b/src-tauri/src/decompress.rs @@ -0,0 +1,192 @@ +//! Decompression module +//! +//! Handles decompressing compressed image files (XZ, GZ, BZ2, ZST) +//! using system tools or fallback Rust libraries. + +use std::fs::File; +use std::io::{BufReader, BufWriter, Read, Write}; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use xz2::bufread::XzDecoder; + +use crate::config; +use crate::download::DownloadState; +use crate::utils::{find_binary, get_recommended_threads}; +use crate::{log_error, log_info, log_warn}; + +const MODULE: &str = "decompress"; + +/// Check if a file needs decompression based on extension +pub fn needs_decompression(path: &Path) -> bool { + let ext = path.extension().and_then(|e| e.to_str()).unwrap_or(""); + matches!(ext.to_lowercase().as_str(), "xz" | "gz" | "bz2" | "zst") +} + +/// Decompress using system xz command (much faster, uses multiple threads) +pub fn decompress_with_system_xz(input_path: &Path, output_path: &Path) -> Result<(), String> { + use std::io::copy; + use std::process::Stdio; + + let xz_path = find_binary("xz").ok_or("xz command not found")?; + let threads = get_recommended_threads(); + + log_info!( + MODULE, + "Using system xz at: {} with {} threads", + xz_path.display(), + threads + ); + + let mut child = Command::new(&xz_path) + .args(["-d", "-k", "-c"]) // decompress, keep original, output to stdout + .arg(format!("-T{}", threads)) + .arg(input_path) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .map_err(|e| { + log_error!(MODULE, "Failed to spawn xz: {}", e); + format!("Failed to spawn xz: {}", e) + })?; + + let mut stdout = child.stdout.take().ok_or("Failed to capture xz stdout")?; + + let mut output_file = + File::create(output_path).map_err(|e| format!("Failed to create output file: {}", e))?; + + copy(&mut stdout, &mut output_file) + .map_err(|e| format!("Failed to write decompressed data: {}", e))?; + + let status = child + .wait() + .map_err(|e| format!("Failed to wait for xz: {}", e))?; + + if status.success() { + log_info!(MODULE, "System xz decompression complete"); + Ok(()) + } else { + let _ = std::fs::remove_file(output_path); + log_error!(MODULE, "xz decompression failed"); + Err("xz decompression failed".to_string()) + } +} + +/// Decompress using Rust xz2 library (slower, single-threaded fallback) +pub fn decompress_with_rust_library( + input_path: &Path, + output_path: &Path, + state: &Arc, +) -> Result<(), String> { + let temp_file = + File::open(input_path).map_err(|e| format!("Failed to open temp file: {}", e))?; + + let buf_reader = BufReader::with_capacity(config::download::DECOMPRESS_BUFFER_SIZE, temp_file); + let mut decoder = XzDecoder::new(buf_reader); + + let output_file = + File::create(output_path).map_err(|e| format!("Failed to create output file: {}", e))?; + + let mut buf_writer = + BufWriter::with_capacity(config::download::DECOMPRESS_BUFFER_SIZE, output_file); + let mut buffer = vec![0u8; config::download::CHUNK_SIZE]; + + loop { + if state.is_cancelled.load(Ordering::SeqCst) { + drop(buf_writer); + let _ = std::fs::remove_file(output_path); + return Err("Decompression cancelled".to_string()); + } + + let bytes_read = decoder + .read(&mut buffer) + .map_err(|e| format!("Decompression error: {}", e))?; + + if bytes_read == 0 { + break; + } + + buf_writer + .write_all(&buffer[..bytes_read]) + .map_err(|e| format!("Failed to write decompressed data: {}", e))?; + } + + buf_writer + .flush() + .map_err(|e| format!("Failed to flush output: {}", e))?; + + Ok(()) +} + +/// Decompress a local file (for custom images) +/// Returns the path to the decompressed file +pub fn decompress_local_file( + input_path: &PathBuf, + state: &Arc, +) -> Result { + let filename = input_path + .file_name() + .and_then(|n| n.to_str()) + .ok_or("Invalid filename")?; + + // Determine output filename (remove compression extension) + let output_filename = filename + .trim_end_matches(".xz") + .trim_end_matches(".gz") + .trim_end_matches(".bz2") + .trim_end_matches(".zst"); + + // Output to same directory as input + let output_path = input_path + .parent() + .ok_or("Invalid input path")? + .join(output_filename); + + // Check if already decompressed + if output_path.exists() { + log_info!( + MODULE, + "Decompressed file already exists: {}", + output_path.display() + ); + return Ok(output_path); + } + + state.is_decompressing.store(true, Ordering::SeqCst); + + // Get input file size for progress indication + if let Ok(metadata) = std::fs::metadata(input_path) { + state.total_bytes.store(metadata.len(), Ordering::SeqCst); + } + + log_info!( + MODULE, + "Decompressing custom image: {} -> {}", + input_path.display(), + output_path.display() + ); + + // Only handle .xz for now (most common for Armbian) + if filename.ends_with(".xz") { + // Try system xz first, fall back to Rust library + if let Err(e) = decompress_with_system_xz(input_path, &output_path) { + log_warn!( + MODULE, + "System xz failed: {}, falling back to Rust library (slower)", + e + ); + decompress_with_rust_library(input_path, &output_path, state)?; + } + } else { + return Err(format!( + "Unsupported compression format for: {}", + filename + )); + } + + state.is_decompressing.store(false, Ordering::SeqCst); + log_info!(MODULE, "Decompression complete: {}", output_path.display()); + + Ok(output_path) +} diff --git a/src-tauri/src/devices/linux.rs b/src-tauri/src/devices/linux.rs new file mode 100644 index 0000000..4cf327b --- /dev/null +++ b/src-tauri/src/devices/linux.rs @@ -0,0 +1,122 @@ +//! Linux device detection +//! +//! Uses lsblk to enumerate block devices. + +use std::process::Command; + +use crate::utils::format_size; + +use super::types::BlockDevice; + +/// Get list of block devices on Linux +pub fn get_block_devices() -> Result, String> { + let output = Command::new("lsblk") + .args(["-dpno", "NAME,SIZE,MODEL,RM", "-b"]) + .output() + .map_err(|e| format!("Failed to run lsblk: {}", e))?; + + if !output.status.success() { + return Err("lsblk command failed".to_string()); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + let mut devices = Vec::new(); + let system_disks = get_system_disks(); + + for line in stdout.lines() { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() < 2 { + continue; + } + + let path = parts[0]; + + // Skip non-standard devices + if !path.starts_with("/dev/sd") + && !path.starts_with("/dev/hd") + && !path.starts_with("/dev/vd") + && !path.starts_with("/dev/nvme") + && !path.starts_with("/dev/mmcblk") + { + continue; + } + + // Skip mmcblk boot/rpmb partitions + if path.contains("boot") || path.contains("rpmb") { + continue; + } + + // Skip system disks + let dev_name = path.split('/').last().unwrap_or(""); + let is_system = system_disks.iter().any(|sys| { + sys.starts_with(dev_name) || dev_name.starts_with(sys) + }); + + if is_system { + continue; + } + + let size: u64 = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0); + if size == 0 { + continue; + } + + let model = if parts.len() > 2 { + if parts.len() > 3 { + parts[2..parts.len() - 1].join(" ") + } else { + parts[2].to_string() + } + } else { + String::new() + }; + + let is_removable = parts.last().map(|s| *s == "1").unwrap_or(false); + + devices.push(BlockDevice { + path: path.to_string(), + name: dev_name.to_string(), + size, + size_formatted: format_size(size), + model, + is_removable, + is_system: false, + }); + } + + Ok(devices) +} + +/// Get list of system disk names to exclude +fn get_system_disks() -> Vec { + let mut system_disks = Vec::new(); + + for mount in &["/", "/boot", "/boot/efi"] { + if let Ok(output) = Command::new("findmnt") + .args(["-n", "-o", "SOURCE", mount]) + .output() + { + let source = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if !source.is_empty() { + if let Ok(pkname_output) = Command::new("lsblk") + .args(["-no", "PKNAME", &source]) + .output() + { + let pkname = String::from_utf8_lossy(&pkname_output.stdout) + .trim() + .to_string(); + if !pkname.is_empty() { + system_disks.push(pkname); + } + } + if let Some(name) = source.split('/').last() { + system_disks.push(name.to_string()); + } + } + } + } + + system_disks.sort(); + system_disks.dedup(); + system_disks +} diff --git a/src-tauri/src/devices/macos.rs b/src-tauri/src/devices/macos.rs new file mode 100644 index 0000000..221a767 --- /dev/null +++ b/src-tauri/src/devices/macos.rs @@ -0,0 +1,144 @@ +//! macOS device detection +//! +//! Uses diskutil to enumerate block devices. + +use std::process::Command; + +use crate::utils::format_size; + +use super::types::BlockDevice; + +/// Get list of block devices on macOS +pub fn get_block_devices() -> Result, String> { + let output = Command::new("diskutil") + .args(["list", "-plist", "external", "physical"]) + .output() + .map_err(|e| format!("Failed to run diskutil: {}", e))?; + + if !output.status.success() { + // Try without external flag for older macOS + let output = Command::new("diskutil") + .args(["list", "-plist"]) + .output() + .map_err(|e| format!("Failed to run diskutil: {}", e))?; + + return parse_diskutil(&output.stdout); + } + + parse_diskutil(&output.stdout) +} + +/// Parse diskutil output to extract devices +fn parse_diskutil(_plist_data: &[u8]) -> Result, String> { + let mut devices = Vec::new(); + + let output = Command::new("diskutil") + .args(["list"]) + .output() + .map_err(|e| format!("Failed to run diskutil list: {}", e))?; + + let list_output = String::from_utf8_lossy(&output.stdout); + let system_disk = get_system_disk(); + + for line in list_output.lines() { + if !line.starts_with("/dev/disk") { + continue; + } + + // Skip synthesized disks (APFS containers) + if line.contains("synthesized") { + continue; + } + + // Skip disk images + if line.contains("disk image") { + continue; + } + + let disk_path = line.split_whitespace().next().unwrap_or(""); + let disk_name = disk_path.trim_end_matches(':'); + + if let Ok(mut info) = get_disk_info(disk_name) { + // Mark as system disk if it contains the system + if let Some(ref sys_disk) = system_disk { + if disk_name.contains(sys_disk) || disk_name == "/dev/disk0" { + info.is_system = true; + } + } + + // Mark as system if internal and not removable + let is_internal = line.contains("(internal"); + if is_internal && !info.is_removable { + info.is_system = true; + } + + if info.size > 0 { + devices.push(info); + } + } + } + + Ok(devices) +} + +/// Get the system disk identifier +fn get_system_disk() -> Option { + let output = Command::new("diskutil") + .args(["info", "/"]) + .output() + .ok()?; + + let info = String::from_utf8_lossy(&output.stdout); + for line in info.lines() { + if line.contains("Part of Whole:") { + return line.split(':').nth(1).map(|s| s.trim().to_string()); + } + } + None +} + +/// Get detailed info for a specific disk +fn get_disk_info(disk_path: &str) -> Result { + let output = Command::new("diskutil") + .args(["info", disk_path]) + .output() + .map_err(|e| format!("Failed to get disk info: {}", e))?; + + let info = String::from_utf8_lossy(&output.stdout); + + let mut size: u64 = 0; + let mut model = String::new(); + let mut is_removable = true; + let mut is_internal = false; + + for line in info.lines() { + let line = line.trim(); + if line.starts_with("Disk Size:") { + if let Some(bytes_part) = line.split('(').nth(1) { + if let Some(bytes_str) = bytes_part.split_whitespace().next() { + size = bytes_str.parse().unwrap_or(0); + } + } + } else if line.starts_with("Device / Media Name:") { + model = line + .split(':') + .nth(1) + .map(|s| s.trim().to_string()) + .unwrap_or_default(); + } else if line.starts_with("Removable Media:") { + is_removable = line.contains("Removable"); + } else if line.starts_with("Device Location:") { + is_internal = line.contains("Internal"); + } + } + + Ok(BlockDevice { + path: disk_path.to_string(), + name: disk_path.split('/').last().unwrap_or(disk_path).to_string(), + size, + size_formatted: format_size(size), + model, + is_removable, + is_system: is_internal && !is_removable, + }) +} diff --git a/src-tauri/src/devices/mod.rs b/src-tauri/src/devices/mod.rs new file mode 100644 index 0000000..a48b53a --- /dev/null +++ b/src-tauri/src/devices/mod.rs @@ -0,0 +1,27 @@ +//! Block device detection module +//! +//! Platform-specific implementations for detecting available storage devices. + +mod types; + +#[cfg(target_os = "macos")] +mod macos; + +#[cfg(target_os = "linux")] +mod linux; + +#[cfg(target_os = "windows")] +mod windows; + +// Re-export types +pub use types::BlockDevice; + +// Re-export platform-specific implementation +#[cfg(target_os = "macos")] +pub use macos::get_block_devices; + +#[cfg(target_os = "linux")] +pub use linux::get_block_devices; + +#[cfg(target_os = "windows")] +pub use windows::get_block_devices; diff --git a/src-tauri/src/devices/types.rs b/src-tauri/src/devices/types.rs new file mode 100644 index 0000000..d868aa5 --- /dev/null +++ b/src-tauri/src/devices/types.rs @@ -0,0 +1,24 @@ +//! Device types module +//! +//! Common types for block device representation. + +use serde::{Deserialize, Serialize}; + +/// Represents a block device (disk) on the system +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BlockDevice { + /// Device path (e.g., /dev/sda, /dev/disk2, \\.\PhysicalDrive1) + pub path: String, + /// Device name (e.g., sda, disk2) + pub name: String, + /// Size in bytes + pub size: u64, + /// Human-readable size (e.g., "32 GB") + pub size_formatted: String, + /// Device model/name + pub model: String, + /// Whether the device is removable (USB, SD card) + pub is_removable: bool, + /// Whether this is a system disk (contains OS) + pub is_system: bool, +} diff --git a/src-tauri/src/devices/windows.rs b/src-tauri/src/devices/windows.rs new file mode 100644 index 0000000..20f09b4 --- /dev/null +++ b/src-tauri/src/devices/windows.rs @@ -0,0 +1,94 @@ +//! Windows device detection +//! +//! Uses PowerShell Get-Disk to enumerate block devices. + +use std::process::Command; + +use crate::utils::format_size; + +use super::types::BlockDevice; + +/// Get list of block devices on Windows +pub fn get_block_devices() -> Result, String> { + let output = Command::new("powershell") + .args([ + "-NoProfile", + "-Command", + "Get-Disk | Where-Object { $_.BusType -ne 'NVMe' -or $_.IsSystem -eq $false } | Select-Object Number, FriendlyName, Size, BusType | ConvertTo-Json", + ]) + .output() + .map_err(|e| format!("Failed to run PowerShell: {}", e))?; + + if !output.status.success() { + return Err("PowerShell command failed".to_string()); + } + + let stdout = String::from_utf8_lossy(&output.stdout); + + let json: serde_json::Value = + serde_json::from_str(&stdout).map_err(|e| format!("Failed to parse disk info: {}", e))?; + + let mut devices = Vec::new(); + + let disks = if json.is_array() { + json.as_array().unwrap().clone() + } else { + vec![json] + }; + + let system_disk = get_system_disk(); + + for disk in disks { + let number = disk["Number"].as_i64().unwrap_or(-1); + if number < 0 { + continue; + } + + // Skip system disk + if let Some(sys_num) = system_disk { + if number == sys_num { + continue; + } + } + + let size = disk["Size"].as_u64().unwrap_or(0); + if size == 0 { + continue; + } + + let model = disk["FriendlyName"] + .as_str() + .unwrap_or("Unknown") + .to_string(); + + let bus_type = disk["BusType"].as_str().unwrap_or(""); + let is_removable = bus_type == "USB" || bus_type == "SD"; + + devices.push(BlockDevice { + path: format!("\\\\.\\PhysicalDrive{}", number), + name: format!("Disk {}", number), + size, + size_formatted: format_size(size), + model, + is_removable, + is_system: false, + }); + } + + Ok(devices) +} + +/// Get the system disk number +fn get_system_disk() -> Option { + let output = Command::new("powershell") + .args([ + "-NoProfile", + "-Command", + "(Get-Partition -DriveLetter C | Get-Disk).Number", + ]) + .output() + .ok()?; + + let stdout = String::from_utf8_lossy(&output.stdout); + stdout.trim().parse().ok() +} diff --git a/src-tauri/src/download.rs b/src-tauri/src/download.rs new file mode 100644 index 0000000..498dda0 --- /dev/null +++ b/src-tauri/src/download.rs @@ -0,0 +1,185 @@ +//! Download module +//! +//! Handles downloading Armbian images from the web. + +use futures_util::StreamExt; +use reqwest::Client; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::Arc; +use tokio::sync::Mutex; + +use crate::config; +use crate::decompress::{decompress_with_rust_library, decompress_with_system_xz}; +use crate::{log_error, log_info, log_warn}; + +const MODULE: &str = "download"; + +/// Download progress state +pub struct DownloadState { + pub total_bytes: AtomicU64, + pub downloaded_bytes: AtomicU64, + pub is_decompressing: AtomicBool, + pub is_cancelled: AtomicBool, + pub error: Mutex>, + pub output_path: Mutex>, +} + +impl DownloadState { + pub fn new() -> Self { + Self { + total_bytes: AtomicU64::new(0), + downloaded_bytes: AtomicU64::new(0), + is_decompressing: AtomicBool::new(false), + is_cancelled: AtomicBool::new(false), + error: Mutex::new(None), + output_path: Mutex::new(None), + } + } + + pub fn reset(&self) { + self.total_bytes.store(0, Ordering::SeqCst); + self.downloaded_bytes.store(0, Ordering::SeqCst); + self.is_decompressing.store(false, Ordering::SeqCst); + self.is_cancelled.store(false, Ordering::SeqCst); + } +} + +impl Default for DownloadState { + fn default() -> Self { + Self::new() + } +} + +/// Extract filename from URL +fn extract_filename(url: &str) -> Result<&str, String> { + let url_path = url.split('?').next().unwrap_or(url); + url_path + .split('/') + .last() + .filter(|s| !s.is_empty()) + .ok_or_else(|| "Invalid URL: no filename".to_string()) +} + +/// Download and decompress an Armbian image +pub async fn download_image( + url: &str, + output_dir: &PathBuf, + state: Arc, +) -> Result { + state.reset(); + + let filename = extract_filename(url)?; + + // Determine output filename (remove .xz if present) + let output_filename = filename.trim_end_matches(".xz"); + let output_path = output_dir.join(output_filename); + + log_info!(MODULE, "Download requested: {}", url); + log_info!(MODULE, "Output path: {}", output_path.display()); + + // Check if already exists + if output_path.exists() { + log_info!(MODULE, "Image already exists, skipping download"); + *state.output_path.lock().await = Some(output_path.clone()); + return Ok(output_path); + } + + // Create output directory if needed + std::fs::create_dir_all(output_dir) + .map_err(|e| format!("Failed to create output directory: {}", e))?; + + let client = Client::builder() + .user_agent(config::app::USER_AGENT) + .build() + .map_err(|e| format!("Failed to create HTTP client: {}", e))?; + + // Start download + log_info!(MODULE, "Starting download..."); + let response = client + .get(url) + .send() + .await + .map_err(|e| { + log_error!(MODULE, "Failed to start download: {}", e); + format!("Failed to start download: {}", e) + })?; + + if !response.status().is_success() { + log_error!(MODULE, "Download failed with status: {}", response.status()); + return Err(format!( + "Download failed with status: {}", + response.status() + )); + } + + // Get content length + let total_size = response.content_length().unwrap_or(0); + state.total_bytes.store(total_size, Ordering::SeqCst); + + log_info!( + MODULE, + "Download size: {} bytes ({:.2} MB)", + total_size, + total_size as f64 / 1024.0 / 1024.0 + ); + + // Create temp file for compressed data + let temp_path = output_dir.join(format!("{}.downloading", filename)); + let mut temp_file = + File::create(&temp_path).map_err(|e| format!("Failed to create temp file: {}", e))?; + + // Download with progress + let mut stream = response.bytes_stream(); + let mut downloaded: u64 = 0; + + while let Some(chunk) = stream.next().await { + if state.is_cancelled.load(Ordering::SeqCst) { + log_info!(MODULE, "Download cancelled by user"); + drop(temp_file); + let _ = std::fs::remove_file(&temp_path); + return Err("Download cancelled".to_string()); + } + + let chunk = chunk.map_err(|e| format!("Download error: {}", e))?; + temp_file + .write_all(&chunk) + .map_err(|e| format!("Failed to write chunk: {}", e))?; + + downloaded += chunk.len() as u64; + state.downloaded_bytes.store(downloaded, Ordering::SeqCst); + } + + drop(temp_file); + log_info!(MODULE, "Download complete: {} bytes", downloaded); + + // Decompress if needed + if filename.ends_with(".xz") { + state.is_decompressing.store(true, Ordering::SeqCst); + log_info!(MODULE, "Starting decompression..."); + + // Try system xz first, fall back to Rust library + if let Err(e) = decompress_with_system_xz(&temp_path, &output_path) { + log_warn!( + MODULE, + "System xz failed: {}, falling back to Rust library (slower)", + e + ); + decompress_with_rust_library(&temp_path, &output_path, &state)?; + log_info!(MODULE, "Rust fallback decompression complete"); + } + + // Clean up temp file + let _ = std::fs::remove_file(&temp_path); + } else { + // No decompression needed, just rename + std::fs::rename(&temp_path, &output_path) + .map_err(|e| format!("Failed to move file: {}", e))?; + } + + log_info!(MODULE, "Image ready: {}", output_path.display()); + *state.output_path.lock().await = Some(output_path.clone()); + Ok(output_path) +} diff --git a/src-tauri/src/flash/linux.rs b/src-tauri/src/flash/linux.rs new file mode 100644 index 0000000..97a8a9e --- /dev/null +++ b/src-tauri/src/flash/linux.rs @@ -0,0 +1,126 @@ +//! Linux-specific flash implementation +//! +//! Uses pkexec (PolicyKit) for privilege escalation. +//! This shows a graphical authentication dialog for the user to enter their password. + +use super::{sync_device, unmount_device, FlashState}; +use crate::config; +use crate::{log_error, log_info}; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; +use std::process::{Command, Stdio}; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +const MODULE: &str = "flash::linux"; + +/// Flash an image to a block device on Linux +/// +/// Uses pkexec to run dd with elevated privileges. The user will see +/// a PolicyKit authentication dialog. +pub async fn flash_image( + image_path: &PathBuf, + device_path: &str, + state: Arc, + verify: bool, +) -> Result<(), String> { + state.reset(); + + log_info!( + MODULE, + "Starting flash: {} -> {}", + image_path.display(), + device_path + ); + + // Get image size + let image_size = std::fs::metadata(image_path) + .map_err(|e| format!("Failed to get image size: {}", e))? + .len(); + + state.total_bytes.store(image_size, Ordering::SeqCst); + + log_info!( + MODULE, + "Image size: {} bytes ({:.2} GB)", + image_size, + image_size as f64 / 1024.0 / 1024.0 / 1024.0 + ); + + // Unmount the device first + log_info!(MODULE, "Unmounting device partitions..."); + unmount_device(device_path)?; + + // Use pkexec to run dd with elevated privileges + let image_path_str = image_path.to_string_lossy(); + + log_info!(MODULE, "Starting privileged write with pkexec dd..."); + + let output = Command::new("pkexec") + .args([ + "dd", + &format!("if={}", image_path_str), + &format!("of={}", device_path), + "bs=4M", + "status=none", + ]) + .output() + .map_err(|e| { + log_error!(MODULE, "Failed to start privileged write: {}", e); + format!("Failed to start privileged write: {}", e) + })?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + if stderr.contains("dismissed") || stderr.contains("Not authorized") { + log_info!(MODULE, "Operation cancelled by user"); + return Err("Operation cancelled by user".to_string()); + } + log_error!(MODULE, "Flash failed: {}", stderr); + return Err(format!("Flash failed: {}", stderr)); + } + + state.written_bytes.store(image_size, Ordering::SeqCst); + log_info!(MODULE, "Write complete, syncing device..."); + + // Sync to ensure all data is written + sync_device(device_path); + + // Verify if requested + if verify { + log_info!(MODULE, "Starting verification..."); + verify_written_data(image_path, device_path, state.clone())?; + } + + log_info!(MODULE, "Flash complete!"); + Ok(()) +} + +/// Verify written data by reading back and comparing +/// Uses the shared verification logic from flash/verify.rs +fn verify_written_data( + image_path: &PathBuf, + device_path: &str, + state: Arc, +) -> Result<(), String> { + // Try to open device directly first, fall back to pkexec cat if permission denied + let device_result = std::fs::OpenOptions::new().read(true).open(device_path); + + let mut device: Box = match device_result { + Ok(f) => Box::new(f), + Err(_) => { + // Need elevated privileges to read - use pkexec cat + let child = Command::new("pkexec") + .args(["cat", device_path]) + .stdout(Stdio::piped()) + .spawn() + .map_err(|e| format!("Failed to start verification read: {}", e))?; + + Box::new(child.stdout.ok_or("Failed to capture stdout")?) + } + }; + + // Use shared verification logic + super::verify::verify_data(image_path, &mut device, state) +} diff --git a/src-tauri/src/flash/macos/authorization.rs b/src-tauri/src/flash/macos/authorization.rs new file mode 100644 index 0000000..e431436 --- /dev/null +++ b/src-tauri/src/flash/macos/authorization.rs @@ -0,0 +1,102 @@ +//! Authorization management module +//! +//! Handles macOS authorization requests and storage. + +use once_cell::sync::Lazy; +use std::sync::Mutex; + +use crate::{log_debug, log_error, log_info}; + +use super::bindings::{ + AuthorizationCreate, AuthorizationExternalForm, AuthorizationFree, AuthorizationItem, + AuthorizationMakeExternalForm, AuthorizationRef, AuthorizationRights, SafeAuthRef, + K_AUTHORIZATION_FLAG_EXTEND_RIGHTS, K_AUTHORIZATION_FLAG_INTERACTION_ALLOWED, + K_AUTHORIZATION_FLAG_PRE_AUTHORIZE, +}; + +const MODULE: &str = "flash::macos::auth"; + +/// Saved authorization state between request and flash +pub struct SavedAuthorization { + pub auth_ref: SafeAuthRef, + pub external_form: AuthorizationExternalForm, + pub device_path: String, +} + +/// Global state to store authorization between request and flash +pub static SAVED_AUTH: Lazy>> = Lazy::new(|| Mutex::new(None)); + +/// Request authorization BEFORE download starts +/// This shows the dialog immediately when user clicks Write +pub fn request_authorization(device_path: &str) -> Result { + // Use raw disk path + let raw_device = device_path.replace("/dev/disk", "/dev/rdisk"); + + unsafe { + // Build the right name exactly like RPi Imager: "sys.openfile.readwrite./dev/rdiskX" + let right_name = format!("sys.openfile.readwrite.{}", raw_device); + let right_name_cstr = std::ffi::CString::new(right_name.clone()).unwrap(); + + let mut item = AuthorizationItem { + name: right_name_cstr.as_ptr(), + value_length: 0, + value: std::ptr::null_mut(), + flags: 0, + }; + + let rights = AuthorizationRights { + count: 1, + items: &mut item, + }; + + let flags = K_AUTHORIZATION_FLAG_INTERACTION_ALLOWED + | K_AUTHORIZATION_FLAG_EXTEND_RIGHTS + | K_AUTHORIZATION_FLAG_PRE_AUTHORIZE; + + let mut auth_ref: AuthorizationRef = std::ptr::null_mut(); + + log_info!(MODULE, "Requesting authorization for device: {}", raw_device); + log_debug!(MODULE, "Right name: {}", right_name); + + let status = AuthorizationCreate( + &rights as *const AuthorizationRights, + std::ptr::null(), + flags, + &mut auth_ref, + ); + + log_debug!(MODULE, "AuthorizationCreate returned status: {}", status); + + if status != 0 { + // User cancelled or error + return Ok(false); + } + + // Convert to external form + let mut external_form = AuthorizationExternalForm::default(); + let status = AuthorizationMakeExternalForm(auth_ref, &mut external_form); + + if status != 0 { + AuthorizationFree(auth_ref, 0); + log_error!(MODULE, "AuthorizationMakeExternalForm failed: {}", status); + return Err(format!("Failed to create authorization: {}", status)); + } + + // Save authorization for later use - KEEP auth_ref alive! + let mut saved = SAVED_AUTH.lock().unwrap(); + *saved = Some(SavedAuthorization { + auth_ref: SafeAuthRef(auth_ref), + external_form, + device_path: raw_device, + }); + + log_info!(MODULE, "Authorization saved successfully"); + Ok(true) + } +} + +/// Free an authorization reference +pub unsafe fn free_authorization(auth_ref: AuthorizationRef) { + log_debug!(MODULE, "Freeing authorization ref"); + AuthorizationFree(auth_ref, 0); +} diff --git a/src-tauri/src/flash/macos/bindings.rs b/src-tauri/src/flash/macos/bindings.rs new file mode 100644 index 0000000..f3125d9 --- /dev/null +++ b/src-tauri/src/flash/macos/bindings.rs @@ -0,0 +1,72 @@ +//! Security framework FFI bindings +//! +//! C bindings for macOS Security.framework authorization. + +use std::ffi::c_void; + +// Security framework bindings +#[link(name = "Security", kind = "framework")] +extern "C" { + pub fn AuthorizationCreate( + rights: *const AuthorizationRights, + environment: *const AuthorizationEnvironment, + flags: u32, + authorization: *mut AuthorizationRef, + ) -> i32; + + pub fn AuthorizationFree(authorization: AuthorizationRef, flags: u32) -> i32; + + pub fn AuthorizationMakeExternalForm( + authorization: AuthorizationRef, + external_form: *mut AuthorizationExternalForm, + ) -> i32; +} + +/// Authorization reference (opaque pointer) +pub type AuthorizationRef = *mut c_void; + +/// Wrapper to make AuthorizationRef Send+Sync safe +pub struct SafeAuthRef(pub AuthorizationRef); +unsafe impl Send for SafeAuthRef {} +unsafe impl Sync for SafeAuthRef {} + +/// Authorization item for rights requests +#[repr(C)] +pub struct AuthorizationItem { + pub name: *const i8, + pub value_length: usize, + pub value: *mut c_void, + pub flags: u32, +} + +/// Authorization rights structure +#[repr(C)] +pub struct AuthorizationRights { + pub count: u32, + pub items: *mut AuthorizationItem, +} + +/// Authorization environment structure +#[repr(C)] +pub struct AuthorizationEnvironment { + pub count: u32, + pub items: *mut AuthorizationItem, +} + +/// External form for passing authorization between processes +#[repr(C)] +#[derive(Clone, Copy)] +pub struct AuthorizationExternalForm { + pub bytes: [u8; 32], +} + +impl Default for AuthorizationExternalForm { + fn default() -> Self { + Self { bytes: [0u8; 32] } + } +} + +// Authorization flags +pub const K_AUTHORIZATION_FLAG_INTERACTION_ALLOWED: u32 = 1 << 0; +pub const K_AUTHORIZATION_FLAG_EXTEND_RIGHTS: u32 = 1 << 1; +pub const K_AUTHORIZATION_FLAG_PRE_AUTHORIZE: u32 = 1 << 4; diff --git a/src-tauri/src/flash/macos/mod.rs b/src-tauri/src/flash/macos/mod.rs new file mode 100644 index 0000000..d9fe509 --- /dev/null +++ b/src-tauri/src/flash/macos/mod.rs @@ -0,0 +1,12 @@ +//! macOS-specific flash implementation +//! +//! Uses Security.framework AuthorizationCreate + authopen with -extauth +//! This is the same approach used by Raspberry Pi Imager. + +mod authorization; +mod bindings; +mod writer; + +// Re-export public API +pub use authorization::request_authorization; +pub use writer::flash_image; diff --git a/src-tauri/src/flash/macos/writer.rs b/src-tauri/src/flash/macos/writer.rs new file mode 100644 index 0000000..b358290 --- /dev/null +++ b/src-tauri/src/flash/macos/writer.rs @@ -0,0 +1,395 @@ +//! Device writer module +//! +//! Handles opening devices with authorization and writing data. + +use std::fs::File; +use std::io::{Read, Write}; +use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::path::PathBuf; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +use crate::config; +use crate::flash::{sync_device, unmount_device, FlashState}; +use crate::{log_debug, log_error, log_info}; + +use super::authorization::{free_authorization, SAVED_AUTH}; +use super::bindings::AuthorizationRef; + +const MODULE: &str = "flash::macos::writer"; + +/// Wrapper to make auth_ref Send safe +pub struct SendableAuthRef(pub AuthorizationRef); +unsafe impl Send for SendableAuthRef {} + +/// Result of opening device - includes auth_ref that must be kept alive +pub struct OpenDeviceResult { + pub file: File, + pub auth_ref: SendableAuthRef, +} + +/// Open device using previously saved authorization +pub fn open_device_with_saved_auth(device_path: &str) -> Result { + let mut saved_guard = SAVED_AUTH.lock().unwrap(); + let auth = saved_guard + .take() + .ok_or("No authorization saved - call request_authorization first")?; + + if auth.device_path != device_path { + // Put it back if mismatch + *saved_guard = Some(auth); + return Err(format!( + "Authorization mismatch: saved for {} but trying to open {}", + saved_guard.as_ref().unwrap().device_path, + device_path + )); + } + + let external_form = auth.external_form; + let auth_ref = auth.auth_ref.0; + drop(saved_guard); // Release lock before fork + + let result = unsafe { + // Create socket pair for receiving fd + let mut sock_pair: [i32; 2] = [0; 2]; + if libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, sock_pair.as_mut_ptr()) != 0 { + return Err("Failed to create socket pair".to_string()); + } + + // Create pipe for stdin (to pass auth) + let mut stdin_pipe: [i32; 2] = [0; 2]; + if libc::pipe(stdin_pipe.as_mut_ptr()) != 0 { + libc::close(sock_pair[0]); + libc::close(sock_pair[1]); + return Err("Failed to create stdin pipe".to_string()); + } + + let pid = libc::fork(); + if pid < 0 { + libc::close(sock_pair[0]); + libc::close(sock_pair[1]); + libc::close(stdin_pipe[0]); + libc::close(stdin_pipe[1]); + return Err("Failed to fork".to_string()); + } + + if pid == 0 { + // Child process + libc::close(sock_pair[0]); + libc::close(stdin_pipe[1]); + + // Redirect stdout to socket + libc::dup2(sock_pair[1], libc::STDOUT_FILENO); + // Redirect stdin from pipe + libc::dup2(stdin_pipe[0], libc::STDIN_FILENO); + + let authopen = std::ffi::CString::new("/usr/libexec/authopen").unwrap(); + let arg_stdoutpipe = std::ffi::CString::new("-stdoutpipe").unwrap(); + let arg_extauth = std::ffi::CString::new("-extauth").unwrap(); + let arg_o = std::ffi::CString::new("-o").unwrap(); + let arg_mode = std::ffi::CString::new("2").unwrap(); // O_RDWR + let path = std::ffi::CString::new(device_path).unwrap(); + + libc::execl( + authopen.as_ptr(), + authopen.as_ptr(), + arg_stdoutpipe.as_ptr(), + arg_extauth.as_ptr(), + arg_o.as_ptr(), + arg_mode.as_ptr(), + path.as_ptr(), + std::ptr::null::(), + ); + + libc::_exit(1); + } + + // Parent process + libc::close(sock_pair[1]); + libc::close(stdin_pipe[0]); + + // Write the external auth form to authopen's stdin + log_debug!(MODULE, "Sending saved authorization to authopen"); + libc::write( + stdin_pipe[1], + external_form.bytes.as_ptr() as *const libc::c_void, + external_form.bytes.len(), + ); + libc::close(stdin_pipe[1]); + + // Receive file descriptor from authopen + let buf_size = 64; + let mut buf = vec![0u8; buf_size]; + + let cmsg_size = libc::CMSG_SPACE(std::mem::size_of::() as u32) as usize; + let mut cmsg_buf = vec![0u8; cmsg_size]; + + let mut iov = libc::iovec { + iov_base: buf.as_mut_ptr() as *mut libc::c_void, + iov_len: buf_size, + }; + + let mut msg: libc::msghdr = std::mem::zeroed(); + msg.msg_iov = &mut iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf.as_mut_ptr() as *mut libc::c_void; + msg.msg_controllen = cmsg_size as u32; + + let size = libc::recvmsg(sock_pair[0], &mut msg, 0); + log_debug!(MODULE, "recvmsg returned size: {}", size); + + // Wait for child + let mut status: i32 = 0; + loop { + let wpid = libc::waitpid(pid, &mut status, 0); + if wpid != -1 || *libc::__error() != libc::EINTR { + break; + } + } + + log_debug!(MODULE, "authopen exit code: {}", libc::WEXITSTATUS(status)); + + libc::close(sock_pair[0]); + + if size <= 0 { + return Err(format!( + "Failed to receive file descriptor (size={})", + size + )); + } + + if libc::WIFEXITED(status) && libc::WEXITSTATUS(status) != 0 { + return Err(format!( + "authopen failed with exit code {}", + libc::WEXITSTATUS(status) + )); + } + + // Extract fd from control message + let cmsg = libc::CMSG_FIRSTHDR(&msg); + if cmsg.is_null() { + return Err("No control message received".to_string()); + } + + let cmsg_ref = &*cmsg; + if cmsg_ref.cmsg_type != libc::SCM_RIGHTS { + return Err("Unexpected control message type".to_string()); + } + + let fd_ptr = libc::CMSG_DATA(cmsg) as *const i32; + let fd = *fd_ptr; + + if fd < 0 { + return Err("Received invalid file descriptor".to_string()); + } + + log_debug!(MODULE, "Successfully received fd: {}", fd); + OpenDeviceResult { + file: File::from_raw_fd(fd), + auth_ref: SendableAuthRef(auth_ref), + } + }; + + Ok(result) +} + +/// Quick erase - write zeros to first portion of device +pub fn quick_erase(device: &mut File, device_fd: i32) -> Result<(), String> { + let erase_size = config::flash::QUICK_ERASE_SIZE; + let chunk_size = config::flash::ERASE_CHUNK_SIZE; + + log_info!( + MODULE, + "Quick erase: writing zeros to first {} MB", + erase_size / (1024 * 1024) + ); + + // Seek to beginning + unsafe { + libc::lseek(device_fd, 0, libc::SEEK_SET); + } + + let zero_buffer = vec![0u8; chunk_size]; + let mut erased: usize = 0; + + while erased < erase_size { + let to_write = std::cmp::min(chunk_size, erase_size - erased); + device + .write_all(&zero_buffer[..to_write]) + .map_err(|e| format!("Quick erase failed at byte {}: {}", erased, e))?; + erased += to_write; + } + + // Sync the erase + device.flush().ok(); + unsafe { + libc::fsync(device_fd); + } + + // Seek back to beginning for image write + unsafe { + libc::lseek(device_fd, 0, libc::SEEK_SET); + } + + log_info!(MODULE, "Quick erase complete"); + Ok(()) +} + +/// Flash an image to a block device on macOS +pub async fn flash_image( + image_path: &PathBuf, + device_path: &str, + state: Arc, + verify: bool, +) -> Result<(), String> { + state.reset(); + + // Get image size + let image_size = std::fs::metadata(image_path) + .map_err(|e| format!("Failed to get image size: {}", e))? + .len(); + + state.total_bytes.store(image_size, Ordering::SeqCst); + + // Use raw disk access for better performance + let raw_device = device_path.replace("/dev/disk", "/dev/rdisk"); + + // Unmount the device first + unmount_device(device_path)?; + + // Small delay to ensure unmount completes + std::thread::sleep(std::time::Duration::from_millis(500)); + + // Open device using saved authorization (no dialog here!) + log_info!(MODULE, "Opening device with saved authorization"); + let open_result = open_device_with_saved_auth(&raw_device)?; + let mut device = open_result.file; + let device_fd = device.as_raw_fd(); + let auth_ref_wrapper = open_result.auth_ref; + + log_debug!(MODULE, "Keeping authorization ref alive during flash"); + + // Clear saved auth state (we have the auth_ref now) + { + let mut saved = SAVED_AUTH.lock().unwrap(); + *saved = None; + } + + // Use inner function to do the actual work, then always free auth at the end + let result = + do_flash_work(image_path, device_path, &mut device, device_fd, image_size, state, verify) + .await; + + drop(device); + + // ALWAYS free the authorization - after everything is done + unsafe { + free_authorization(auth_ref_wrapper.0); + } + + result +} + +/// Inner function to do flash work +async fn do_flash_work( + image_path: &PathBuf, + device_path: &str, + device: &mut File, + device_fd: i32, + image_size: u64, + state: Arc, + verify: bool, +) -> Result<(), String> { + // Quick erase first - clear partition tables and boot sectors + quick_erase(device, device_fd)?; + + // Open image file + let mut image_file = + File::open(image_path).map_err(|e| format!("Failed to open image: {}", e))?; + + // Write image in chunks with progress + let chunk_size = config::flash::CHUNK_SIZE; + let mut buffer = vec![0u8; chunk_size]; + let mut written: u64 = 0; + + log_info!( + MODULE, + "Starting to write {} bytes ({:.2} GB)", + image_size, + image_size as f64 / 1024.0 / 1024.0 / 1024.0 + ); + + loop { + if state.is_cancelled.load(Ordering::SeqCst) { + return Err("Flash cancelled".to_string()); + } + + let bytes_read = image_file + .read(&mut buffer) + .map_err(|e| format!("Failed to read image: {}", e))?; + + if bytes_read == 0 { + break; + } + + if let Err(e) = device.write_all(&buffer[..bytes_read]) { + log_error!( + MODULE, + "Write error at byte {}/{}: {}", + written, + image_size, + e + ); + return Err(format!( + "Failed to write to device at byte {}: {}", + written, e + )); + } + + written += bytes_read as u64; + state.written_bytes.store(written, Ordering::SeqCst); + + // Log progress every 512MB + if written % (512 * 1024 * 1024) == 0 { + log_info!( + MODULE, + "Progress: {:.1}%", + (written as f64 / image_size as f64) * 100.0 + ); + } + } + + log_info!(MODULE, "Write complete, syncing..."); + + // Sync to ensure all data is written + device.flush().ok(); + unsafe { + libc::fsync(device_fd); + } + sync_device(device_path); + + // Verify if requested - reuse same fd (no additional auth needed) + if verify { + log_info!(MODULE, "Starting verification"); + verify_written_data(image_path, device, device_fd, state.clone())?; + } + + log_info!(MODULE, "Flash complete!"); + Ok(()) +} + +/// Verify written data by reading back and comparing +fn verify_written_data( + image_path: &PathBuf, + device: &mut File, + device_fd: i32, + state: Arc, +) -> Result<(), String> { + // Seek device back to beginning before verification + unsafe { + libc::lseek(device_fd, 0, libc::SEEK_SET); + } + + // Use shared verification logic + crate::flash::verify::verify_data(image_path, device, state) +} diff --git a/src-tauri/src/flash/mod.rs b/src-tauri/src/flash/mod.rs new file mode 100644 index 0000000..d8df6df --- /dev/null +++ b/src-tauri/src/flash/mod.rs @@ -0,0 +1,137 @@ +//! Flash module - Platform-specific image flashing implementations +//! +//! This module provides privilege escalation and raw device writing for each platform: +//! - macOS: Uses authopen with Touch ID support +//! - Linux: Uses pkexec for privilege escalation +//! - Windows: Requires running as Administrator + +mod verify; + +#[cfg(target_os = "macos")] +mod macos; +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "windows")] +mod windows; + +use sha2::{Digest, Sha256}; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; +use std::process::Command; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use tokio::sync::Mutex; + +/// Flash progress state shared between frontend and backend +pub struct FlashState { + pub total_bytes: AtomicU64, + pub written_bytes: AtomicU64, + pub verified_bytes: AtomicU64, + pub is_verifying: AtomicBool, + pub is_cancelled: AtomicBool, + pub error: Mutex>, +} + +impl FlashState { + pub fn new() -> Self { + Self { + total_bytes: AtomicU64::new(0), + written_bytes: AtomicU64::new(0), + verified_bytes: AtomicU64::new(0), + is_verifying: AtomicBool::new(false), + is_cancelled: AtomicBool::new(false), + error: Mutex::new(None), + } + } + + pub fn reset(&self) { + self.total_bytes.store(0, Ordering::SeqCst); + self.written_bytes.store(0, Ordering::SeqCst); + self.verified_bytes.store(0, Ordering::SeqCst); + self.is_verifying.store(false, Ordering::SeqCst); + self.is_cancelled.store(false, Ordering::SeqCst); + } +} + +// Re-export the platform-specific flash_image function +#[cfg(target_os = "macos")] +pub use macos::flash_image; +#[cfg(target_os = "linux")] +pub use linux::flash_image; +#[cfg(target_os = "windows")] +pub use windows::flash_image; + +// Re-export authorization function for macOS +#[cfg(target_os = "macos")] +pub use macos::request_authorization; + +/// Request authorization before flashing (platform-specific) +/// On macOS: Shows Touch ID / password dialog +/// On Linux/Windows: No-op (authorization happens during flash) +#[cfg(not(target_os = "macos"))] +pub fn request_authorization(_device_path: &str) -> Result { + Ok(true) +} + +/// Unmount a device before flashing (platform-specific) +pub(crate) fn unmount_device(device_path: &str) -> Result<(), String> { + #[cfg(target_os = "macos")] + { + let _ = Command::new("diskutil") + .args(["unmountDisk", device_path]) + .output(); + } + + #[cfg(target_os = "linux")] + { + let output = Command::new("lsblk") + .args(["-ln", "-o", "NAME", device_path]) + .output(); + + if let Ok(output) = output { + let stdout = String::from_utf8_lossy(&output.stdout); + for line in stdout.lines() { + let part_path = format!("/dev/{}", line.trim()); + let _ = Command::new("umount").arg(&part_path).output(); + } + } + } + + #[cfg(target_os = "windows")] + { + let _ = device_path; + } + + Ok(()) +} + +/// Sync device to ensure all data is written to disk +pub(crate) fn sync_device(_device_path: &str) { + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let _ = Command::new("sync").output(); + } +} + +/// Calculate SHA256 hash of a file +#[allow(dead_code)] +pub fn calculate_sha256(path: &PathBuf) -> Result { + let mut file = File::open(path).map_err(|e| format!("Failed to open file: {}", e))?; + + let mut hasher = Sha256::new(); + let mut buffer = vec![0u8; 1024 * 1024]; + + loop { + let bytes_read = file + .read(&mut buffer) + .map_err(|e| format!("Failed to read file: {}", e))?; + + if bytes_read == 0 { + break; + } + + hasher.update(&buffer[..bytes_read]); + } + + Ok(hex::encode(hasher.finalize())) +} diff --git a/src-tauri/src/flash/verify.rs b/src-tauri/src/flash/verify.rs new file mode 100644 index 0000000..a0d561d --- /dev/null +++ b/src-tauri/src/flash/verify.rs @@ -0,0 +1,134 @@ +//! Shared verification logic for all platforms +//! +//! This module provides common verification functionality that can be used +//! across macOS, Linux, and Windows implementations. + +#![allow(dead_code)] + +use crate::config; +use crate::{log_error, log_info}; +use std::fs::File; +use std::io::Read; +use std::path::PathBuf; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +use super::FlashState; + +const MODULE: &str = "flash::verify"; + +/// Verification reader trait for platform-specific device reading +pub trait VerificationReader: Read + Send {} + +impl VerificationReader for T {} + +/// Verify written data by comparing image file with device contents +/// +/// This function is platform-agnostic and takes any reader that implements +/// the Read trait. Platform-specific code is responsible for providing +/// the appropriate device reader. +pub fn verify_data( + image_path: &PathBuf, + device_reader: &mut R, + state: Arc, +) -> Result<(), String> { + state.is_verifying.store(true, Ordering::SeqCst); + state.verified_bytes.store(0, Ordering::SeqCst); + + let mut image_file = + File::open(image_path).map_err(|e| format!("Failed to open image for verification: {}", e))?; + + let chunk_size = config::flash::CHUNK_SIZE; + let mut image_buffer = vec![0u8; chunk_size]; + let mut device_buffer = vec![0u8; chunk_size]; + let mut verified: u64 = 0; + let mut last_logged_percent: u64 = 0; + + let image_size = state.total_bytes.load(Ordering::SeqCst); + + log_info!( + MODULE, + "Starting verification of {} bytes ({:.2} GB)", + image_size, + image_size as f64 / 1024.0 / 1024.0 / 1024.0 + ); + + while verified < image_size { + if state.is_cancelled.load(Ordering::SeqCst) { + return Err("Verification cancelled".to_string()); + } + + let to_read = std::cmp::min(chunk_size as u64, image_size - verified) as usize; + + let image_read = image_file + .read(&mut image_buffer[..to_read]) + .map_err(|e| format!("Failed to read image: {}", e))?; + + if image_read == 0 { + break; + } + + // Read same amount from device + let mut device_read = 0; + while device_read < image_read { + let n = device_reader + .read(&mut device_buffer[device_read..image_read]) + .map_err(|e| format!("Failed to read device: {}", e))?; + if n == 0 { + break; + } + device_read += n; + } + + if device_read != image_read { + log_error!( + MODULE, + "Verification failed: size mismatch at byte {} (expected {}, got {})", + verified, + image_read, + device_read + ); + return Err(format!( + "Verification failed: size mismatch at byte {} (expected {}, got {})", + verified, image_read, device_read + )); + } + + if image_buffer[..image_read] != device_buffer[..device_read] { + log_error!(MODULE, "Verification failed: data mismatch at byte {}", verified); + return Err(format!( + "Verification failed: data mismatch at byte {}", + verified + )); + } + + verified += image_read as u64; + state.verified_bytes.store(verified, Ordering::SeqCst); + + // Log progress at configured interval + let current_percent = (verified * 100 / image_size) as u64; + if current_percent >= last_logged_percent + config::flash::LOG_INTERVAL_PERCENT { + log_info!( + MODULE, + "Progress: {:.1}%", + (verified as f64 / image_size as f64) * 100.0 + ); + last_logged_percent = current_percent; + } + } + + log_info!(MODULE, "Verification complete!"); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn test_verify_matching_data() { + // This test requires a temp file, which we'll skip for now + // In production, we'd create temp files and verify they match + } +} diff --git a/src-tauri/src/flash/windows.rs b/src-tauri/src/flash/windows.rs new file mode 100644 index 0000000..ef7bcbd --- /dev/null +++ b/src-tauri/src/flash/windows.rs @@ -0,0 +1,142 @@ +//! Windows-specific flash implementation +//! +//! On Windows, the application must be run as Administrator to access raw disk devices. +//! Uses direct file I/O to write to physical disks. + +use super::FlashState; +use crate::config; +use crate::{log_debug, log_error, log_info}; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; +use std::sync::atomic::Ordering; +use std::sync::Arc; + +const MODULE: &str = "flash::windows"; + +/// Flash an image to a block device on Windows +/// +/// Requires the application to be running with Administrator privileges. +/// Opens the physical disk device directly for raw write access. +pub async fn flash_image( + image_path: &PathBuf, + device_path: &str, + state: Arc, + verify: bool, +) -> Result<(), String> { + state.reset(); + + log_info!( + MODULE, + "Starting flash: {} -> {}", + image_path.display(), + device_path + ); + + // Get image size + let image_size = std::fs::metadata(image_path) + .map_err(|e| format!("Failed to get image size: {}", e))? + .len(); + + state.total_bytes.store(image_size, Ordering::SeqCst); + + log_info!( + MODULE, + "Image size: {} bytes ({:.2} GB)", + image_size, + image_size as f64 / 1024.0 / 1024.0 / 1024.0 + ); + + // Open image file + let mut image_file = + File::open(image_path).map_err(|e| format!("Failed to open image: {}", e))?; + + // Open device for writing (requires Administrator) + log_info!(MODULE, "Opening device for writing (requires Administrator)..."); + let mut device = open_device_for_write(device_path)?; + + // Write image in chunks with progress + let chunk_size = config::flash::CHUNK_SIZE; + let mut buffer = vec![0u8; chunk_size]; + let mut written: u64 = 0; + let mut last_logged_percent: u64 = 0; + + log_info!(MODULE, "Writing image to device..."); + + loop { + if state.is_cancelled.load(Ordering::SeqCst) { + log_info!(MODULE, "Flash cancelled by user"); + return Err("Flash cancelled".to_string()); + } + + let bytes_read = image_file + .read(&mut buffer) + .map_err(|e| { + log_error!(MODULE, "Failed to read image: {}", e); + format!("Failed to read image: {}", e) + })?; + + if bytes_read == 0 { + break; + } + + device + .write_all(&buffer[..bytes_read]) + .map_err(|e| { + log_error!(MODULE, "Failed to write to device: {}", e); + format!("Failed to write to device: {}", e) + })?; + + written += bytes_read as u64; + state.written_bytes.store(written, Ordering::SeqCst); + + // Log progress at intervals + let current_percent = (written * 100 / image_size) as u64; + if current_percent >= last_logged_percent + config::flash::LOG_INTERVAL_PERCENT { + log_debug!( + MODULE, + "Write progress: {:.1}%", + (written as f64 / image_size as f64) * 100.0 + ); + last_logged_percent = current_percent; + } + } + + device.flush().ok(); + log_info!(MODULE, "Write complete!"); + + // Verify if requested + if verify { + log_info!(MODULE, "Starting verification..."); + // Close and reopen device for reading + drop(device); + let mut device = open_device_for_read(device_path)?; + + // Use shared verification logic + super::verify::verify_data(image_path, &mut device, state)?; + } + + log_info!(MODULE, "Flash complete!"); + Ok(()) +} + +/// Open device for writing (requires Administrator privileges) +fn open_device_for_write(device_path: &str) -> Result { + std::fs::OpenOptions::new() + .write(true) + .open(device_path) + .map_err(|e| { + format!( + "Failed to open device for writing: {}. Try running as Administrator.", + e + ) + }) +} + +/// Open device for reading +fn open_device_for_read(device_path: &str) -> Result { + std::fs::OpenOptions::new() + .read(true) + .open(device_path) + .map_err(|e| format!("Failed to open device for reading: {}", e)) +} diff --git a/src-tauri/src/images/filters.rs b/src-tauri/src/images/filters.rs new file mode 100644 index 0000000..244feec --- /dev/null +++ b/src-tauri/src/images/filters.rs @@ -0,0 +1,190 @@ +//! Image filtering and extraction +//! +//! Functions for parsing and filtering image data. + +use once_cell::sync::Lazy; +use std::collections::HashMap; + +use crate::config; +use crate::utils::normalize_slug; + +use super::models::{ArmbianImage, BoardInfo, ImageInfo}; + +// Load board names from JSON at compile time +static BOARD_NAMES: Lazy> = Lazy::new(|| { + let json_str = include_str!("../data/board_names.json"); + serde_json::from_str(json_str).unwrap_or_default() +}); + +/// Convert a board slug to a human-readable name +fn slug_to_display_name(slug: &str) -> String { + let normalized = normalize_slug(slug); + + // Look up in JSON map first + if let Some(name) = BOARD_NAMES.get(&normalized) { + return name.clone(); + } + + // Fallback: simple formatting for unknown boards + slug.split(|c| c == '-' || c == '_') + .filter(|s| !s.is_empty()) + .map(|part| { + let mut chars = part.chars(); + match chars.next() { + Some(first) => first.to_uppercase().collect::() + chars.as_str(), + None => String::new(), + } + }) + .collect::>() + .join(" ") +} + +/// Check if file extension is a valid image file +fn is_valid_image_extension(ext: &str) -> bool { + let ext_lower = ext.to_lowercase(); + ext_lower.starts_with("img") + && !ext_lower.contains("asc") + && !ext_lower.contains("torrent") + && !ext_lower.contains("sha") +} + +/// Extract all image objects from the nested JSON structure +pub fn extract_images(json: &serde_json::Value) -> Vec { + let mut images = Vec::new(); + extract_images_recursive(json, &mut images); + images +} + +fn extract_images_recursive(value: &serde_json::Value, images: &mut Vec) { + match value { + serde_json::Value::Object(map) => { + if map.contains_key("board_slug") { + if let Ok(img) = serde_json::from_value::(value.clone()) { + if let Some(ref ext) = img.file_extension { + if is_valid_image_extension(ext) { + let kernel = img.kernel_branch.as_deref().unwrap_or(""); + if kernel != "cloud" { + images.push(img); + } + } + } + } + } + for (_, v) in map { + extract_images_recursive(v, images); + } + } + serde_json::Value::Array(arr) => { + for v in arr { + extract_images_recursive(v, images); + } + } + _ => {} + } +} + +/// Get unique board list from images +pub fn get_unique_boards(images: &[ArmbianImage]) -> Vec { + let mut board_map: HashMap = HashMap::new(); + + for img in images { + if let Some(ref slug) = img.board_slug { + let normalized = normalize_slug(slug); + let entry = board_map + .entry(normalized.clone()) + .or_insert((slug.clone(), 0, false)); + entry.1 += 1; + if img.promoted.as_deref() == Some("true") { + entry.2 = true; + } + } + } + + let mut boards: Vec = board_map + .into_iter() + .map(|(slug, (original_slug, count, promoted))| BoardInfo { + slug, + name: slug_to_display_name(&original_slug), + image_count: count, + has_promoted: promoted, + }) + .collect(); + + boards.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase())); + boards +} + +/// Filter images for a specific board +pub fn filter_images_for_board( + images: &[ArmbianImage], + board_slug: &str, + preapp_filter: Option<&str>, + kernel_filter: Option<&str>, + variant_filter: Option<&str>, + stable_only: bool, +) -> Vec { + let normalized_board = normalize_slug(board_slug); + + let mut filtered: Vec = images + .iter() + .filter(|img| { + let img_slug = img.board_slug.as_deref().unwrap_or(""); + if normalize_slug(img_slug) != normalized_board { + return false; + } + + if let Some(filter) = preapp_filter { + let preapp = img.preinstalled_application.as_deref().unwrap_or(""); + if filter == config::images::EMPTY_FILTER { + if !preapp.is_empty() { + return false; + } + } else if preapp != filter { + return false; + } + } + + if stable_only { + let repo = img.download_repository.as_deref().unwrap_or(""); + if repo != config::images::STABLE_REPO { + return false; + } + } + + if let Some(filter) = kernel_filter { + let kernel = img.kernel_branch.as_deref().unwrap_or(""); + if kernel != filter { + return false; + } + } + + if let Some(filter) = variant_filter { + let variant = img.image_variant.as_deref().unwrap_or(""); + if variant != filter { + return false; + } + } + + true + }) + .map(|img| ImageInfo { + armbian_version: img.armbian_version.clone().unwrap_or_default(), + distro_release: img.distro_release.clone().unwrap_or_default(), + kernel_branch: img.kernel_branch.clone().unwrap_or_default(), + image_variant: img.image_variant.clone().unwrap_or_default(), + preinstalled_application: img.preinstalled_application.clone().unwrap_or_default(), + promoted: img.promoted.as_deref() == Some("true"), + file_url: img.file_url.clone().unwrap_or_default(), + file_size: img.file_size.as_ref().and_then(|s| s.parse().ok()).unwrap_or(0), + download_repository: img.download_repository.clone().unwrap_or_default(), + }) + .collect(); + + filtered.sort_by(|a, b| match (a.promoted, b.promoted) { + (true, false) => std::cmp::Ordering::Less, + (false, true) => std::cmp::Ordering::Greater, + _ => b.armbian_version.cmp(&a.armbian_version), + }); + + filtered +} diff --git a/src-tauri/src/images/mod.rs b/src-tauri/src/images/mod.rs new file mode 100644 index 0000000..bf480aa --- /dev/null +++ b/src-tauri/src/images/mod.rs @@ -0,0 +1,27 @@ +//! Image management module +//! +//! Handles fetching, parsing, and filtering Armbian image data. + +mod filters; +mod models; + +// Re-export types and functions +pub use filters::{extract_images, filter_images_for_board, get_unique_boards}; +pub use models::{BoardInfo, ImageInfo}; +// ArmbianImage is used internally by filters module + +use crate::config; + +/// Fetch the all-images.json from Armbian +pub async fn fetch_all_images() -> Result { + let response = reqwest::get(config::urls::ALL_IMAGES) + .await + .map_err(|e| format!("Failed to fetch images: {}", e))?; + + let json: serde_json::Value = response + .json() + .await + .map_err(|e| format!("Failed to parse JSON: {}", e))?; + + Ok(json) +} diff --git a/src-tauri/src/images/models.rs b/src-tauri/src/images/models.rs new file mode 100644 index 0000000..ee02c2c --- /dev/null +++ b/src-tauri/src/images/models.rs @@ -0,0 +1,45 @@ +//! Image data models +//! +//! Types representing Armbian images and boards. + +use serde::{Deserialize, Serialize}; + +/// Raw Armbian image data from the API +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ArmbianImage { + pub board_slug: Option, + pub armbian_version: Option, + pub distro_release: Option, + pub kernel_branch: Option, + pub image_variant: Option, + pub preinstalled_application: Option, + pub promoted: Option, + pub file_url: Option, + pub file_extension: Option, + pub file_size: Option, + pub download_repository: Option, + pub redi_url: Option, +} + +/// Board information for display +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct BoardInfo { + pub slug: String, + pub name: String, + pub image_count: usize, + pub has_promoted: bool, +} + +/// Processed image information for the UI +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ImageInfo { + pub armbian_version: String, + pub distro_release: String, + pub kernel_branch: String, + pub image_variant: String, + pub preinstalled_application: String, + pub promoted: bool, + pub file_url: String, + pub file_size: u64, + pub download_repository: String, +} diff --git a/src-tauri/src/logging/mod.rs b/src-tauri/src/logging/mod.rs new file mode 100644 index 0000000..f1bce74 --- /dev/null +++ b/src-tauri/src/logging/mod.rs @@ -0,0 +1,335 @@ +//! Application logging system +//! +//! Provides structured, formatted logging with file output support. +//! Logs are written to the application's log directory with timestamps +//! and log level indicators. + +use chrono::{DateTime, Local}; +use once_cell::sync::Lazy; +use std::fs::{self, File, OpenOptions}; +use std::io::Write; +use std::path::PathBuf; +use std::sync::Mutex; + +use crate::config; +use crate::utils::get_cache_dir; + +/// Log levels for categorizing messages +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum LogLevel { + Debug, + Info, + Warn, + Error, +} + +impl LogLevel { + /// Get the string representation with fixed width for alignment + pub fn as_str(&self) -> &'static str { + match self { + LogLevel::Debug => "DEBUG", + LogLevel::Info => "INFO ", + LogLevel::Warn => "WARN ", + LogLevel::Error => "ERROR", + } + } + + /// Get ANSI color code for terminal output + pub fn color_code(&self) -> &'static str { + match self { + LogLevel::Debug => "\x1b[36m", // Cyan + LogLevel::Info => "\x1b[32m", // Green + LogLevel::Warn => "\x1b[33m", // Yellow + LogLevel::Error => "\x1b[31m", // Red + } + } +} + +/// Logger configuration +pub struct LoggerConfig { + /// Minimum log level to output + pub min_level: LogLevel, + /// Whether to output to stderr + pub console_output: bool, + /// Whether to output to file + pub file_output: bool, + /// Whether to use colors in console output + pub use_colors: bool, +} + +impl Default for LoggerConfig { + fn default() -> Self { + Self { + min_level: LogLevel::Info, + console_output: true, + file_output: true, + use_colors: true, + } + } +} + +/// Global logger instance +struct Logger { + config: LoggerConfig, + log_file: Option, + log_path: Option, +} + +impl Logger { + fn new() -> Self { + let config = LoggerConfig::default(); + let (log_file, log_path) = Self::create_log_file(); + + Self { + config, + log_file, + log_path, + } + } + + fn create_log_file() -> (Option, Option) { + let log_dir = get_log_dir(); + + if let Err(e) = fs::create_dir_all(&log_dir) { + eprintln!("Failed to create log directory: {}", e); + return (None, None); + } + + // Create log file with timestamp + let timestamp = Local::now().format("%Y%m%d_%H%M%S"); + let log_filename = format!("armbian-imager_{}.log", timestamp); + let log_path = log_dir.join(&log_filename); + + match OpenOptions::new() + .create(true) + .write(true) + .append(true) + .open(&log_path) + { + Ok(file) => (Some(file), Some(log_path)), + Err(e) => { + eprintln!("Failed to create log file: {}", e); + (None, None) + } + } + } + + fn log(&mut self, level: LogLevel, module: &str, message: &str) { + if level < self.config.min_level { + return; + } + + let timestamp = Local::now(); + let formatted = self.format_message(level, module, message, ×tamp); + + // Console output + if self.config.console_output { + if self.config.use_colors { + let reset = "\x1b[0m"; + eprintln!( + "{}[{}] [{}] [{}]{} {}", + level.color_code(), + timestamp.format("%H:%M:%S%.3f"), + level.as_str(), + module, + reset, + message + ); + } else { + eprintln!("{}", formatted); + } + } + + // File output + if self.config.file_output { + if let Some(ref mut file) = self.log_file { + let _ = writeln!(file, "{}", formatted); + let _ = file.flush(); + } + } + } + + fn format_message( + &self, + level: LogLevel, + module: &str, + message: &str, + timestamp: &DateTime, + ) -> String { + format!( + "[{}] [{}] [{}] {}", + timestamp.format("%Y-%m-%d %H:%M:%S%.3f"), + level.as_str(), + module, + message + ) + } +} + +/// Global logger instance +static LOGGER: Lazy> = Lazy::new(|| Mutex::new(Logger::new())); + +/// Get the log directory path +pub fn get_log_dir() -> PathBuf { + get_cache_dir(config::app::NAME).join("logs") +} + +/// Get the current log file path (if any) +pub fn get_current_log_path() -> Option { + LOGGER.lock().ok()?.log_path.clone() +} + +/// Log a debug message +pub fn debug(module: &str, message: &str) { + if let Ok(mut logger) = LOGGER.lock() { + logger.log(LogLevel::Debug, module, message); + } +} + +/// Log an info message +pub fn info(module: &str, message: &str) { + if let Ok(mut logger) = LOGGER.lock() { + logger.log(LogLevel::Info, module, message); + } +} + +/// Log a warning message +pub fn warn(module: &str, message: &str) { + if let Ok(mut logger) = LOGGER.lock() { + logger.log(LogLevel::Warn, module, message); + } +} + +/// Log an error message +pub fn error(module: &str, message: &str) { + if let Ok(mut logger) = LOGGER.lock() { + logger.log(LogLevel::Error, module, message); + } +} + +/// Log a message with format arguments (debug level) +#[macro_export] +macro_rules! log_debug { + ($module:expr, $($arg:tt)*) => { + $crate::logging::debug($module, &format!($($arg)*)) + }; +} + +/// Log a message with format arguments (info level) +#[macro_export] +macro_rules! log_info { + ($module:expr, $($arg:tt)*) => { + $crate::logging::info($module, &format!($($arg)*)) + }; +} + +/// Log a message with format arguments (warn level) +#[macro_export] +macro_rules! log_warn { + ($module:expr, $($arg:tt)*) => { + $crate::logging::warn($module, &format!($($arg)*)) + }; +} + +/// Log a message with format arguments (error level) +#[macro_export] +macro_rules! log_error { + ($module:expr, $($arg:tt)*) => { + $crate::logging::error($module, &format!($($arg)*)) + }; +} + +/// Clean up old log files, keeping only the most recent ones +pub fn cleanup_old_logs(keep_count: usize) -> Result { + let log_dir = get_log_dir(); + + if !log_dir.exists() { + return Ok(0); + } + + let mut log_files: Vec<_> = fs::read_dir(&log_dir) + .map_err(|e| format!("Failed to read log directory: {}", e))? + .filter_map(|entry| entry.ok()) + .filter(|entry| { + entry.path().extension().map_or(false, |ext| ext == "log") + }) + .collect(); + + // Sort by modification time (newest first) + log_files.sort_by(|a, b| { + let a_time = a.metadata().and_then(|m| m.modified()).ok(); + let b_time = b.metadata().and_then(|m| m.modified()).ok(); + b_time.cmp(&a_time) + }); + + let mut deleted = 0; + for entry in log_files.into_iter().skip(keep_count) { + if fs::remove_file(entry.path()).is_ok() { + deleted += 1; + } + } + + Ok(deleted) +} + +/// Get total size of log files in bytes +#[allow(dead_code)] +pub fn get_logs_size() -> u64 { + let log_dir = get_log_dir(); + + if !log_dir.exists() { + return 0; + } + + fs::read_dir(&log_dir) + .map(|entries| { + entries + .filter_map(|entry| entry.ok()) + .filter_map(|entry| entry.metadata().ok()) + .map(|meta| meta.len()) + .sum() + }) + .unwrap_or(0) +} + +/// Initialize the logger (call at application startup) +pub fn init() { + // Force initialization of the lazy static + drop(LOGGER.lock()); + + info("logger", "Armbian Imager logging initialized"); + + if let Some(path) = get_current_log_path() { + info("logger", &format!("Log file: {}", path.display())); + } + + // Clean up old logs (keep last 10) + match cleanup_old_logs(10) { + Ok(deleted) if deleted > 0 => { + info("logger", &format!("Cleaned up {} old log files", deleted)); + } + Err(e) => { + warn("logger", &format!("Failed to cleanup old logs: {}", e)); + } + _ => {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_log_level_ordering() { + assert!(LogLevel::Debug < LogLevel::Info); + assert!(LogLevel::Info < LogLevel::Warn); + assert!(LogLevel::Warn < LogLevel::Error); + } + + #[test] + fn test_log_dir() { + let log_dir = get_log_dir(); + assert!(log_dir.to_string_lossy().contains("armbian-imager")); + assert!(log_dir.to_string_lossy().contains("logs")); + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..3a57659 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,81 @@ +//! Armbian Imager - Flash Armbian OS images to SD cards and USB drives +//! +//! A cross-platform Tauri application for downloading and flashing +//! Armbian images to removable media. + +// Prevents additional console window on Windows in release +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +mod commands; +mod config; +mod decompress; +mod devices; +mod download; +mod flash; +mod images; +mod logging; +mod utils; + +use commands::AppState; +#[cfg(debug_assertions)] +use tauri::Manager; + +use crate::utils::get_cache_dir; + +/// Clean up cached images from previous sessions +fn cleanup_image_cache() { + let images_dir = get_cache_dir(config::app::NAME).join("images"); + + if images_dir.exists() { + if let Ok(entries) = std::fs::read_dir(&images_dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_file() { + let _ = std::fs::remove_file(&path); + } + } + } + } +} + +fn main() { + // Initialize logging system + logging::init(); + + // Clean up any leftover images from previous sessions + cleanup_image_cache(); + log_info!("main", "Starting Armbian Imager"); + + tauri::Builder::default() + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_dialog::init()) + .manage(AppState::default()) + .invoke_handler(tauri::generate_handler![ + commands::board_queries::get_boards, + commands::board_queries::get_images_for_board, + commands::board_queries::get_block_devices, + commands::scraping::get_board_image_url, + commands::operations::request_write_authorization, + commands::operations::download_image, + commands::operations::flash_image, + commands::operations::delete_downloaded_image, + commands::progress::cancel_operation, + commands::progress::get_download_progress, + commands::progress::get_flash_progress, + commands::custom_image::select_custom_image, + commands::custom_image::check_needs_decompression, + commands::custom_image::decompress_custom_image, + ]) + .setup(|app| { + #[cfg(debug_assertions)] + { + if let Some(window) = app.get_webview_window("main") { + let _ = window.open_devtools(); + } + } + let _ = app; // Suppress unused warning in release + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/utils/format.rs b/src-tauri/src/utils/format.rs new file mode 100644 index 0000000..a0e6dd3 --- /dev/null +++ b/src-tauri/src/utils/format.rs @@ -0,0 +1,59 @@ +//! Formatting utilities for human-readable output +//! +//! Provides consistent formatting functions for the application. + +/// Format bytes into human-readable size string (e.g., "1.5 GB", "256 MB") +pub fn format_size(bytes: u64) -> String { + const KB: u64 = 1024; + const MB: u64 = KB * 1024; + const GB: u64 = MB * 1024; + const TB: u64 = GB * 1024; + + if bytes >= TB { + format!("{:.1} TB", bytes as f64 / TB as f64) + } else if bytes >= GB { + format!("{:.1} GB", bytes as f64 / GB as f64) + } else if bytes >= MB { + format!("{:.0} MB", bytes as f64 / MB as f64) + } else if bytes >= KB { + format!("{:.0} KB", bytes as f64 / KB as f64) + } else { + format!("{} B", bytes) + } +} + +/// Normalize a slug by replacing non-alphanumeric chars with hyphens +/// and collapsing multiple hyphens into one +pub fn normalize_slug(slug: &str) -> String { + slug.to_lowercase() + .chars() + .map(|c| if c.is_alphanumeric() { c } else { '-' }) + .collect::() + .split('-') + .filter(|s| !s.is_empty()) + .collect::>() + .join("-") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_size() { + assert_eq!(format_size(0), "0 B"); + assert_eq!(format_size(512), "512 B"); + assert_eq!(format_size(1024), "1 KB"); + assert_eq!(format_size(1536), "2 KB"); + assert_eq!(format_size(1048576), "1 MB"); + assert_eq!(format_size(1073741824), "1.0 GB"); + assert_eq!(format_size(1610612736), "1.5 GB"); + } + + #[test] + fn test_normalize_slug() { + assert_eq!(normalize_slug("Orange-Pi-5"), "orange-pi-5"); + assert_eq!(normalize_slug("rock__pi__4"), "rock-pi-4"); + assert_eq!(normalize_slug("Banana PI M5"), "banana-pi-m5"); + } +} diff --git a/src-tauri/src/utils/http.rs b/src-tauri/src/utils/http.rs new file mode 100644 index 0000000..606eaad --- /dev/null +++ b/src-tauri/src/utils/http.rs @@ -0,0 +1,31 @@ +//! HTTP client utilities +//! +//! Provides centralized HTTP client creation with consistent configuration. + +use crate::config; +use reqwest::Client; +use std::time::Duration; + +/// Create an HTTP client with a short timeout for quick requests +/// +/// Useful for requests like fetching board info pages where we don't +/// want to wait too long. +pub fn create_short_timeout_client() -> Result { + Client::builder() + .user_agent(config::app::USER_AGENT) + .timeout(Duration::from_secs(config::http::SHORT_TIMEOUT_SECS)) + .connect_timeout(Duration::from_secs(config::http::SHORT_TIMEOUT_SECS)) + .build() + .map_err(|e| format!("Failed to create HTTP client: {}", e)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_short_timeout_client() { + let client = create_short_timeout_client(); + assert!(client.is_ok()); + } +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs new file mode 100644 index 0000000..1ee66ee --- /dev/null +++ b/src-tauri/src/utils/mod.rs @@ -0,0 +1,12 @@ +//! Utility functions shared across the application +//! +//! This module contains common helpers for formatting, system info, +//! HTTP client creation, and path management. + +mod format; +mod http; +mod system; + +pub use format::*; +pub use http::*; +pub use system::*; diff --git a/src-tauri/src/utils/system.rs b/src-tauri/src/utils/system.rs new file mode 100644 index 0000000..a77d072 --- /dev/null +++ b/src-tauri/src/utils/system.rs @@ -0,0 +1,97 @@ +//! System utilities for CPU, paths, and platform detection +//! +//! Provides system-level utilities for cross-platform functionality. + +use std::path::PathBuf; + +/// Get the number of CPU cores available on the system +fn get_cpu_cores() -> usize { + std::thread::available_parallelism() + .map(|p| p.get()) + .unwrap_or(2) +} + +/// Get recommended thread count for CPU-intensive operations +/// Uses half of available cores to avoid saturating the system +pub fn get_recommended_threads() -> usize { + std::cmp::max(1, get_cpu_cores() / 2) +} + +/// Find a binary in common system locations +/// Returns the first path that exists +pub fn find_binary(name: &str) -> Option { + let paths = get_binary_search_paths(name); + + for path in paths { + if path.exists() { + return Some(path); + } + } + None +} + +/// Get platform-specific search paths for a binary +fn get_binary_search_paths(name: &str) -> Vec { + #[cfg(target_os = "macos")] + { + vec![ + PathBuf::from(format!("/opt/homebrew/bin/{}", name)), // macOS ARM + PathBuf::from(format!("/usr/local/bin/{}", name)), // macOS Intel + PathBuf::from(format!("/usr/bin/{}", name)), + ] + } + + #[cfg(target_os = "linux")] + { + vec![ + PathBuf::from(format!("/usr/bin/{}", name)), + PathBuf::from(format!("/bin/{}", name)), + PathBuf::from(format!("/usr/local/bin/{}", name)), + ] + } + + #[cfg(target_os = "windows")] + { + // On Windows, rely on PATH or specific install locations + vec![ + PathBuf::from(format!("C:\\Program Files\\{0}\\{0}.exe", name)), + PathBuf::from(format!("C:\\Program Files (x86)\\{0}\\{0}.exe", name)), + ] + } + + #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] + { + vec![PathBuf::from(format!("/usr/bin/{}", name))] + } +} + +/// Get the cache directory for the application +pub fn get_cache_dir(app_name: &str) -> PathBuf { + dirs::cache_dir() + .unwrap_or_else(|| PathBuf::from("/tmp")) + .join(app_name) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get_cpu_cores() { + let cores = get_cpu_cores(); + assert!(cores >= 1); + } + + #[test] + fn test_get_recommended_threads() { + let threads = get_recommended_threads(); + assert!(threads >= 1); + assert!(threads <= get_cpu_cores()); + } + + #[test] + fn test_get_cache_dir() { + let cache = get_cache_dir("test-app"); + assert!(cache.to_string_lossy().contains("test-app")); + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..87e51bc --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,78 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "Armbian Imager", + "version": "1.0.0", + "identifier": "com.armbian.imager", + "build": { + "beforeBuildCommand": "npm run build", + "beforeDevCommand": "npm run dev", + "devUrl": "http://localhost:5173", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "label": "main", + "title": "Armbian Imager", + "width": 1100, + "height": 700, + "resizable": true, + "minWidth": 1050, + "minHeight": 575, + "center": true + } + ], + "security": { + "csp": null + }, + "macOSPrivateApi": true + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "category": "Utility", + "shortDescription": "Flash Armbian OS images", + "longDescription": "Armbian Imager is a utility for flashing Armbian OS images to SD cards and USB drives for single board computers.", + "macOS": { + "entitlements": "./entitlements.plist", + "minimumSystemVersion": "10.15", + "dmg": { + "background": "./dmg-background.png", + "windowSize": { + "width": 540, + "height": 380 + }, + "appPosition": { + "x": 140, + "y": 170 + }, + "applicationFolderPosition": { + "x": 400, + "y": 170 + } + } + }, + "windows": { + "certificateThumbprint": null, + "digestAlgorithm": "sha256", + "timestampUrl": "" + }, + "linux": { + "appimage": { + "bundleMediaFramework": false + } + } + }, + "plugins": { + "shell": { + "open": true + } + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..c2977b6 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,177 @@ +import { useState } from 'react'; +import { Header } from './components/Header'; +import { HomePage } from './components/HomePage'; +import { ManufacturerModal, type Manufacturer } from './components/ManufacturerModal'; +import { BoardModal } from './components/BoardModal'; +import { ImageModal } from './components/ImageModal'; +import { DeviceModal } from './components/DeviceModal'; +import { FlashProgress } from './components/FlashProgress'; +import { selectCustomImage } from './hooks/useTauri'; +import type { BoardInfo, ImageInfo, BlockDevice, ModalType } from './types'; +import './styles/index.css'; + +/** Selection step in the wizard flow */ +type SelectionStep = 'manufacturer' | 'board' | 'image' | 'device'; + +function App() { + const [isFlashing, setIsFlashing] = useState(false); + const [activeModal, setActiveModal] = useState('none'); + const [selectedManufacturer, setSelectedManufacturer] = useState(null); + const [selectedBoard, setSelectedBoard] = useState(null); + const [selectedImage, setSelectedImage] = useState(null); + const [selectedDevice, setSelectedDevice] = useState(null); + + /** + * Reset selections from a given step onwards. + * When user changes a selection, downstream selections become invalid. + */ + function resetSelectionsFrom(step: SelectionStep) { + const steps: SelectionStep[] = ['manufacturer', 'board', 'image', 'device']; + const stepIndex = steps.indexOf(step); + + if (stepIndex <= 0) setSelectedManufacturer(null); + if (stepIndex <= 1) setSelectedBoard(null); + if (stepIndex <= 2) setSelectedImage(null); + if (stepIndex <= 3) setSelectedDevice(null); + } + + function handleManufacturerSelect(manufacturer: Manufacturer) { + setSelectedManufacturer(manufacturer); + resetSelectionsFrom('board'); // Reset board, image, device + setActiveModal('none'); + } + + function handleBoardSelect(board: BoardInfo) { + setSelectedBoard(board); + resetSelectionsFrom('image'); // Reset image, device + setActiveModal('none'); + } + + function handleImageSelect(image: ImageInfo) { + setSelectedImage(image); + resetSelectionsFrom('device'); // Reset device + setActiveModal('none'); + } + + function handleDeviceSelect(device: BlockDevice) { + setSelectedDevice(device); + setActiveModal('none'); + } + + async function handleCustomImage() { + try { + const result = await selectCustomImage(); + if (result) { + // Create a custom ImageInfo object + const customImage: ImageInfo = { + armbian_version: 'Custom', + distro_release: result.name, + kernel_branch: '', + image_variant: 'custom', + preinstalled_application: '', + promoted: false, + file_url: '', + file_size: result.size, + download_repository: 'local', + is_custom: true, + custom_path: result.path, + }; + // Reset selections and set custom board/image for display purposes + resetSelectionsFrom('manufacturer'); + setSelectedBoard({ + slug: 'custom', + name: 'Custom Image', + image_count: 1, + has_promoted: false, + }); + setSelectedImage(customImage); + } + } catch (err) { + console.error('Failed to select custom image:', err); + } + } + + function handleWrite() { + // For custom images, we only need the image and device + const hasValidImage = selectedImage && (selectedImage.is_custom || selectedBoard); + if (hasValidImage && selectedDevice) { + setIsFlashing(true); + } + } + + function handleComplete() { + setIsFlashing(false); + resetSelectionsFrom('manufacturer'); // Reset all selections + } + + function handleBackFromFlash() { + setIsFlashing(false); + } + + return ( +
+
+ +
+ {!isFlashing ? ( + setActiveModal('manufacturer')} + onChooseBoard={() => setActiveModal('board')} + onChooseImage={() => setActiveModal('image')} + onChooseDevice={() => setActiveModal('device')} + onChooseCustomImage={handleCustomImage} + onWrite={handleWrite} + /> + ) : ( + selectedBoard && selectedImage && selectedDevice && ( + + ) + )} +
+ + {/* Modals */} + setActiveModal('none')} + onSelect={handleManufacturerSelect} + /> + + setActiveModal('none')} + onSelect={handleBoardSelect} + manufacturer={selectedManufacturer} + /> + + setActiveModal('none')} + onSelect={handleImageSelect} + board={selectedBoard} + /> + + setActiveModal('none')} + onSelect={handleDeviceSelect} + /> +
+ ); +} + +export default App; diff --git a/src/assets/armbian-icon.png b/src/assets/armbian-icon.png new file mode 100644 index 0000000..b4183f3 Binary files /dev/null and b/src/assets/armbian-icon.png differ diff --git a/src/assets/armbian-logo.png b/src/assets/armbian-logo.png new file mode 100644 index 0000000..35f51d2 Binary files /dev/null and b/src/assets/armbian-logo.png differ diff --git a/src/assets/os-logos/debian.svg b/src/assets/os-logos/debian.svg new file mode 100644 index 0000000..96dfb32 --- /dev/null +++ b/src/assets/os-logos/debian.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/os-logos/homeassistant.png b/src/assets/os-logos/homeassistant.png new file mode 100644 index 0000000..64e156f Binary files /dev/null and b/src/assets/os-logos/homeassistant.png differ diff --git a/src/assets/os-logos/index.ts b/src/assets/os-logos/index.ts new file mode 100644 index 0000000..43e1ee1 --- /dev/null +++ b/src/assets/os-logos/index.ts @@ -0,0 +1,83 @@ +// OS Logos +import debianLogo from './debian.svg'; +import ubuntuLogo from './ubuntu.png'; +import armbianLogo from '../armbian-logo.png'; + +// App Logos +import homeassistantLogo from './homeassistant.png'; +import openmediavaultLogo from './openmediavault.jpeg'; + +export const osLogos: Record = { + debian: debianLogo, + ubuntu: ubuntuLogo, + armbian: armbianLogo, +}; + +export const appLogos: Record = { + homeassistant: homeassistantLogo, + 'home assistant': homeassistantLogo, + openmediavault: openmediavaultLogo, + omv: openmediavaultLogo, +}; + +// Debian codenames +const debianCodenames = ['bookworm', 'bullseye', 'trixie', 'sid', 'buster', 'stretch']; +// Ubuntu codenames +const ubuntuCodenames = ['jammy', 'noble', 'focal', 'kinetic', 'lunar', 'mantic']; + +/** + * Get the appropriate logo for an image based on distro release and preinstalled app + * Priority: preinstalled app logo > OS logo based on distro > Armbian default + * For custom images, also checks the filename for OS/app keywords + */ +export function getImageLogo(distroRelease: string, preinstalledApp?: string): string { + // First check if there's a preinstalled app with a logo + if (preinstalledApp) { + const appKey = preinstalledApp.toLowerCase(); + for (const [key, logo] of Object.entries(appLogos)) { + if (appKey.includes(key)) { + return logo; + } + } + } + + // Check OS based on distro release (also works for custom image filenames) + const distro = distroRelease.toLowerCase(); + + // Check for apps in filename (for custom images) + for (const [key, logo] of Object.entries(appLogos)) { + if (distro.includes(key)) { + return logo; + } + } + + // Check Ubuntu codenames + if (distro.includes('ubuntu') || ubuntuCodenames.some(c => distro.includes(c))) { + return osLogos.ubuntu; + } + + // Check Debian codenames + if (distro.includes('debian') || debianCodenames.some(c => distro.includes(c))) { + return osLogos.debian; + } + + // Default to Armbian + return osLogos.armbian; +} + +/** + * Get the OS name from distro release + */ +export function getOsName(distroRelease: string): string { + const distro = distroRelease.toLowerCase(); + + if (distro.includes('ubuntu') || ubuntuCodenames.some(c => distro.includes(c))) { + return 'Ubuntu'; + } + + if (distro.includes('debian') || debianCodenames.some(c => distro.includes(c))) { + return 'Debian'; + } + + return 'Armbian'; +} diff --git a/src/assets/os-logos/openmediavault.jpeg b/src/assets/os-logos/openmediavault.jpeg new file mode 100644 index 0000000..76645e9 Binary files /dev/null and b/src/assets/os-logos/openmediavault.jpeg differ diff --git a/src/assets/os-logos/ubuntu.png b/src/assets/os-logos/ubuntu.png new file mode 100644 index 0000000..0c3778a Binary files /dev/null and b/src/assets/os-logos/ubuntu.png differ diff --git a/src/components/BoardModal.tsx b/src/components/BoardModal.tsx new file mode 100644 index 0000000..254d79f --- /dev/null +++ b/src/components/BoardModal.tsx @@ -0,0 +1,156 @@ +import { useState, useEffect, useMemo, useCallback, useRef } from 'react'; +import { Search, Star } from 'lucide-react'; +import { Modal } from './Modal'; +import type { BoardInfo, Manufacturer } from '../types'; +import { getBoards, getBoardImageUrl } from '../hooks/useTauri'; +import { useAsyncDataWhen } from '../hooks/useAsyncData'; +import { getManufacturer } from '../config'; + +function getBoardColor(name: string): string { + const colors = ['#3baed4', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4', '#84cc16', '#f97316']; + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + return colors[Math.abs(hash) % colors.length]; +} + +function getBoardInitials(name: string): string { + const words = name.split(/[\s-]+/).filter(w => w.length > 0); + if (words.length >= 2) { + return (words[0][0] + words[1][0]).toUpperCase(); + } + return name.substring(0, 2).toUpperCase(); +} + +interface BoardModalProps { + isOpen: boolean; + onClose: () => void; + onSelect: (board: BoardInfo) => void; + manufacturer: Manufacturer | null; +} + +export function BoardModal({ isOpen, onClose, onSelect, manufacturer }: BoardModalProps) { + const [search, setSearch] = useState(''); + const [boardImages, setBoardImages] = useState>({}); + const loadingImagesRef = useRef>(new Set()); + const loadedRef = useRef>(new Set()); + + // Use hook for async data fetching + const { data: boards, loading, error, reload } = useAsyncDataWhen( + isOpen, + () => getBoards(), + [isOpen] + ); + + useEffect(() => { + setSearch(''); + }, [manufacturer]); + + const loadBoardImage = useCallback(async (slug: string) => { + if (loadedRef.current.has(slug) || loadingImagesRef.current.has(slug)) return; + loadingImagesRef.current.add(slug); + try { + const url = await getBoardImageUrl(slug); + loadedRef.current.add(slug); + setBoardImages((prev) => ({ ...prev, [slug]: url })); + } catch { + loadedRef.current.add(slug); + setBoardImages((prev) => ({ ...prev, [slug]: null })); + } finally { + loadingImagesRef.current.delete(slug); + } + }, []); + + const filteredBoards = useMemo(() => { + if (!manufacturer || !boards) return []; + const searchLower = search.toLowerCase(); + return boards + .filter((board) => { + const mfr = getManufacturer(board.slug, board.name); + if (mfr !== manufacturer.id) return false; + return board.name.toLowerCase().includes(searchLower) || + board.slug.toLowerCase().includes(searchLower); + }) + .sort((a, b) => { + if (a.has_promoted && !b.has_promoted) return -1; + if (!a.has_promoted && b.has_promoted) return 1; + return a.name.localeCompare(b.name); + }); + }, [boards, manufacturer, search]); + + useEffect(() => { + if (isOpen && filteredBoards.length > 0) { + const loadImages = async () => { + for (let i = 0; i < Math.min(filteredBoards.length, 10); i += 5) { + const batch = filteredBoards.slice(i, i + 5); + await Promise.all(batch.map(board => loadBoardImage(board.slug))); + } + }; + loadImages(); + } + }, [isOpen, filteredBoards, loadBoardImage]); + + const title = manufacturer ? `${manufacturer.name} Boards` : 'Select Board'; + + return ( + +
+
+ + setSearch(e.target.value)} + className="search-input" + autoFocus + /> +
+
+ + {loading ? ( +
+
+

Loading...

+
+ ) : error ? ( +
+

{error}

+ +
+ ) : ( +
+ {filteredBoards.map((board) => ( + + ))} + {filteredBoards.length === 0 && ( +
+

No boards found

+
+ )} +
+ )} + + ); +} diff --git a/src/components/DeviceModal.tsx b/src/components/DeviceModal.tsx new file mode 100644 index 0000000..cbc1354 --- /dev/null +++ b/src/components/DeviceModal.tsx @@ -0,0 +1,152 @@ +import { useState, useMemo } from 'react'; +import { HardDrive, RefreshCw, AlertTriangle, Usb, Shield } from 'lucide-react'; +import { Modal } from './Modal'; +import type { BlockDevice } from '../types'; +import { getBlockDevices } from '../hooks/useTauri'; +import { useAsyncDataWhen } from '../hooks/useAsyncData'; + +/** Sort devices: removable first, then by size */ +function sortDevices(devices: BlockDevice[]): BlockDevice[] { + return [...devices].sort((a, b) => { + if (a.is_system !== b.is_system) return a.is_system ? 1 : -1; + if (a.is_removable !== b.is_removable) return a.is_removable ? -1 : 1; + return b.size - a.size; + }); +} + +interface DeviceModalProps { + isOpen: boolean; + onClose: () => void; + onSelect: (device: BlockDevice) => void; +} + +export function DeviceModal({ isOpen, onClose, onSelect }: DeviceModalProps) { + const [selectedDevice, setSelectedDevice] = useState(null); + const [showConfirm, setShowConfirm] = useState(false); + + // Use hook for async data fetching - always refresh when modal opens + const { data: rawDevices, loading, error, reload } = useAsyncDataWhen( + isOpen, + () => getBlockDevices(), + [isOpen] + ); + + // Sort devices after fetching + const devices = useMemo(() => { + return rawDevices ? sortDevices(rawDevices) : []; + }, [rawDevices]); + + function handleDeviceClick(device: BlockDevice) { + if (device.is_system) return; + setSelectedDevice(device); + setShowConfirm(true); + } + + function handleConfirm() { + if (selectedDevice && !selectedDevice.is_system) { + onSelect(selectedDevice); + setShowConfirm(false); + } + } + + return ( + <> + +
+ + All data on selected device will be erased +
+ + {loading ? ( +
+
+

Scanning devices...

+
+ ) : error ? ( +
+

{error}

+ +
+ ) : devices.length === 0 ? ( +
+ +

No devices found

+

+ Insert an SD card or USB drive +

+ +
+ ) : ( + <> +
+ {devices.map((device) => ( + + ))} +
+
+ +
+ + )} + + + {/* Confirmation Dialog */} + {showConfirm && selectedDevice && !selectedDevice.is_system && ( +
setShowConfirm(false)}> +
e.stopPropagation()}> +
+ +
+

Confirm Selection

+

You are about to write to:

+
+ {selectedDevice.model || selectedDevice.name} + {selectedDevice.path} ({selectedDevice.size_formatted}) +
+

ALL DATA WILL BE PERMANENTLY ERASED

+
+ + +
+
+
+ )} + + ); +} diff --git a/src/components/FlashProgress/FlashActions.tsx b/src/components/FlashProgress/FlashActions.tsx new file mode 100644 index 0000000..58fbdd6 --- /dev/null +++ b/src/components/FlashProgress/FlashActions.tsx @@ -0,0 +1,53 @@ +import { RotateCcw } from 'lucide-react'; +import type { FlashStage } from './FlashStageIcon'; + +interface FlashActionsProps { + stage: FlashStage; + onComplete: () => void; + onBack: () => void; + onRetry: () => void; + onCancel: () => void; +} + +export function FlashActions({ + stage, + onComplete, + onBack, + onRetry, + onCancel, +}: FlashActionsProps) { + if (stage === 'complete') { + return ( +
+ + +
+ ); + } + + if (stage === 'error') { + return ( +
+ + +
+ ); + } + + return ( +
+ +
+ ); +} diff --git a/src/components/FlashProgress/FlashStageIcon.tsx b/src/components/FlashProgress/FlashStageIcon.tsx new file mode 100644 index 0000000..73bfb49 --- /dev/null +++ b/src/components/FlashProgress/FlashStageIcon.tsx @@ -0,0 +1,61 @@ +import { + Download, + HardDrive, + CheckCircle, + XCircle, + Check, + Archive, + Shield, +} from 'lucide-react'; + +export type FlashStage = + | 'authorizing' + | 'downloading' + | 'decompressing' + | 'flashing' + | 'verifying' + | 'complete' + | 'error'; + +interface FlashStageIconProps { + stage: FlashStage; + size?: number; +} + +export function FlashStageIcon({ stage, size = 32 }: FlashStageIconProps) { + switch (stage) { + case 'authorizing': + return ; + case 'downloading': + return ; + case 'decompressing': + return ; + case 'flashing': + return ; + case 'verifying': + return ; + case 'complete': + return ; + case 'error': + return ; + } +} + +export function getStageText(stage: FlashStage): string { + switch (stage) { + case 'authorizing': + return 'Requesting authorization...'; + case 'downloading': + return 'Downloading image...'; + case 'decompressing': + return 'Decompressing image...'; + case 'flashing': + return 'Writing image to device...'; + case 'verifying': + return 'Verifying written data...'; + case 'complete': + return 'Flash complete!'; + case 'error': + return 'An error occurred'; + } +} diff --git a/src/components/FlashProgress/index.tsx b/src/components/FlashProgress/index.tsx new file mode 100644 index 0000000..1008e65 --- /dev/null +++ b/src/components/FlashProgress/index.tsx @@ -0,0 +1,354 @@ +import { useState, useEffect, useRef } from 'react'; +import { HardDrive, AlertTriangle, Cpu } from 'lucide-react'; +import type { BoardInfo, ImageInfo, BlockDevice } from '../../types'; +import { getImageLogo, getOsName } from '../../assets/os-logos'; +import { + downloadImage, + flashImage, + getDownloadProgress, + getFlashProgress, + cancelOperation, + getBoardImageUrl, + deleteDownloadedImage, + requestWriteAuthorization, + checkNeedsDecompression, + decompressCustomImage, +} from '../../hooks/useTauri'; +import { FlashStageIcon, getStageText } from './FlashStageIcon'; +import { FlashActions } from './FlashActions'; +import type { FlashStage } from './FlashStageIcon'; + +interface FlashProgressProps { + board: BoardInfo; + image: ImageInfo; + device: BlockDevice; + onComplete: () => void; + onBack: () => void; +} + +export function FlashProgress({ + board, + image, + device, + onComplete, + onBack, +}: FlashProgressProps) { + const [stage, setStage] = useState('authorizing'); + const [progress, setProgress] = useState(0); + const [error, setError] = useState(null); + const [boardImageUrl, setBoardImageUrl] = useState(null); + const [imagePath, setImagePath] = useState(null); + const intervalRef = useRef(null); + const maxProgressRef = useRef(0); + const hasStartedRef = useRef(false); + + // Cleanup downloaded image file (skip for custom images) + async function cleanupImage(path: string | null) { + if (image.is_custom) return; + if (path) { + try { + await deleteDownloadedImage(path); + } catch { + // Ignore cleanup errors + } + } + } + + useEffect(() => { + if (hasStartedRef.current) return; + hasStartedRef.current = true; + + loadBoardImage(); + handleAuthorization(); + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + }; + }, []); + + async function loadBoardImage() { + try { + const url = await getBoardImageUrl(board.slug); + setBoardImageUrl(url); + } catch { + // Ignore + } + } + + async function handleAuthorization() { + setStage('authorizing'); + setProgress(0); + setError(null); + + try { + const authorized = await requestWriteAuthorization(device.path); + if (!authorized) { + setError('Authorization cancelled by user'); + setStage('error'); + return; + } + + if (image.is_custom && image.custom_path) { + await handleCustomImage(image.custom_path); + } else { + startDownload(); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Authorization failed'); + setStage('error'); + } + } + + async function handleCustomImage(customPath: string) { + try { + const needsDecompress = await checkNeedsDecompression(customPath); + + if (needsDecompress) { + setStage('decompressing'); + setProgress(0); + const decompressedPath = await decompressCustomImage(customPath); + setImagePath(decompressedPath); + startFlash(decompressedPath); + } else { + setImagePath(customPath); + startFlash(customPath); + } + } catch (err) { + setError(err instanceof Error ? err.message : 'Decompression failed'); + setStage('error'); + } + } + + async function startDownload() { + setStage('downloading'); + setProgress(0); + setError(null); + maxProgressRef.current = 0; + + intervalRef.current = window.setInterval(async () => { + try { + const prog = await getDownloadProgress(); + + if (prog.is_decompressing && stage !== 'decompressing') { + setStage('decompressing'); + maxProgressRef.current = 0; + setProgress(0); + } + + if (!prog.is_decompressing) { + const newProgress = prog.progress_percent; + if (newProgress >= maxProgressRef.current) { + maxProgressRef.current = newProgress; + setProgress(newProgress); + } + } + + if (prog.error) { + setError(prog.error); + setStage('error'); + if (intervalRef.current) clearInterval(intervalRef.current); + } + } catch { + // Ignore polling errors + } + }, 250); + + try { + const path = await downloadImage(image.file_url); + setImagePath(path); + if (intervalRef.current) clearInterval(intervalRef.current); + startFlash(path); + } catch (err) { + if (intervalRef.current) clearInterval(intervalRef.current); + setError(err instanceof Error ? err.message : 'Download failed'); + setStage('error'); + } + } + + async function startFlash(path: string) { + setStage('flashing'); + setProgress(0); + maxProgressRef.current = 0; + + intervalRef.current = window.setInterval(async () => { + try { + const prog = await getFlashProgress(); + if (prog.is_verifying) { + setStage('verifying'); + if (maxProgressRef.current > 50) { + maxProgressRef.current = 0; + } + } + if (prog.progress_percent >= maxProgressRef.current) { + maxProgressRef.current = prog.progress_percent; + setProgress(prog.progress_percent); + } + if (prog.error) { + setError(prog.error); + setStage('error'); + if (intervalRef.current) clearInterval(intervalRef.current); + } + } catch { + // Ignore polling errors + } + }, 250); + + try { + await flashImage(path, device.path, true); + if (intervalRef.current) clearInterval(intervalRef.current); + setStage('complete'); + setProgress(100); + } catch (err) { + if (intervalRef.current) clearInterval(intervalRef.current); + setError(err instanceof Error ? err.message : 'Flash failed'); + setStage('error'); + } + } + + async function handleCancel() { + try { + await cancelOperation(); + if (intervalRef.current) clearInterval(intervalRef.current); + await cleanupImage(imagePath); + onBack(); + } catch { + // Ignore + } + } + + function handleRetry() { + setError(null); + if (imagePath) { + startFlash(imagePath); + } else if (image.is_custom && image.custom_path) { + startFlash(image.custom_path); + } else { + startDownload(); + } + } + + async function handleBack() { + await cleanupImage(imagePath); + onBack(); + } + + function getImageDisplayText(): string { + if (image.is_custom) { + const fileName = image.distro_release; + if (fileName.length > 45) { + const extIndex = fileName.lastIndexOf('.'); + if (extIndex > 0) { + const extension = fileName.substring(extIndex); + return fileName.substring(0, 42 - extension.length) + '...' + extension; + } + return fileName.substring(0, 42) + '...'; + } + return fileName; + } + return `Armbian ${image.armbian_version} ${image.distro_release}`; + } + + const showHeader = stage !== 'authorizing' && stage !== 'error'; + + return ( +
+ {showHeader && ( +
+ {boardImageUrl ? ( + {board.name} + ) : ( +
+ +
+ )} +
+

{board.name}

+
+
+ {getOsName(image.distro_release)} + {getImageDisplayText()} +
+
+ + + {device.model || device.name} + + {device.size_formatted} +
+
+
+
+ )} + +
+ +

{getStageText(stage)}

+ + {stage !== 'complete' && + stage !== 'error' && + stage !== 'authorizing' && ( +
+
+
+
+ {stage !== 'decompressing' && ( + {progress.toFixed(0)}% + )} +
+ )} + + {stage === 'complete' && ( +

+ Your SD card is ready! You can safely remove the device and insert + it into your {board.name}. +

+ )} + + {error && ( +
+ + {error} +
+ )} + + +
+
+ ); +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..d133c3f --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,51 @@ +import { Check } from 'lucide-react'; +import armbianLogo from '../assets/armbian-logo.png'; +import type { BoardInfo, ImageInfo, BlockDevice } from '../types'; +import type { Manufacturer } from './ManufacturerModal'; + +interface HeaderProps { + selectedManufacturer?: Manufacturer | null; + selectedBoard?: BoardInfo | null; + selectedImage?: ImageInfo | null; + selectedDevice?: BlockDevice | null; +} + +export function Header({ + selectedManufacturer, + selectedBoard, + selectedImage, + selectedDevice, +}: HeaderProps) { + const isCustomImage = selectedImage?.is_custom; + + // For custom images, show different steps + const steps = isCustomImage + ? [ + { label: 'Image', completed: !!selectedImage }, + { label: 'Storage', completed: !!selectedDevice }, + ] + : [ + { label: 'Manufacturer', completed: !!selectedManufacturer }, + { label: 'Board', completed: !!selectedBoard }, + { label: 'OS', completed: !!selectedImage }, + { label: 'Storage', completed: !!selectedDevice }, + ]; + + return ( +
+
+ Armbian +
+
+ {steps.map((step, index) => ( +
+ + {step.completed ? : (index + 1)} + + {step.label} +
+ ))} +
+
+ ); +} diff --git a/src/components/HomePage.tsx b/src/components/HomePage.tsx new file mode 100644 index 0000000..2d6222c --- /dev/null +++ b/src/components/HomePage.tsx @@ -0,0 +1,137 @@ +import { Factory, Cpu, Database, HardDrive, FolderOpen } from 'lucide-react'; +import type { BoardInfo, ImageInfo, BlockDevice } from '../types'; +import type { Manufacturer } from './ManufacturerModal'; + +interface HomePageProps { + selectedManufacturer: Manufacturer | null; + selectedBoard: BoardInfo | null; + selectedImage: ImageInfo | null; + selectedDevice: BlockDevice | null; + onChooseManufacturer: () => void; + onChooseBoard: () => void; + onChooseImage: () => void; + onChooseDevice: () => void; + onChooseCustomImage: () => void; + onWrite: () => void; +} + +export function HomePage({ + selectedManufacturer, + selectedBoard, + selectedImage, + selectedDevice, + onChooseManufacturer, + onChooseBoard, + onChooseImage, + onChooseDevice, + onChooseCustomImage, + onWrite, +}: HomePageProps) { + const canWrite = selectedBoard && selectedImage && selectedDevice; + const isCustomImage = selectedImage?.is_custom; + + return ( +
+
+
+ Manufacturer + +
+ +
+ Board + +
+ +
+ Operating System + +
+ +
+ Storage + +
+
+ +
+ +
+ +
+ +
+
+ ); +} diff --git a/src/components/ImageModal.tsx b/src/components/ImageModal.tsx new file mode 100644 index 0000000..9bc3671 --- /dev/null +++ b/src/components/ImageModal.tsx @@ -0,0 +1,220 @@ +import { useState, useMemo } from 'react'; +import { Star, Download, Package, Monitor, Terminal, Zap } from 'lucide-react'; +import { Modal } from './Modal'; +import type { BoardInfo, ImageInfo, ImageFilterType } from '../types'; +import { getImagesForBoard } from '../hooks/useTauri'; +import { useAsyncDataWhen } from '../hooks/useAsyncData'; +import { + getOsInfo, + getAppInfo, + getDesktopEnv, + getKernelType, + DESKTOP_BADGES, + KERNEL_BADGES, + DESKTOP_ENVIRONMENTS, +} from '../config'; + +interface ImageModalProps { + isOpen: boolean; + onClose: () => void; + onSelect: (image: ImageInfo) => void; + board: BoardInfo | null; +} + +function formatSize(bytes: number): string { + if (bytes === 0) return 'Unknown'; + const gb = bytes / (1024 * 1024 * 1024); + if (gb >= 1) return `${gb.toFixed(1)} GB`; + const mb = bytes / (1024 * 1024); + return `${mb.toFixed(0)} MB`; +} + +/** + * Filter predicates - defined once, used for both checking availability and filtering + * Each predicate returns true if the image matches the filter criteria + */ +const IMAGE_FILTER_PREDICATES: Record, (img: ImageInfo) => boolean> = { + // Stable: from archive repository and not trunk version + stable: (img) => img.download_repository === 'archive' && !img.armbian_version.includes('trunk'), + // Nightly: trunk versions + nightly: (img) => img.armbian_version.includes('trunk'), + // Apps: has preinstalled application + apps: (img) => !!(img.preinstalled_application && img.preinstalled_application.length > 0), + // Barebone/Minimal: no desktop environment and no preinstalled apps + barebone: (img) => { + const variant = img.image_variant.toLowerCase(); + const hasDesktop = DESKTOP_ENVIRONMENTS.some(de => variant.includes(de)); + const hasApp = img.preinstalled_application && img.preinstalled_application.length > 0; + return !hasDesktop && !hasApp; + }, +}; + +/** Check if any images match a filter */ +function hasImagesForFilter(images: ImageInfo[], filter: Exclude): boolean { + return images.some(IMAGE_FILTER_PREDICATES[filter]); +} + +/** Apply filter to images */ +function applyFilter(images: ImageInfo[], filter: ImageFilterType): ImageInfo[] { + if (filter === 'all') return images; + return images.filter(IMAGE_FILTER_PREDICATES[filter]); +} + +export function ImageModal({ isOpen, onClose, onSelect, board }: ImageModalProps) { + const [filterType, setFilterType] = useState('all'); + + // Use hook for async data fetching + const { data: allImages, loading, error, reload } = useAsyncDataWhen( + isOpen && !!board, + () => getImagesForBoard(board!.slug, undefined, undefined, undefined, false), + [isOpen, board?.slug] + ); + + // Calculate available filters based on all images + const availableFilters = useMemo(() => { + if (!allImages) return { stable: false, nightly: false, apps: false, barebone: false }; + return { + stable: hasImagesForFilter(allImages, 'stable'), + nightly: hasImagesForFilter(allImages, 'nightly'), + apps: hasImagesForFilter(allImages, 'apps'), + barebone: hasImagesForFilter(allImages, 'barebone'), + }; + }, [allImages]); + + // Apply filter using useMemo + const filteredImages = useMemo(() => { + if (!allImages) return []; + return applyFilter(allImages, filterType); + }, [allImages, filterType]); + + const title = board ? `${board.name} - Select OS` : 'Select Operating System'; + + return ( + +
+ + {availableFilters.stable && ( + + )} + {availableFilters.nightly && ( + + )} + {availableFilters.apps && ( + + )} + {availableFilters.barebone && ( + + )} +
+ + {loading ? ( +
+
+

Loading images...

+
+ ) : error ? ( +
+

{error}

+ +
+ ) : filteredImages.length === 0 ? ( +
+ +

No images found

+ +
+ ) : ( +
+ {filteredImages.map((image, index) => { + const desktopEnv = getDesktopEnv(image.image_variant); + const kernelType = getKernelType(image.kernel_branch); + const osInfo = getOsInfo(image.distro_release); + const appInfo = getAppInfo(image.preinstalled_application); + + // Use app logo if available, otherwise use OS logo + const displayInfo = appInfo || osInfo; + + return ( + + ); + })} +
+ )} + + ); +} diff --git a/src/components/ManufacturerModal.tsx b/src/components/ManufacturerModal.tsx new file mode 100644 index 0000000..cc2e55e --- /dev/null +++ b/src/components/ManufacturerModal.tsx @@ -0,0 +1,145 @@ +import { useState, useMemo } from 'react'; +import { Search } from 'lucide-react'; +import { Modal } from './Modal'; +import type { BoardInfo, Manufacturer } from '../types'; +import { getBoards } from '../hooks/useTauri'; +import { useAsyncDataWhen } from '../hooks/useAsyncData'; +import { + MANUFACTURERS, + getManufacturer, + getManufacturerLogoUrl, + type ManufacturerConfig, +} from '../config'; + +// Re-export Manufacturer type for backward compatibility +export type { Manufacturer } from '../types'; + +function ManufacturerIcon({ config, size = 36 }: { config: ManufacturerConfig; size?: number }) { + const [imgError, setImgError] = useState(false); + const logoUrl = getManufacturerLogoUrl(config.logo); + + if (logoUrl && !imgError) { + return ( +
+ {config.name} setImgError(true)} + /> +
+ ); + } + + return ( +
+ {config.name.substring(0, 2).toUpperCase()} +
+ ); +} + +interface ManufacturerModalProps { + isOpen: boolean; + onClose: () => void; + onSelect: (manufacturer: Manufacturer) => void; +} + +export function ManufacturerModal({ isOpen, onClose, onSelect }: ManufacturerModalProps) { + const [search, setSearch] = useState(''); + + // Use hook for async data fetching - only fetch when modal opens and no data yet + const { data: boards, loading, error, reload } = useAsyncDataWhen( + isOpen, + () => getBoards(), + [isOpen] + ); + + const manufacturers = useMemo(() => { + if (!boards) return []; + const searchLower = search.toLowerCase(); + const counts: Record = {}; + + for (const board of boards) { + const mfr = getManufacturer(board.slug, board.name); + if (!counts[mfr]) counts[mfr] = 0; + counts[mfr]++; + } + + const result: Manufacturer[] = Object.entries(counts) + .filter(([key, count]) => { + if (count === 0) return false; + const config = MANUFACTURERS[key]; + return config.name.toLowerCase().includes(searchLower); + }) + .map(([key, count]) => ({ + id: key, + name: MANUFACTURERS[key].name, + color: MANUFACTURERS[key].color, + boardCount: count, + logo: MANUFACTURERS[key].logo, + })) + .sort((a, b) => { + if (a.id === 'other') return 1; + if (b.id === 'other') return -1; + return b.boardCount - a.boardCount; + }); + + return result; + }, [boards, search]); + + return ( + +
+
+ + setSearch(e.target.value)} + className="search-input" + autoFocus + /> +
+
+ + {loading ? ( +
+
+

Loading...

+
+ ) : error ? ( +
+

{error}

+ +
+ ) : ( +
+ {manufacturers.map((mfr) => { + const config = MANUFACTURERS[mfr.id]; + return ( + + ); + })} + {manufacturers.length === 0 && ( +
+

No manufacturers found

+
+ )} +
+ )} + + ); +} + +// Re-export from config for backward compatibility +export { MANUFACTURERS, getManufacturer } from '../config'; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx new file mode 100644 index 0000000..ccc4d50 --- /dev/null +++ b/src/components/Modal.tsx @@ -0,0 +1,46 @@ +import { type ReactNode, useEffect, useCallback } from 'react'; +import { X } from 'lucide-react'; + +interface ModalProps { + isOpen: boolean; + onClose: () => void; + title: string; + children: ReactNode; +} + +export function Modal({ isOpen, onClose, title, children }: ModalProps) { + const handleEscape = useCallback((e: KeyboardEvent) => { + if (e.key === 'Escape') { + onClose(); + } + }, [onClose]); + + useEffect(() => { + if (isOpen) { + document.addEventListener('keydown', handleEscape); + document.body.style.overflow = 'hidden'; + } + return () => { + document.removeEventListener('keydown', handleEscape); + document.body.style.overflow = ''; + }; + }, [isOpen, handleEscape]); + + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()}> +
+

{title}

+ +
+
+ {children} +
+
+
+ ); +} diff --git a/src/config/badges.ts b/src/config/badges.ts new file mode 100644 index 0000000..f7ba685 --- /dev/null +++ b/src/config/badges.ts @@ -0,0 +1,61 @@ +/** + * Badge configuration for desktop environments and kernel types + */ + +export interface BadgeConfig { + label: string; + color: string; +} + +/** + * Desktop environment badges + */ +export const DESKTOP_BADGES: Record = { + 'gnome': { label: 'GNOME', color: '#4a86cf' }, + 'kde': { label: 'KDE', color: '#1d99f3' }, + 'xfce': { label: 'XFCE', color: '#2284f2' }, + 'cinnamon': { label: 'Cinnamon', color: '#dc682e' }, + 'budgie': { label: 'Budgie', color: '#6a9fb5' }, + 'mate': { label: 'MATE', color: '#9bda5a' }, + 'lxde': { label: 'LXDE', color: '#a4a4a4' }, + 'lxqt': { label: 'LXQt', color: '#0192d3' }, + 'i3': { label: 'i3wm', color: '#1a8cff' }, + 'sway': { label: 'Sway', color: '#68b0d8' }, +}; + +/** + * Kernel type badges + */ +export const KERNEL_BADGES: Record = { + 'current': { label: 'Current', color: '#10b981' }, + 'edge': { label: 'Edge', color: '#f59e0b' }, + 'legacy': { label: 'Legacy', color: '#6b7280' }, + 'vendor': { label: 'Vendor', color: '#8b5cf6' }, +}; + +/** + * List of desktop environment keys for filtering + */ +export const DESKTOP_ENVIRONMENTS = Object.keys(DESKTOP_BADGES); + +/** + * Get desktop environment from variant string + */ +export function getDesktopEnv(variant: string): string | null { + const v = variant.toLowerCase(); + for (const key of DESKTOP_ENVIRONMENTS) { + if (v.includes(key)) return key; + } + return null; +} + +/** + * Get kernel type from branch string + */ +export function getKernelType(branch: string): string | null { + const b = branch.toLowerCase(); + for (const key of Object.keys(KERNEL_BADGES)) { + if (b.includes(key)) return key; + } + return null; +} diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..8859a3a --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,31 @@ +/** + * Configuration exports + */ + +// Manufacturer configuration +export { + MANUFACTURERS, + getManufacturer, + getManufacturerLogoUrl, + type ManufacturerConfig, +} from './manufacturers'; + +// OS/App information +export { + OS_INFO, + APP_INFO, + getOsInfo, + getAppInfo, + type OsInfoConfig, + type AppInfoConfig, +} from './os-info'; + +// Badge configuration +export { + DESKTOP_BADGES, + KERNEL_BADGES, + DESKTOP_ENVIRONMENTS, + getDesktopEnv, + getKernelType, + type BadgeConfig, +} from './badges'; diff --git a/src/config/manufacturers.ts b/src/config/manufacturers.ts new file mode 100644 index 0000000..d8087fc --- /dev/null +++ b/src/config/manufacturers.ts @@ -0,0 +1,66 @@ +/** + * Manufacturer definitions for board categorization + */ + +export interface ManufacturerConfig { + name: string; + color: string; + keywords: string[]; + logo?: string; +} + +export const MANUFACTURERS: Record = { + 'radxa': { name: 'Radxa', color: '#8b5cf6', keywords: ['radxa', 'rock-', 'rock5', 'rock3', 'rock4', 'rockpi'], logo: 'radxa.com' }, + 'orangepi': { name: 'Orange Pi', color: '#f97316', keywords: ['orangepi', 'orange-pi'], logo: 'orangepi.org' }, + 'bananapi': { name: 'Banana Pi', color: '#f59e0b', keywords: ['bananapi', 'bpi-'], logo: 'banana-pi.org' }, + 'khadas': { name: 'Khadas', color: '#10b981', keywords: ['khadas', 'vim1', 'vim2', 'vim3', 'vim4', 'edge'], logo: 'khadas.com' }, + 'hardkernel': { name: 'Hardkernel (ODROID)', color: '#3b82f6', keywords: ['odroid'], logo: 'hardkernel.com' }, + 'pine64': { name: 'Pine64', color: '#06b6d4', keywords: ['pine64', 'pinebook', 'pinephone', 'rock64', 'quartz64', 'sopine', 'pinetab', 'star64', 'ox64'], logo: 'pine64.org' }, + 'friendlyarm': { name: 'FriendlyElec', color: '#ec4899', keywords: ['nanopi', 'nanopc', 'friendlyelec', 'zeropi'], logo: 'friendlyelec.com' }, + 'olimex': { name: 'Olimex', color: '#84cc16', keywords: ['olimex', 'lime', 'olinuxino'], logo: 'olimex.com' }, + 'armsom': { name: 'ArmSoM', color: '#0ea5e9', keywords: ['armsom'], logo: 'armsom.org' }, + 'libre': { name: 'Libre Computer', color: '#22c55e', keywords: ['lepotato', 'lafrite', 'libre', 'tritium', 'renegade', 'solitude', 'sweet-potato', 'libretech', 'potato', 'frite'], logo: 'libre.computer' }, + 'asus': { name: 'ASUS Tinker', color: '#00529b', keywords: ['asus', 'tinker'], logo: 'asus.com' }, + 'nvidia': { name: 'NVIDIA Jetson', color: '#76b900', keywords: ['jetson', 'nvidia', 'tegra'], logo: 'nvidia.com' }, + 'beagle': { name: 'BeagleBoard', color: '#2e8b57', keywords: ['beagle', 'bone', 'pocketbeagle'], logo: 'beagleboard.org' }, + 'solidrun': { name: 'SolidRun', color: '#dc2626', keywords: ['solidrun', 'hummingboard', 'cubox', 'clearfog', 'honeycomb', 'lx2k'], logo: 'solid-run.com' }, + 'firefly': { name: 'Firefly', color: '#ff6600', keywords: ['firefly', 'roc-rk'], logo: 'en.t-firefly.com' }, + 'starfive': { name: 'StarFive', color: '#7c3aed', keywords: ['starfive', 'visionfive', 'jh71'], logo: 'starfivetech.com' }, + 'sipeed': { name: 'Sipeed', color: '#ea580c', keywords: ['sipeed', 'lichee', 'tang', 'maix'], logo: 'sipeed.com' }, + 'milkv': { name: 'Milk-V', color: '#be185d', keywords: ['milkv', 'milk-v', 'mars', 'duo', 'pioneer'], logo: 'milkv.io' }, + 'amlogic': { name: 'Amlogic TV Boxes', color: '#a855f7', keywords: ['aml-', 'wetek', 'ugoos', 'beelink', 'tanix', 'tx6', 'phicomm', 'n1', 'x96', 't95', 'h96', 'mecool'], logo: 'amlogic.com' }, + 'rockchip': { name: 'Rockchip Generic', color: '#6366f1', keywords: ['rk3', 'station-m', 'station-p', 'miqi'], logo: 'rock-chips.com' }, + 'allwinner': { name: 'Allwinner Generic', color: '#14b8a6', keywords: ['cubieboard', 'cubietruck', 'lamobo', 'pcduino', 'banana-pro', 'sunxi', 'a10', 'a20', 'h3', 'h5', 'h6', 'a64', 'h616'], logo: 'allwinnertech.com' }, + 'marvell': { name: 'Marvell', color: '#2563eb', keywords: ['espressobin', 'marvell', 'macchiatobin', 'globalscale'], logo: 'marvell.com' }, + 'helios': { name: 'Kobol/Helios', color: '#0891b2', keywords: ['helios', 'kobol'], logo: 'kobol.io' }, + 'mediatek': { name: 'MediaTek', color: '#ffc107', keywords: ['mediatek', 'mt7', 'mt8'], logo: 'mediatek.com' }, + 'bigtreetech': { name: 'BigTreeTech', color: '#16a34a', keywords: ['bigtreetech', 'btt', 'cb1', 'cb2'], logo: 'bigtree-tech.com' }, + 'hinlink': { name: 'Hinlink', color: '#0891b2', keywords: ['hinlink', 'h28k', 'h66k', 'h68k', 'h88k'] }, + 'embedfire': { name: 'EmbedFire', color: '#dc2626', keywords: ['embedfire', 'lubancat', 'wildfire'], logo: 'embedfire.com' }, + 'mixtile': { name: 'Mixtile', color: '#0284c7', keywords: ['mixtile', 'blade'], logo: 'mixtile.com' }, + 'cool-pi': { name: 'Cool Pi', color: '#0ea5e9', keywords: ['coolpi', 'cool-pi'], logo: 'cool-pi.com' }, + 'uefi': { name: 'UEFI/Generic', color: '#64748b', keywords: ['uefi', 'generic', 'uefi-arm64', 'uefi-x86'] }, + 'other': { name: 'Other Boards', color: '#64748b', keywords: [] }, +}; + +/** + * Get manufacturer ID from board slug and name + */ +export function getManufacturer(slug: string, name: string): string { + const searchStr = (slug + ' ' + name).toLowerCase(); + for (const [key, config] of Object.entries(MANUFACTURERS)) { + if (key === 'other') continue; + if (config.keywords.some(kw => searchStr.includes(kw))) { + return key; + } + } + return 'other'; +} + +/** + * Get manufacturer logo URL from domain + */ +export function getManufacturerLogoUrl(domain: string | undefined): string | null { + if (!domain) return null; + return `https://logo.clearbit.com/${domain}?size=128`; +} diff --git a/src/config/os-info.ts b/src/config/os-info.ts new file mode 100644 index 0000000..9485763 --- /dev/null +++ b/src/config/os-info.ts @@ -0,0 +1,81 @@ +/** + * OS/Distro information configuration + */ + +import debianLogo from '../assets/os-logos/debian.svg'; +import ubuntuLogo from '../assets/os-logos/ubuntu.png'; +import homeassistantLogo from '../assets/os-logos/homeassistant.png'; +import openmediavaultLogo from '../assets/os-logos/openmediavault.jpeg'; + +export interface OsInfoConfig { + name: string; + color: string; + logo: string; +} + +export interface AppInfoConfig { + name: string; + color: string; + badgeColor: string; + logo: string | null; +} + +/** + * OS/Distro release information with local logos + */ +export const OS_INFO: Record = { + // Debian releases + 'bookworm': { name: 'Debian 12', color: 'transparent', logo: debianLogo }, + 'bullseye': { name: 'Debian 11', color: 'transparent', logo: debianLogo }, + 'trixie': { name: 'Debian 13', color: 'transparent', logo: debianLogo }, + 'forky': { name: 'Debian 14', color: 'transparent', logo: debianLogo }, + 'sid': { name: 'Debian Sid', color: 'transparent', logo: debianLogo }, + // Ubuntu releases + 'noble': { name: 'Ubuntu 24.04', color: 'transparent', logo: ubuntuLogo }, + 'jammy': { name: 'Ubuntu 22.04', color: 'transparent', logo: ubuntuLogo }, + 'plucky': { name: 'Ubuntu 25.04', color: 'transparent', logo: ubuntuLogo }, + 'oracular': { name: 'Ubuntu 24.10', color: 'transparent', logo: ubuntuLogo }, + 'focal': { name: 'Ubuntu 20.04', color: 'transparent', logo: ubuntuLogo }, + 'mantic': { name: 'Ubuntu 23.10', color: 'transparent', logo: ubuntuLogo }, + 'lunar': { name: 'Ubuntu 23.04', color: 'transparent', logo: ubuntuLogo }, +}; + +/** + * Special applications with their own branding + */ +export const APP_INFO: Record = { + 'homeassistant': { name: 'Home Assistant', color: 'transparent', badgeColor: '#18bcf2', logo: homeassistantLogo }, + 'openmediavault': { name: 'OpenMediaVault', color: 'transparent', badgeColor: '#5dacdf', logo: openmediavaultLogo }, + 'omv': { name: 'OpenMediaVault', color: 'transparent', badgeColor: '#5dacdf', logo: openmediavaultLogo }, + 'nextcloud': { name: 'Nextcloud', color: '#0082c9', badgeColor: '#0082c9', logo: null }, + 'openwrt': { name: 'OpenWrt', color: '#00a3e0', badgeColor: '#00a3e0', logo: null }, + 'pihole': { name: 'Pi-hole', color: '#96060c', badgeColor: '#96060c', logo: null }, + 'kodi': { name: 'Kodi', color: '#17b2e7', badgeColor: '#17b2e7', logo: null }, +}; + +/** + * Get OS info from distro release name + */ +export function getOsInfo(distroRelease: string): OsInfoConfig | null { + const release = distroRelease.toLowerCase(); + for (const [key, info] of Object.entries(OS_INFO)) { + if (release.includes(key)) { + return info; + } + } + return null; +} + +/** + * Get App info from preinstalled application + */ +export function getAppInfo(app: string | null): AppInfoConfig | null { + if (!app) return null; + const appLower = app.toLowerCase(); + for (const [key, info] of Object.entries(APP_INFO)) { + if (appLower.includes(key)) { + return info; + } + } + return null; +} diff --git a/src/hooks/useAsyncData.ts b/src/hooks/useAsyncData.ts new file mode 100644 index 0000000..0133bea --- /dev/null +++ b/src/hooks/useAsyncData.ts @@ -0,0 +1,176 @@ +import { useState, useEffect, useCallback, useRef } from 'react'; + +/** + * Result of async data fetching + */ +export interface AsyncDataResult { + /** The fetched data, null if not loaded yet */ + data: T | null; + /** Whether data is currently being loaded */ + loading: boolean; + /** Error message if fetch failed */ + error: string | null; + /** Function to reload the data */ + reload: () => Promise; +} + +/** + * Options for useAsyncData hook + */ +export interface UseAsyncDataOptions { + /** Whether to fetch data immediately on mount (default: true) */ + immediate?: boolean; + /** Reset data to null before reloading (default: false) */ + resetOnReload?: boolean; +} + +/** + * Internal hook that handles the core async data fetching logic. + * Used by both useAsyncData and useAsyncDataWhen to avoid code duplication. + */ +function useAsyncDataCore( + fetcher: () => Promise, + options: { resetOnReload?: boolean; initialLoading?: boolean } = {} +): AsyncDataResult & { triggerFetch: () => void } { + const { resetOnReload = false, initialLoading = false } = options; + + const [data, setData] = useState(null); + const [loading, setLoading] = useState(initialLoading); + const [error, setError] = useState(null); + + // Track if component is mounted to prevent state updates after unmount + const mountedRef = useRef(true); + // Track the current fetch to handle race conditions + const fetchIdRef = useRef(0); + // Store fetcher in ref to avoid recreating reload callback + const fetcherRef = useRef(fetcher); + fetcherRef.current = fetcher; + + const reload = useCallback(async () => { + const currentFetchId = ++fetchIdRef.current; + + if (resetOnReload) { + setData(null); + } + setLoading(true); + setError(null); + + try { + const result = await fetcherRef.current(); + + // Only update state if this is still the latest fetch and component is mounted + if (mountedRef.current && currentFetchId === fetchIdRef.current) { + setData(result); + setError(null); + } + } catch (err) { + if (mountedRef.current && currentFetchId === fetchIdRef.current) { + const message = err instanceof Error ? err.message : 'An error occurred'; + setError(message); + } + } finally { + if (mountedRef.current && currentFetchId === fetchIdRef.current) { + setLoading(false); + } + } + }, [resetOnReload]); + + // Cleanup on unmount + useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + }; + }, []); + + return { data, loading, error, reload, triggerFetch: reload }; +} + +/** + * Hook for managing async data fetching with loading and error states. + * + * Eliminates boilerplate code for: + * - Loading state management + * - Error handling + * - Data state management + * - Reload functionality + * + * @param fetcher - Async function that returns the data + * @param deps - Dependencies that trigger a refetch when changed + * @param options - Additional options + * + * @example + * ```tsx + * // Basic usage + * const { data, loading, error, reload } = useAsyncData( + * () => getBoards(), + * [isOpen] + * ); + * + * // With conditional fetching + * const { data, loading, error } = useAsyncData( + * () => board ? getImagesForBoard(board.slug) : Promise.resolve([]), + * [board?.slug] + * ); + * ``` + */ +export function useAsyncData( + fetcher: () => Promise, + deps: React.DependencyList = [], + options: UseAsyncDataOptions = {} +): AsyncDataResult { + const { immediate = true, resetOnReload = false } = options; + + const { data, loading, error, reload } = useAsyncDataCore(fetcher, { + resetOnReload, + initialLoading: immediate + }); + + // Fetch on mount and when dependencies change + useEffect(() => { + if (immediate) { + reload(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [...deps, immediate]); + + return { data, loading, error, reload }; +} + +/** + * Hook for conditional async data fetching. + * Only fetches when the condition is true. + * + * @param condition - Whether to fetch data + * @param fetcher - Async function that returns the data + * @param deps - Additional dependencies + * + * @example + * ```tsx + * const { data, loading, error, reload } = useAsyncDataWhen( + * isOpen && boards.length === 0, + * () => getBoards(), + * [isOpen] + * ); + * ``` + */ +export function useAsyncDataWhen( + condition: boolean, + fetcher: () => Promise, + deps: React.DependencyList = [] +): AsyncDataResult { + const { data, loading: coreLoading, error, reload } = useAsyncDataCore(fetcher); + + // Fetch only when condition is true + useEffect(() => { + if (condition) { + reload(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [condition, ...deps]); + + // Show loading when condition is true but data hasn't been fetched yet + const loading = coreLoading || (condition && data === null && error === null); + + return { data, loading, error, reload }; +} diff --git a/src/hooks/useProgressPolling.ts b/src/hooks/useProgressPolling.ts new file mode 100644 index 0000000..e0e7331 --- /dev/null +++ b/src/hooks/useProgressPolling.ts @@ -0,0 +1,62 @@ +import { useRef, useCallback } from 'react'; + +/** Progress object that may contain an error field */ +interface ProgressWithError { + error?: string | null; +} + +interface PollingOptions { + /** Function to fetch progress data */ + fetchProgress: () => Promise; + /** Callback when progress updates */ + onProgress: (progress: T) => void; + /** Callback when an error is detected in progress */ + onError?: (error: string) => void; + /** Polling interval in ms (default: 250) */ + interval?: number; +} + +/** + * Hook for polling progress with automatic cleanup + */ +export function useProgressPolling() { + const intervalRef = useRef(null); + + const startPolling = useCallback(

(options: PollingOptions

) => { + const { fetchProgress, onProgress, onError, interval = 250 } = options; + + // Clear any existing interval + if (intervalRef.current) { + clearInterval(intervalRef.current); + } + + intervalRef.current = window.setInterval(async () => { + try { + const progress = await fetchProgress(); + onProgress(progress); + + // Check for error in progress object (type-safe check) + const progressWithError = progress as ProgressWithError; + if (onError && progressWithError.error) { + onError(progressWithError.error); + stopPolling(); + } + } catch (err) { + // Log polling errors in development for debugging + if (import.meta.env.DEV) { + console.warn('[useProgressPolling] Polling error:', err); + } + // Don't stop polling - transient network issues shouldn't halt progress tracking + } + }, interval); + }, []); + + const stopPolling = useCallback(() => { + if (intervalRef.current) { + clearInterval(intervalRef.current); + intervalRef.current = null; + } + }, []); + + return { startPolling, stopPolling, intervalRef }; +} diff --git a/src/hooks/useTauri.ts b/src/hooks/useTauri.ts new file mode 100644 index 0000000..8879552 --- /dev/null +++ b/src/hooks/useTauri.ts @@ -0,0 +1,77 @@ +import { invoke } from '@tauri-apps/api/core'; +import type { BoardInfo, ImageInfo, BlockDevice, DownloadProgress, FlashProgress, CustomImageInfo } from '../types'; + +export async function getBoards(): Promise { + return invoke('get_boards'); +} + +export async function getImagesForBoard( + boardSlug: string, + preappFilter?: string, + kernelFilter?: string, + variantFilter?: string, + stableOnly: boolean = false +): Promise { + return invoke('get_images_for_board', { + boardSlug, + preappFilter, + kernelFilter, + variantFilter, + stableOnly, + }); +} + +export async function getBoardImageUrl(boardSlug: string): Promise { + return invoke('get_board_image_url', { boardSlug }); +} + +export async function getBlockDevices(): Promise { + return invoke('get_block_devices'); +} + +export async function requestWriteAuthorization(devicePath: string): Promise { + return invoke('request_write_authorization', { devicePath }); +} + +export async function downloadImage(fileUrl: string): Promise { + return invoke('download_image', { fileUrl }); +} + +export async function getDownloadProgress(): Promise { + return invoke('get_download_progress'); +} + +export async function flashImage( + imagePath: string, + devicePath: string, + verify: boolean = true +): Promise { + return invoke('flash_image', { imagePath, devicePath, verify }); +} + +export async function getFlashProgress(): Promise { + return invoke('get_flash_progress'); +} + +export async function cancelOperation(): Promise { + return invoke('cancel_operation'); +} + +export async function deleteDownloadedImage(imagePath: string): Promise { + return invoke('delete_downloaded_image', { imagePath }); +} + +// Re-export CustomImageInfo for backward compatibility +export type { CustomImageInfo } from '../types'; + +export async function selectCustomImage(): Promise { + return invoke('select_custom_image'); +} + +export async function checkNeedsDecompression(imagePath: string): Promise { + return invoke('check_needs_decompression', { imagePath }); +} + +export async function decompressCustomImage(imagePath: string): Promise { + return invoke('decompress_custom_image', { imagePath }); +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..55cb426 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +// Disable context menu in production +if (import.meta.env.PROD) { + document.addEventListener('contextmenu', (e) => e.preventDefault()); +} + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +); diff --git a/src/styles/base.css b/src/styles/base.css new file mode 100644 index 0000000..4a201c3 --- /dev/null +++ b/src/styles/base.css @@ -0,0 +1,90 @@ +/* ======================================== + BASE STYLES + ======================================== */ + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body, #root { + height: 100%; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + background: var(--bg-app); + color: var(--text-primary); + -webkit-font-smoothing: antialiased; + overflow: hidden; +} + +/* ======================================== + SCROLLBAR + ======================================== */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border-color); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* ======================================== + STATES + ======================================== */ +.loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px; + color: var(--text-secondary); +} + +.spinner { + width: 32px; + height: 32px; + border: 3px solid var(--border-color); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; + margin-bottom: 16px; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.spin { + animation: spin 0.8s linear infinite; +} + +.error { + text-align: center; + color: var(--text-secondary); +} + +.error p { + margin-bottom: 16px; +} + +.no-results { + text-align: center; + padding: 48px; + color: var(--text-muted); +} + +.no-results p { + margin-bottom: 8px; +} diff --git a/src/styles/components.css b/src/styles/components.css new file mode 100644 index 0000000..c37b425 --- /dev/null +++ b/src/styles/components.css @@ -0,0 +1,661 @@ +/* ======================================== + BUTTONS + ======================================== */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 10px 20px; + border-radius: 6px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + border: none; + transition: all 0.15s ease; +} + +.btn-primary { + background: var(--accent); + color: white; +} + +.btn-primary:hover { + background: var(--accent-hover); +} + +.btn-secondary { + background: var(--bg-secondary); + color: var(--text-primary); + border: 1px solid var(--border-color); +} + +.btn-secondary:hover { + background: var(--bg-hover); +} + +.btn-danger { + background: #ef4444; + color: white; +} + +.btn-danger:hover { + background: #dc2626; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* ======================================== + SEARCH + ======================================== */ +.search-box { + position: relative; + margin-bottom: 16px; +} + +.search-icon { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + color: var(--text-muted); + pointer-events: none; +} + +.search-input { + width: 100%; + padding: 10px 14px 10px 40px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 8px; + color: var(--text-primary); + font-size: 14px; +} + +.search-input:focus { + outline: none; + border-color: var(--accent); +} + +.search-input::placeholder { + color: var(--text-muted); +} + +/* ======================================== + LIST ITEMS + ======================================== */ +.list-item { + display: flex; + align-items: center; + gap: 14px; + padding: 14px 20px; + background: transparent; + border: none; + border-bottom: 1px solid var(--border-light); + cursor: pointer; + transition: background 0.1s ease; + width: 100%; + text-align: left; +} + +.list-item:hover { + background: var(--bg-hover); +} + +.list-item:last-child { + border-bottom: none; +} + +.list-item-icon { + width: 40px; + height: 40px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: 700; + color: white; + flex-shrink: 0; + overflow: hidden; +} + +.list-item-icon img { + width: 100%; + height: 100%; + object-fit: contain; + padding: 4px; + background: white; + border-radius: 6px; +} + +.list-item-icon.os-icon { + width: 44px; + height: 44px; + border-radius: 10px; +} + +.list-item-icon.os-icon img { + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border-radius: 0; +} + +.list-item-content { + flex: 1; + min-width: 0; +} + +.list-item-title { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 2px; +} + +.list-item-subtitle { + font-size: 12px; + color: var(--text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.list-item-meta { + font-size: 11px; + color: var(--text-muted); +} + +.list-item-arrow { + color: var(--text-muted); + flex-shrink: 0; +} + +/* ======================================== + BADGES + ======================================== */ +.badge { + font-size: 10px; + padding: 2px 8px; + border-radius: 10px; + font-weight: 600; +} + +.badge-desktop { + background: #3b82f6; + color: white; +} + +.badge-cli { + background: var(--bg-secondary); + color: var(--text-secondary); + border: 1px solid var(--border-color); +} + +.badge-kernel { + background: #8b5cf6; + color: white; +} + +.badge-stable { + background: #10b981; + color: white; +} + +.removable-badge, +.system-badge { + font-size: 9px; + padding: 2px 6px; + border-radius: 10px; + font-weight: 700; + text-transform: uppercase; +} + +.removable-badge { + background: #10b981; + color: white; +} + +.system-badge { + background: #ef4444; + color: white; +} + +.promoted-star { + color: var(--armbian-orange); +} + +.promoted-badge { + color: #f59e0b; +} + +/* ======================================== + FILTER BAR + ======================================== */ +.filter-bar { + display: flex; + gap: 8px; + margin-bottom: 12px; + flex-wrap: wrap; +} + +.filter-btn { + padding: 8px 14px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 20px; + color: var(--text-secondary); + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.15s ease; +} + +.filter-btn:hover { + border-color: var(--accent); + color: var(--text-primary); +} + +.filter-btn.active { + background: var(--accent); + border-color: var(--accent); + color: white; +} + +/* ======================================== + GRIDS (fallback) + ======================================== */ +.manufacturer-grid, +.board-grid { + display: flex; + flex-direction: column; + gap: 0; + max-height: calc(100vh - 300px); + overflow-y: auto; + border: 1px solid var(--border-color); + border-radius: 8px; + background: var(--bg-card); + box-shadow: var(--shadow-sm); +} + +.manufacturer-card, +.board-card { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: transparent; + border: none; + border-bottom: 1px solid var(--border-light); + cursor: pointer; + transition: background 0.1s ease; + width: 100%; + text-align: left; +} + +.manufacturer-card:hover, +.board-card:hover { + background: var(--bg-hover); +} + +.manufacturer-card:last-child, +.board-card:last-child { + border-bottom: none; +} + +.manufacturer-card .manufacturer-icon { + width: 36px; + height: 36px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + font-weight: 700; + color: white; + flex-shrink: 0; +} + +.manufacturer-card .manufacturer-name { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + flex: 1; +} + +.manufacturer-card .manufacturer-description { + display: none; +} + +.manufacturer-card .manufacturer-count { + font-size: 12px; + color: var(--text-muted); +} + +.manufacturer-card .manufacturer-arrow { + color: var(--text-muted); +} + +/* Board cards in list */ +.board-card { + flex-direction: row; + min-height: auto; + padding: 10px 16px; +} + +.board-card .board-image { + width: 40px; + height: 40px; + border-radius: 6px; + margin-bottom: 0; +} + +.board-card .board-info { + flex: 1; + align-items: flex-start; +} + +.board-card .board-name { + font-size: 14px; + justify-content: flex-start; +} + +.board-card .board-images-count { + font-size: 11px; +} + +.board-card .board-arrow { + opacity: 1; + position: static; +} + +/* ======================================== + MANUFACTURER DETAIL + ======================================== */ +.manufacturer-detail-header { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: var(--bg-secondary); + border-radius: 8px; + margin-bottom: 12px; +} + +.manufacturer-detail-icon { + width: 36px; + height: 36px; + border-radius: 8px; +} + +.manufacturer-detail-info h2 { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.manufacturer-detail-info p { + font-size: 12px; + color: var(--text-secondary); +} + +/* ======================================== + BOARD HEADER + ======================================== */ +.board-header { + display: flex; + align-items: center; + gap: 16px; + padding: 16px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 8px; + margin-bottom: 16px; + box-shadow: var(--shadow-sm); +} + +.board-header::before { + display: none; +} + +.board-header-image { + width: 60px; + height: 60px; + border-radius: 8px; + object-fit: contain; + background: var(--bg-secondary); + padding: 4px; +} + +.board-header-placeholder { + width: 60px; + height: 60px; + border-radius: 8px; +} + +.board-header-placeholder .board-initials { + font-size: 20px; +} + +.board-header-info h2 { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); +} + +.board-header-info p { + font-size: 13px; + color: var(--text-secondary); +} + +/* Board placeholder */ +.board-image-placeholder { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.board-initials { + font-size: 14px; + font-weight: 700; + color: white; +} + +/* ======================================== + IMAGE LIST + ======================================== */ +.image-list { + display: flex; + flex-direction: column; + gap: 0; + max-height: calc(100vh - 320px); + overflow-y: auto; + border: 1px solid var(--border-color); + border-radius: 8px; + background: var(--bg-card); + box-shadow: var(--shadow-sm); +} + +.image-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: transparent; + border: none; + border-bottom: 1px solid var(--border-light); + cursor: pointer; + transition: background 0.1s ease; + text-align: left; +} + +.image-card:hover { + background: var(--bg-hover); +} + +.image-card:last-child { + border-bottom: none; +} + +.image-card.promoted { + background: rgba(242, 101, 34, 0.05); +} + +.image-main { + flex: 1; + min-width: 0; +} + +.image-title { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 4px; +} + +.image-version { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); +} + +.image-details { + display: flex; + gap: 12px; + flex-wrap: wrap; + margin-bottom: 6px; +} + +.image-detail { + font-size: 12px; + color: var(--text-secondary); +} + +.image-badges { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.image-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 4px; +} + +.image-size { + font-size: 13px; + font-weight: 600; + color: var(--text-primary); +} + +/* ======================================== + DEVICE SECTION + ======================================== */ +.device-warning { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: #fef3c7; + border-left: 3px solid #f59e0b; + border-radius: 6px; + color: #92400e; + margin-bottom: 16px; + font-size: 13px; +} + +@media (prefers-color-scheme: dark) { + .device-warning { + background: rgba(245, 158, 11, 0.1); + color: #fbbf24; + } +} + +.device-list { + display: flex; + flex-direction: column; + gap: 0; + border: 1px solid var(--border-color); + border-radius: 8px; + background: var(--bg-card); + box-shadow: var(--shadow-sm); +} + +.device-card { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 16px; + background: transparent; + border: none; + border-bottom: 1px solid var(--border-light); + cursor: pointer; + transition: background 0.1s ease; + text-align: left; +} + +.device-card:hover:not(.disabled) { + background: var(--bg-hover); +} + +.device-card:last-child { + border-bottom: none; +} + +.device-card.disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.device-card.system { + background: rgba(239, 68, 68, 0.05); +} + +.device-icon { + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-secondary); + border-radius: 8px; + color: var(--text-secondary); +} + +.device-card.removable .device-icon { + color: #10b981; + background: rgba(16, 185, 129, 0.1); +} + +.device-card.system .device-icon { + color: #ef4444; + background: rgba(239, 68, 68, 0.1); +} + +.device-info { + flex: 1; +} + +.device-name { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + display: flex; + align-items: center; + gap: 8px; +} + +.device-details { + font-size: 12px; + color: var(--text-secondary); + margin-top: 2px; +} diff --git a/src/styles/flash.css b/src/styles/flash.css new file mode 100644 index 0000000..5022f20 --- /dev/null +++ b/src/styles/flash.css @@ -0,0 +1,302 @@ +/* ======================================== + FLASH PROGRESS + ======================================== */ +.flash-container { + max-width: min(95vw, 620px); + width: 100%; + margin: 0 auto; +} + +.flash-container.centered { + display: flex; + flex-direction: column; + justify-content: center; + min-height: 280px; +} + +.flash-header { + display: flex; + align-items: center; + gap: 18px; + padding: 18px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 10px; + margin-bottom: 14px; + box-shadow: var(--shadow-sm); +} + +.flash-board-image { + width: 100px; + height: 100px; + border-radius: 10px; + object-fit: cover; + background: var(--bg-secondary); + flex-shrink: 0; +} + +.flash-info { + display: flex; + flex-direction: column; + justify-content: center; + gap: 8px; + flex: 1; +} + +.flash-info h2 { + font-size: 20px; + font-weight: 600; + color: var(--text-primary); + margin: 0; + line-height: 1.2; +} + +.flash-info-badges { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.flash-info-row { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: var(--text-secondary); +} + +.flash-info-row svg { + flex-shrink: 0; + opacity: 0.7; +} + +/* OS Badge - compact pill style */ +.os-badge { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px 4px 6px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 16px; + max-width: 100%; +} + +.os-badge-logo { + width: 20px; + height: 20px; + object-fit: contain; + flex-shrink: 0; +} + +.os-badge-text { + font-size: 12px; + font-weight: 500; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.flash-info-label { + color: var(--text-muted); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.flash-info-value { + color: var(--text-primary); + font-weight: 500; +} + +/* Device row - pill style matching OS badge */ +.flash-device-row { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px 4px 8px; + background: rgba(242, 101, 34, 0.08); + border: 1px solid rgba(242, 101, 34, 0.2); + border-radius: 16px; + width: fit-content; +} + +.flash-device-row svg { + color: var(--accent); + flex-shrink: 0; + width: 16px; + height: 16px; +} + +.flash-device-name { + color: var(--accent); + font-weight: 600; + font-size: 12px; + white-space: nowrap; +} + +.flash-device-size { + color: var(--text-secondary); + font-size: 11px; + margin-left: 4px; + padding-left: 8px; + border-left: 1px solid rgba(242, 101, 34, 0.2); +} + +.flash-status { + padding: 24px; + background: var(--bg-card); + border: 1px solid var(--border-color); + border-radius: 10px; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + box-shadow: var(--shadow-sm); +} + +/* Only use min-height when showing progress (not in centered/error mode) */ +.flash-container:not(.centered) .flash-status { + min-height: 140px; +} + +.flash-status h3 { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); + margin: 10px 0; +} + +/* ======================================== + STAGE ICONS + ======================================== */ +.stage-icon { + color: var(--text-muted); +} + +.stage-icon.downloading, +.stage-icon.flashing { + color: var(--accent); + animation: pulse 1.5s ease-in-out infinite; +} + +.stage-icon.decompressing { + color: #f97316; + animation: pulse 1.5s ease-in-out infinite; +} + +.stage-icon.ready { + color: #10b981; +} + +.stage-icon.verifying { + color: #f59e0b; + animation: pulse 1.5s ease-in-out infinite; +} + +.stage-icon.complete { + color: #10b981; +} + +.stage-icon.error { + color: #ef4444; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +/* ======================================== + PROGRESS BAR + ======================================== */ +.progress-container { + display: flex; + align-items: center; + gap: 16px; + margin-top: 16px; + width: 100%; +} + +.progress-bar { + flex: 1; + height: 8px; + background: var(--progress-bg, var(--bg-secondary)); + border-radius: 4px; + overflow: hidden; + border: 1px solid var(--border-light); +} + +.progress-fill { + height: 100%; + background: var(--accent); + border-radius: 4px; + transition: width 0.3s ease; +} + +/* Indeterminate progress bar animation */ +.progress-bar.indeterminate { + position: relative; +} + +.progress-bar.indeterminate .progress-fill { + width: 30% !important; + animation: indeterminate 1.5s infinite ease-in-out; +} + +@keyframes indeterminate { + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(400%); + } +} + +.progress-text { + font-size: 14px; + font-weight: 600; + color: var(--text-primary); + min-width: 50px; + text-align: right; +} + +/* ======================================== + FLASH ACTIONS + ======================================== */ +.flash-actions { + display: flex; + justify-content: center; + gap: 12px; +} + +.flash-actions-inline { + display: flex; + justify-content: center; + gap: 12px; + margin-top: 20px; +} + +.flash-success-hint { + color: var(--text-secondary); + font-size: 14px; + margin: 16px 0 0 0; + line-height: 1.5; +} + +.error-message { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + margin-top: 12px; + padding: 12px; + background: rgba(239, 68, 68, 0.1); + border: 1px solid #ef4444; + border-radius: 8px; + color: #ef4444; + font-size: 13px; +} diff --git a/src/styles/index.css b/src/styles/index.css new file mode 100644 index 0000000..e30efcd --- /dev/null +++ b/src/styles/index.css @@ -0,0 +1,25 @@ +/* ======================================== + ARMBIAN IMAGER - CSS Module Index + ======================================== + + This file imports all CSS modules in the correct order. + Order matters: theme variables must be loaded first, + then base styles, then components that use them. + + Structure: + - theme.css : CSS variables, colors, dark mode + - base.css : Reset, scrollbars, loading states + - layout.css : App container, header, home page + - components.css : Buttons, badges, lists, forms + - modal.css : Modal dialogs and overlays + - flash.css : Flash progress UI + - responsive.css : Media queries (must be last) + ======================================== */ + +@import './theme.css'; +@import './base.css'; +@import './layout.css'; +@import './components.css'; +@import './modal.css'; +@import './flash.css'; +@import './responsive.css'; diff --git a/src/styles/layout.css b/src/styles/layout.css new file mode 100644 index 0000000..81c6774 --- /dev/null +++ b/src/styles/layout.css @@ -0,0 +1,340 @@ +/* ======================================== + APP CONTAINER + ======================================== */ +.app { + display: flex; + flex-direction: column; + height: 100vh; + background: var(--bg-app); +} + +/* ======================================== + HEADER + ======================================== */ +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 20px 32px; + background: var(--header-bg); + border-bottom: 1px solid var(--header-border); + box-shadow: var(--shadow-sm); + flex-shrink: 0; +} + +.header-left { + display: flex; + align-items: center; +} + +.logo-main { + height: 48px; + width: auto; + filter: var(--logo-filter, none); +} + +.header-steps { + display: flex; + align-items: center; + gap: 4px; + background: var(--header-steps-bg); + border: 1px solid var(--header-steps-border); + border-radius: 10px; + padding: 8px 12px; +} + +.header-step { + display: flex; + align-items: center; + gap: 6px; + padding: 5px 12px; + border-radius: 8px; + transition: all 0.2s ease; +} + +.header-step.completed { + background: rgba(34, 197, 94, 0.15); +} + +.header-step-indicator { + width: 22px; + height: 22px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: 600; + background: var(--header-step-bg); + color: var(--header-step-color); + transition: all 0.2s ease; +} + +.header-step.completed .header-step-indicator { + background: #22c55e; + color: white; +} + +.header-step-label { + font-size: 12px; + font-weight: 500; + color: var(--header-step-color); + text-transform: uppercase; + letter-spacing: 0.3px; + transition: color 0.2s ease; +} + +.header-step.completed .header-step-label { + color: #22c55e; +} + +/* ======================================== + MAIN CONTENT + ======================================== */ +.main-content { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: clamp(16px, 4vw, 32px); + overflow-y: auto; +} + +/* ======================================== + HOME PAGE - Inline Layout + ======================================== */ +.home-page { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + gap: 24px; +} + +.home-buttons-inline { + display: flex; + align-items: flex-end; + gap: 20px; + flex-wrap: wrap; + justify-content: center; +} + +.home-write-section { + display: flex; + justify-content: center; +} + +.home-button-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.home-button-label { + font-size: 14px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; + text-align: center; +} + +.home-button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 28px 36px; + background: var(--bg-card); + border: 2px solid var(--border-color); + border-radius: 12px; + cursor: pointer; + transition: all 0.15s ease; + min-width: 220px; + height: 140px; + box-shadow: var(--shadow-sm); +} + +.home-button:hover:not(:disabled) { + border-color: var(--accent); + background: var(--bg-hover); + box-shadow: var(--shadow-md); +} + +.home-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.home-button.selected { + border-color: var(--accent); + background: var(--bg-hover); +} + +.home-button svg { + color: var(--accent); + flex-shrink: 0; + width: 36px; + height: 36px; +} + +.home-button-text { + font-size: 15px; + font-weight: 600; + color: var(--accent); + text-align: center; + text-transform: uppercase; + letter-spacing: 0.3px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 180px; +} + +.home-button-text-multi { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + max-width: 180px; +} + +.home-button-title { + font-size: 14px; + font-weight: 600; + color: var(--accent); + text-align: center; + text-transform: uppercase; + letter-spacing: 0.3px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 180px; +} + +.home-button-subtitle { + font-size: 12px; + font-weight: 500; + color: var(--text-secondary); + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 180px; +} + +.home-button:disabled .home-button-text, +.home-button:disabled svg { + color: var(--text-muted); +} + +.home-write-button { + display: flex; + align-items: center; + justify-content: center; + padding: 16px 64px; + background: var(--accent); + border: 2px solid var(--accent); + border-radius: 10px; + color: white; + font-size: 14px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.5px; + cursor: pointer; + transition: all 0.15s ease; +} + +.home-write-button:hover:not(:disabled) { + background: var(--accent-hover); + border-color: var(--accent-hover); +} + +.home-write-button:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* ======================================== + SELECTOR CONTAINER (for standalone views) + ======================================== */ +.selector-container { + width: 100%; + max-width: 600px; + align-self: flex-start; +} + +.selector-title { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 4px; +} + +.selector-subtitle { + font-size: 13px; + color: var(--text-secondary); + margin-bottom: 16px; +} + +/* Back Button */ +.back-button { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 12px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 6px; + color: var(--text-secondary); + font-size: 13px; + font-weight: 500; + cursor: pointer; + margin-bottom: 16px; + transition: all 0.15s ease; +} + +.back-button:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +/* ======================================== + CUSTOM IMAGE BUTTON + ======================================== */ +.home-custom-section { + display: flex; + justify-content: center; +} + +.home-custom-button { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background: transparent; + border: 1px solid var(--border-color); + border-radius: 6px; + color: var(--text-secondary); + font-size: 12px; + font-weight: 500; + cursor: pointer; + transition: all 0.15s ease; +} + +.home-custom-button:hover { + background: var(--bg-hover); + border-color: var(--accent); + color: var(--accent); +} + +.home-custom-button svg { + opacity: 0.7; +} + +.home-custom-button:hover svg { + opacity: 1; +} diff --git a/src/styles/modal.css b/src/styles/modal.css new file mode 100644 index 0000000..fcd1a41 --- /dev/null +++ b/src/styles/modal.css @@ -0,0 +1,208 @@ +/* ======================================== + MODAL / DIALOG + ======================================== */ +.modal-overlay { + position: fixed; + inset: 0; + background: var(--bg-overlay); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 24px; +} + +.modal { + background: var(--bg-modal); + border-radius: 12px; + width: 100%; + max-width: min(92vw, 850px); + max-height: min(88vh, 800px); + display: flex; + flex-direction: column; + box-shadow: var(--shadow-lg); + overflow: hidden; +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 20px; + border-bottom: 1px solid var(--border-color); + background: var(--bg-secondary); +} + +.modal-title { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.modal-close { + background: none; + border: none; + padding: 4px; + cursor: pointer; + color: var(--text-secondary); + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + transition: all 0.15s ease; +} + +.modal-close:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.modal-body { + flex: 1; + overflow-y: auto; + padding: 0; +} + +.modal-list { + display: flex; + flex-direction: column; +} + +.modal-warning-banner { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + background: #fef3c7; + border-left: 3px solid #f59e0b; + color: #92400e; + font-size: 12px; +} + +@media (prefers-color-scheme: dark) { + .modal-warning-banner { + background: rgba(245, 158, 11, 0.1); + color: #fbbf24; + } +} + +.modal-actions-bar { + display: flex; + justify-content: flex-end; + padding: 8px 16px; + border-bottom: 1px solid var(--border-light); +} + +.modal-refresh-bottom { + display: flex; + justify-content: center; + padding: 16px; + border-top: 1px solid var(--border-light); +} + +.modal-refresh-bottom .btn { + gap: 6px; +} + +.modal-filter-bar { + display: flex; + gap: 6px; + padding: 12px 16px; + border-bottom: 1px solid var(--border-light); +} + +.modal-filter-bar .filter-btn { + padding: 6px 12px; + font-size: 11px; +} + +/* ======================================== + CONFIRM MODAL + ======================================== */ +.confirm-modal { + max-width: 400px; + text-align: center; + padding: 24px; +} + +.confirm-icon { + margin-bottom: 16px; +} + +.confirm-title { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 8px; +} + +.confirm-text { + font-size: 14px; + color: var(--text-secondary); + margin-bottom: 12px; +} + +.confirm-device { + display: flex; + flex-direction: column; + gap: 4px; + padding: 12px; + background: var(--bg-secondary); + border-radius: 8px; + margin-bottom: 16px; +} + +.confirm-device strong { + font-size: 14px; + color: var(--text-primary); +} + +.confirm-device span { + font-size: 12px; + color: var(--text-secondary); +} + +.confirm-warning { + font-size: 12px; + font-weight: 700; + color: #ef4444; + text-transform: uppercase; + margin-bottom: 20px; +} + +.confirm-actions { + display: flex; + gap: 12px; + justify-content: center; +} + +/* ======================================== + MODAL SEARCH + ======================================== */ +.modal-search { + padding: 12px 16px; + border-bottom: 1px solid var(--border-light); + position: sticky; + top: 0; + background: var(--bg-modal); + z-index: 1; +} + +.modal-search-input { + width: 100%; + padding: 10px 14px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 6px; + color: var(--text-primary); + font-size: 14px; +} + +.modal-search-input:focus { + outline: none; + border-color: var(--accent); +} + +.modal-search-input::placeholder { + color: var(--text-muted); +} diff --git a/src/styles/responsive.css b/src/styles/responsive.css new file mode 100644 index 0000000..27aa9be --- /dev/null +++ b/src/styles/responsive.css @@ -0,0 +1,152 @@ +/* ======================================== + RESPONSIVE - Medium-large screens (< 900px) + ======================================== */ +@media (max-width: 900px) { + .header { + padding: 16px 24px; + } + + .logo-main { + height: 42px; + } + + .header-step-label { + font-size: 11px; + } + + .header-step-indicator { + width: 20px; + height: 20px; + font-size: 10px; + } + + .header-step { + padding: 4px 10px; + gap: 5px; + } + + .header-steps { + gap: 3px; + padding: 7px 10px; + } + + .home-buttons-inline { + flex-wrap: wrap; + gap: 12px; + } + + .home-button { + min-width: 180px; + height: 120px; + padding: 20px 24px; + } + + .flash-header { + gap: 14px; + padding: 14px; + } + + .flash-board-image { + width: 80px; + height: 80px; + } + + .flash-info h2 { + font-size: 17px; + } + + .flash-status { + padding: 18px; + } + + .flash-status h3 { + font-size: 15px; + } +} + +/* ======================================== + RESPONSIVE - Medium screens (< 700px) + ======================================== */ +@media (max-width: 700px) { + .header { + padding: 12px 16px; + } + + .logo-main { + height: 36px; + } + + .header-step-label { + font-size: 10px; + } + + .header-step-indicator { + width: 18px; + height: 18px; + font-size: 9px; + } + + .header-step { + padding: 4px 8px; + gap: 4px; + } + + .header-steps { + gap: 2px; + padding: 6px 8px; + } +} + +/* ======================================== + RESPONSIVE - Small screens (< 600px) + ======================================== */ +@media (max-width: 600px) { + .home-buttons-inline { + flex-direction: column; + align-items: stretch; + width: 100%; + max-width: 300px; + } + + .home-button { + min-width: auto; + width: 100%; + } + + .home-write-button { + width: 100%; + } + + .modal { + max-height: 95vh; + max-width: 98vw; + border-radius: 8px; + } + + .modal-overlay { + padding: 8px; + } + + .flash-board-image { + width: 70px; + height: 70px; + } + + .flash-info h2 { + font-size: 15px; + } +} + +/* ======================================== + RESPONSIVE - Large screens (> 1200px) + ======================================== */ +@media (min-width: 1200px) { + .modal { + max-width: 950px; + max-height: 85vh; + } + + .flash-container { + max-width: 700px; + } +} diff --git a/src/styles/theme.css b/src/styles/theme.css new file mode 100644 index 0000000..6702e47 --- /dev/null +++ b/src/styles/theme.css @@ -0,0 +1,72 @@ +/* ======================================== + THEME SYSTEM - Auto Light/Dark + ======================================== */ + +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); + +:root { + /* Armbian Brand */ + --armbian-orange: #f26522; + --armbian-red: #cc3333; + + /* Light Theme (default) */ + --bg-app: #f8f9fa; + --bg-secondary: #eef0f2; + --bg-card: #ffffff; + --bg-hover: #e9ecef; + --bg-modal: #ffffff; + --bg-overlay: rgba(0, 0, 0, 0.5); + + --text-primary: #1a1a1a; + --text-secondary: #555555; + --text-muted: #777777; + + --border-color: #d0d4d8; + --border-light: #e5e8eb; + + --accent: var(--armbian-orange); + --accent-hover: #e55a1c; + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12); + + /* Header - Light */ + --header-bg: #ffffff; + --header-border: #d0d4d8; + --header-steps-bg: #f0f2f4; + --header-steps-border: #d0d4d8; + --header-step-bg: #e0e3e6; + --header-step-color: #666666; +} + +/* Dark Theme */ +@media (prefers-color-scheme: dark) { + :root { + --bg-app: #1a1a1a; + --bg-secondary: #252525; + --bg-card: #2a2a2a; + --bg-hover: #333333; + --bg-modal: #2a2a2a; + --bg-overlay: rgba(0, 0, 0, 0.7); + + --text-primary: #ffffff; + --text-secondary: #aaaaaa; + --text-muted: #777777; + + --border-color: #404040; + --border-light: #333333; + + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4); + + /* Header - Dark */ + --header-bg: #1a1a1a; + --header-border: #333333; + --header-steps-bg: rgba(255, 255, 255, 0.05); + --header-steps-border: rgba(255, 255, 255, 0.1); + --header-step-bg: rgba(255, 255, 255, 0.1); + --header-step-color: #888888; + } +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..769e0af --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,80 @@ +export interface BoardInfo { + slug: string; + name: string; + image_count: number; + has_promoted: boolean; +} + +export interface ImageInfo { + armbian_version: string; + distro_release: string; + kernel_branch: string; + image_variant: string; + preinstalled_application: string; + promoted: boolean; + file_url: string; + file_size: number; + download_repository: string; + // Custom image fields + is_custom?: boolean; + custom_path?: string; +} + +export interface BlockDevice { + path: string; + name: string; + size: number; + size_formatted: string; + model: string; + is_removable: boolean; + is_system: boolean; +} + +export interface DownloadProgress { + total_bytes: number; + downloaded_bytes: number; + is_decompressing: boolean; + progress_percent: number; + error: string | null; +} + +export interface FlashProgress { + total_bytes: number; + written_bytes: number; + verified_bytes: number; + is_verifying: boolean; + progress_percent: number; + error: string | null; +} + +export type Step = 'board' | 'image' | 'device' | 'flash'; + +/** + * Manufacturer information for board categorization + */ +export interface Manufacturer { + id: string; + name: string; + color: string; + boardCount: number; + logo?: string; +} + +/** + * Filter type for image list + */ +export type ImageFilterType = 'all' | 'stable' | 'nightly' | 'apps' | 'barebone'; + +/** + * Modal type for app navigation + */ +export type ModalType = 'none' | 'manufacturer' | 'board' | 'image' | 'device'; + +/** + * Custom image info from file picker + */ +export interface CustomImageInfo { + path: string; + name: string; + size: number; +} 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..a9b5a59 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": 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..8a67f62 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": 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..8b0f57b --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +})