diff --git a/.eslintignore b/.eslintignore index 58125c0f6..5232681a1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ node_modules dist .vscode -build diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 97% rename from .eslintrc.js rename to .eslintrc.cjs index 95fe00c90..fba2d3dc4 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -1,5 +1,8 @@ /* eslint-env node */ module.exports = { + env: { + node: true, + }, root: true, extends: [ 'eslint:recommended', diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d779c38e1..c11c65333 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,27 +21,24 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 20 - - name: Display Node.js and npm informations - run: | - echo "node version $(node -v) running" - echo "npm version $(npm -v) running" + - uses: actions-rust-lang/setup-rust-toolchain@v1 - name: Install dependencies run: yarn install --frozen-lockfile + - name: Generate types + run: cargo test --manifest-path src/tauri + - name: 'Test: formatting' run: 'yarn run test:formatting' - - name: 'Test: typings' - run: 'yarn run test:typings' - - name: 'Test: TS/JS linting' run: 'yarn run test:lint' @@ -51,138 +48,52 @@ jobs: - name: 'Test: unit' run: 'yarn run test:unit' + - name: 'Test: typings' + run: 'yarn run test:typings' + - name: Build application run: yarn run build - - uses: actions/upload-artifact@v3 - with: - name: application-build - path: dist/ + # - uses: actions/upload-artifact@v4 + # with: + # name: application-build + # path: dist/ # Documentation on environments: # https://docs.github.com/en/free-pro-team@latest/actions/reference/specifications-for-github-hosted-runners - binaries-linux: - runs-on: ubuntu-latest - needs: [qa] - - steps: - - uses: actions/checkout@v3 - - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: 20.x - - - name: Display Node.js and npm informations - run: | - echo "node version $(node -v) running" - echo "npm version $(npm -v) running" - - - uses: actions/download-artifact@v3 - with: - name: application-build - path: dist/ - - - name: Install Linux dependencies - run: | - sudo apt update - sudo apt install --no-install-recommends -y libopenjp2-tools rpm gcc-multilib g++-multilib - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Package Linux binaries - # - name: Package Linux/Windows binaries - run: yarn run package:l - # run: yarn run package:lw - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/upload-artifact@v3 - with: - name: binaries-linux - path: | - build/museeks-linux-i386.deb - build/museeks-linux-amd64.deb - build/museeks-linux-i386.AppImage - build/museeks-linux-x86_64.AppImage - build/museeks-linux-i686.rpm - build/museeks-linux-x86_64.rpm - build/museeks-linux-x64.tar.gz - - binaries-macos: - runs-on: macos-latest - needs: [qa] + binaries: + permissions: + contents: write + strategy: + fail-fast: false + matrix: + platform: [macos-latest, ubuntu-20.04, windows-latest] + runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Use Node.js - uses: actions/setup-node@v3 + - name: setup node + uses: actions/setup-node@v4 with: - node-version: 20.x - - - name: Display Node.js and npm informations - run: | - echo "node version $(node -v) running" - echo "npm version $(npm -v) running" + node-version: 20 - - uses: actions/download-artifact@v3 - with: - name: application-build - path: dist/ - - - name: Install production dependencies - run: yarn install --frozen-lockfile - - - name: Package macOS binaries - run: yarn run package:m - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CSC_LINK: ${{ secrets.CSC_LINK }} - CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + - name: install Rust stable + uses: dtolnay/rust-toolchain@stable - - uses: actions/upload-artifact@v3 - with: - name: binaries-macos - path: | - build/museeks-macos-arm64.dmg - build/museeks-macos-x64.dmg - - binaries-windows: - runs-on: windows-latest - needs: [qa] - - steps: - - uses: actions/checkout@v3 - - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: 20.x - - - name: Display Node.js and npm informations + - name: install dependencies (ubuntu only) + if: matrix.platform == 'ubuntu-20.04' run: | - echo "node version $(node -v) running" - echo "npm version $(npm -v) running" - - - uses: actions/download-artifact@v3 - with: - name: application-build - path: dist/ + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf - - name: Install dependencies + - name: install frontend dependencies run: yarn install --frozen-lockfile - - name: Package Windows binaries - run: yarn run package:w + - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/upload-artifact@v3 with: - name: binaries-windows - path: | - build/museeks-win-x64-setup.exe - build/museeks-win-x64-portable.exe + includeRelease: false + includeUpdaterJson: false diff --git a/.gitignore b/.gitignore index e73512da9..6d6b106f5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,65 +4,25 @@ logs npm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* +lerna-debug.log* -# Runtime data -pids -*.pid -*.seed -*.pid.lock +node_modules +dist +dist-ssr +*.local -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Typescript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -dist/main/* -dist/renderer/* -dist/preload/* -cache/ -build/ -.vscode +# Editor directories and files +.vscode/* !.vscode/extensions.json - +.idea .DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Generated files +src/generated/typings/* +!src/generated/typings/index.ts diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6167b92e0..bdb900b21 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,6 +4,7 @@ "csstools.postcss", "esbenp.prettier-vscode", "stylelint.vscode-stylelint", - "ZixuanChen.vitest-explorer" + "tauri-apps.tauri-vscode", + "rust-lang.rust-analyzer" ] } diff --git a/electron-builder.yml b/electron-builder.yml deleted file mode 100644 index c397bcc25..000000000 --- a/electron-builder.yml +++ /dev/null @@ -1,68 +0,0 @@ -appId: io.museeks.app -directories: - buildResources: './dist' - output: './build' -files: - - dist/**/* - - src/shared/assets/**/* - - 'node_modules/**/*' - - '!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}' - - '!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}' - - '!**/node_modules/*.d.ts' - - '!**/node_modules/.bin' - -# fileAssociations: -# ext: -# - mp3 -# - mp4 -# - aac -# - m4a -# - 3gp -# - wav -# - ogg -# - ogv -# - ogm -# - opus -# - flac -# role: Viewer -mac: - category: public.app-category.music - target: - - target: dmg - arch: - - x64 - - arm64 - icon: src/shared/assets/logos/museeks.icns - artifactName: ${name}-macos-${arch}.${ext} - darkModeSupport: true -linux: - category: AudioVideo - target: - - target: deb - arch: - - x64 - - target: AppImage - arch: - - x64 - - target: rpm - arch: - - x64 - - target: tar.gz - arch: - - x64 - executableName: museeks - artifactName: ${name}-linux-${arch}.${ext} -deb: - depends: ['libdbus-1-dev', 'libglib2.0-dev'] -win: - target: - - target: nsis - arch: - - x64 - - target: portable - arch: - - x64 - icon: src/images/logos/museeks.ico - artifactName: ${name}-win-${arch}-setup.${ext} -portable: - artifactName: ${name}-win-${arch}-portable.${ext} diff --git a/electron.vite.config.ts b/electron.vite.config.ts deleted file mode 100644 index b1c632c9d..000000000 --- a/electron.vite.config.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; -import react from '@vitejs/plugin-react'; - -const externals = ['fs', 'electron', 'globby', 'queue', 'music-metadata']; -const minify = process.env.NODE_ENV === 'production'; - -const commonNodeConfig = { - minify, - target: 'node18', - sourcemap: true, - emptyOutDir: true, -}; - -export default defineConfig({ - main: { - plugins: [externalizeDepsPlugin({ exclude: externals })], - build: { - ...commonNodeConfig, - outDir: 'dist/main', - - lib: { - entry: './src/main/entrypoint.ts', - }, - }, - }, - preload: { - plugins: [externalizeDepsPlugin({ exclude: externals })], - build: { - ...commonNodeConfig, - outDir: 'dist/preload', - lib: { - entry: './src/preload/entrypoint.ts', - }, - }, - }, - renderer: { - plugins: [react()], - appType: 'spa', - build: { - minify, - sourcemap: true, - emptyOutDir: true, - outDir: 'dist/renderer', - }, - }, -}); diff --git a/index.html b/index.html new file mode 100644 index 000000000..03abf55f9 --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + Tauri + React + TS + + + +
+ + + diff --git a/package.json b/package.json index 12e416e95..b10413802 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "museeks", "productName": "Museeks", - "version": "0.14.0", + "version": "0.20.0", "description": "A simple, clean and cross-platform music player.", - "main": "dist/main/entrypoint.js", + "type": "module", "repository": { "type": "git", "url": "https://github.com/martpie/museeks" @@ -14,79 +14,61 @@ "url": "https://github.com/martpie/museeks/issues" }, "scripts": { - "postinstall": "electron-builder install-app-deps", - "build": "electron-vite build", - "dev": "electron-vite dev --watch", - "start": "electron-vite preview", - "museeks": "electron .", - "museeks:debug": "electron . --enable-logging --devtools --trace-warnings", - "test:typings": "yarn run test:typings:root && yarn run test:typings:main && yarn run test:typings:preload && yarn run test:typings:renderer", - "test:typings:root": "tsc --noEmit --project ./tsconfig.json", - "test:typings:main": "tsc --noEmit --project src/main/tsconfig.json", - "test:typings:preload": "tsc --noEmit --project src/preload/tsconfig.json", - "test:typings:renderer": "tsc --noEmit --project src/renderer/tsconfig.json", + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "tauri": "tauri", + "test:typings": "tsc --noEmit --project ./tsconfig.json", "test:unit": "vitest run", "test:lint": "eslint src", "test:lint:fix": "eslint src --fix", "test:formatting": "prettier --check \"./**/*.{ts,tsx,js}\"", "test:formatting:fix": "prettier --write \"./**/*.{ts,tsx,js}\"", "test:css": "stylelint \"src/**/*.css\"", - "package:lmw": "electron-builder -lmw && yarn run package:checksums", - "package:lw": "electron-builder -lw", - "package:l": "electron-builder -l", - "package:w": "electron-builder -w", - "package:m": "electron-builder -m", "package:checksums": "bash scripts/checksum.sh" }, + "browserslist": [ + "defaults" + ], "dependencies": { - "@electron/remote": "^2.1.2", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-slider": "^1.1.2", - "@tanstack/react-virtual": "3.0.4", - "chardet": "^1.6.0", + "@tanstack/react-virtual": "3.1.2", + "@tauri-apps/api": "^2.0.0-beta.3", + "@tauri-apps/plugin-dialog": "^2.0.0-beta.1", + "@tauri-apps/plugin-log": "^2.0.0-beta.1", + "@tauri-apps/plugin-os": "^2.0.0-beta.1", + "@tauri-apps/plugin-shell": "^2.0.0-beta.1", "classnames": "^2.5.1", - "electron-store": "^8.1.0", "font-awesome": "^4.7.0", - "globby": "^13.2.2", - "iconv-lite": "^0.6.3", "lodash": "^4.17.21", - "m3ujs": "^0.2.1", - "music-metadata": "^8.2.0", - "nanoid": "^5.0.5", - "pino": "^8.18.0", + "nanoid": "^5.0.6", + "normalize.css": "^8.0.1", + "pino": "^8.19.0", "pino-pretty": "^10.3.1", - "pouchdb": "^8.0.1", - "pouchdb-find": "^8.0.1", - "queue": "^7.0.0", "react": "^18.2.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-fontawesome": "^1.7.1", "react-keybinding-component": "^2.0.2", - "react-router-dom": "6.22.0", + "react-router-dom": "6.22.1", "semver": "^7.6.0", "svg-inline-react": "^3.2.1", - "zustand": "^4.5.0" + "zustand": "^4.5.1" }, "devDependencies": { - "@total-typescript/ts-reset": "^0.5.1", + "@tauri-apps/cli": "^2.0.0-beta.4", "@types/lodash": "^4.14.202", - "@types/pouchdb": "^6.4.2", - "@types/react": "^18.2.55", + "@types/react": "^18.2.58", "@types/react-dom": "^18.2.19", - "@types/react-fontawesome": "^1.6.8", - "@types/react-router-dom": "^5.3.3", - "@types/semver": "^7.5.6", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@types/semver": "^7.5.8", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", "@vitejs/plugin-react": "^4.2.1", - "electron": "28.2.2", - "electron-builder": "^24.12.0", - "electron-devtools-assembler": "^1.2.0", - "electron-vite": "^2.0.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", + "autoprefixer": "^10.4.17", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", @@ -95,18 +77,19 @@ "eslint-plugin-react-hooks": "^4.6.0", "normalize.css": "^8.0.1", "postcss": "^8.4.35", - "postcss-import": "^15.1.0", + "postcss-import": "^16.0.1", "postcss-nested": "^6.0.1", "postcss-scss": "^4.0.9", "postcss-url": "^10.1.3", "prettier": "^3.2.5", "prettier-eslint": "^16.3.0", - "stylelint": "^15.11.0", - "stylelint-config-css-modules": "^4.3.0", - "stylelint-config-standard": "^34.0.0", + "stylelint": "^16.2.1", + "stylelint-config-css-modules": "^4.4.0", + "stylelint-config-standard": "^36.0.0", "typescript": "^5.3.3", - "typescript-plugin-css-modules": "^5.0.2", - "vite": "^5.1.0", - "vitest": "^1.2.2" + "typescript-plugin-css-modules": "^5.1.0", + "vite": "^5.1.4", + "vite-plugin-react-svg": "^0.2.0", + "vitest": "^1.3.1" } } diff --git a/postcss.config.mjs b/postcss.config.mjs index bd499164c..566405cfb 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -3,5 +3,6 @@ export default { plugins: { 'postcss-import': {}, 'postcss-nested': {}, + 'autoprefixer': {} }, }; diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 000000000..f4dfb82b2 --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock new file mode 100644 index 000000000..fc54f16d3 --- /dev/null +++ b/src-tauri/Cargo.lock @@ -0,0 +1,6728 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "actionable" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9e6839049e5ad3a410c0fcd32ee25e7eb1f0fb9333310b48314ad6686d09f5" +dependencies = [ + "actionable-macros", + "async-trait", + "serde", + "thiserror", +] + +[[package]] +name = "actionable-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219df0f6a405dcf4f2b0fbb85bc2dafde06d89662d3da7b7f350f3515d65d15d" +dependencies = [ + "darling 0.13.4", + "ident_case", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.12", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + +[[package]] +name = "arc-bytes" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de7bfea323262a3d319ed4ed16a960f07395bd903ac650f5e345b32401567d5" +dependencies = [ + "serde", + "thiserror", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ashpd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b22517ee647547c01a687cf9b76074e1c91334032a4324f7243c6ee0f949390" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "serde", + "serde_repr", + "tokio", + "url", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" +dependencies = [ + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +dependencies = [ + "concurrent-queue", + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +dependencies = [ + "async-lock 3.3.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451e3cf68011bd56771c79db04a9e333095ab6349f7e47592b788e9b98720cc8" +dependencies = [ + "async-channel", + "async-io", + "async-lock 3.3.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 5.1.0", + "futures-lite", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib 0.18.5", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "attribute-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c94f43ede6f25dab1dea046bff84d85dea61bd49aba7a9011ad66c0d449077b" +dependencies = [ + "attribute-derive-macro", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "attribute-derive-macro" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b409e2b2d2dc206d2c0ad3575a93f001ae21a1593e2d0c69b69c308e63f3b422" +dependencies = [ + "collection_literals", + "interpolator", + "manyhow", + "proc-macro-utils", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.51", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "basic-toml" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5" +dependencies = [ + "serde", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel", + "async-lock 3.3.0", + "async-task", + "fastrand", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + +[[package]] +name = "bonsaidb" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8e0684e6d0a625039c24880ddd23ff5eec068b4f47a5f7150ec261812bdf7b1" +dependencies = [ + "bonsaidb-core", + "bonsaidb-files", + "bonsaidb-local", + "derive-where", +] + +[[package]] +name = "bonsaidb-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c75825d18ec94b4530afd246b638c318934416808683d6cf6971bf9590f5e48" +dependencies = [ + "actionable", + "arc-bytes", + "async-trait", + "bonsaidb-macros", + "bytecount", + "circulate", + "derive-where", + "futures", + "itertools", + "num-traits", + "ordered-varint", + "pot", + "serde", + "sha2", + "thiserror", + "tinyvec", + "transmog", + "transmog-pot", + "zeroize", +] + +[[package]] +name = "bonsaidb-files" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6cb6f5473367d7f492c9ada0a35fb1317e5e55db8d48737d78e8f49dba82de" +dependencies = [ + "bonsaidb-core", + "bonsaidb-macros", + "bonsaidb-utils", + "derive-where", + "futures", + "lru 0.12.3", + "parking_lot", + "serde", + "thiserror", + "tokio", +] + +[[package]] +name = "bonsaidb-local" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa13e68a85fef7b5c5ebcf90cd3b9d1f821a4ddb6f0e3fbe1150071b5226078a" +dependencies = [ + "async-trait", + "bincode", + "bonsaidb-core", + "bonsaidb-utils", + "byteorder", + "derive-where", + "easy-parallel", + "flume 0.11.0", + "fs2", + "futures", + "itertools", + "log", + "nebari", + "p256", + "parking_lot", + "pot", + "rand 0.8.5", + "serde", + "sysinfo", + "thiserror", + "tokio", + "transmog-versions", + "watchable", +] + +[[package]] +name = "bonsaidb-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c96879fa66046a8d550de9c52f93fa99feea4b24e81917f883c39b5eb11f54" +dependencies = [ + "attribute-derive", + "manyhow", + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "quote-use", + "syn 2.0.51", + "trybuild", +] + +[[package]] +name = "bonsaidb-utils" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184e5ac9b34e6d55fb0a8437bd279650942b25d3ee95b74f3fed2bb5032d5f93" + +[[package]] +name = "borsh" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028" +dependencies = [ + "borsh-derive", + "cfg_aliases 0.1.1", +] + +[[package]] +name = "borsh-derive" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" +dependencies = [ + "once_cell", + "proc-macro-crate 2.0.2", + "proc-macro2", + "quote", + "syn 2.0.51", + "syn_derive", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" + +[[package]] +name = "byte-unit" +version = "5.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ac19bdf0b2665407c39d82dbc937e951e7e2001609f0fb32edd0af45a2d63e" +dependencies = [ + "rust_decimal", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + +[[package]] +name = "bytemuck" +version = "1.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.4.2", + "cairo-sys-rs", + "glib 0.18.5", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cargo_toml" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" +dependencies = [ + "serde", + "toml 0.8.2", +] + +[[package]] +name = "cc" +version = "1.0.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f" + +[[package]] +name = "chrono" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.3", +] + +[[package]] +name = "circulate" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56d670e28743ed41e3cd4388cf7fc928bcf9cedf515b63443a2e13fc4c986df" +dependencies = [ + "arc-bytes", + "flume 0.11.0", + "futures", + "parking_lot", + "pot", + "serde", +] + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics 0.22.3", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics 0.23.1", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "collection_literals" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colored" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f741c91823341bebf717d4c71bda820630ce065443b58bd1b7451af008355" +dependencies = [ + "is-terminal", + "lazy_static", + "winapi 0.3.9", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.51", +] + +[[package]] +name = "ctor" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" +dependencies = [ + "quote", + "syn 2.0.51", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.51", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi 0.3.9", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-where" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.1", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi 0.3.9", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "drm" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" +dependencies = [ + "bitflags 2.4.2", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix", +] + +[[package]] +name = "drm-ffi" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" +dependencies = [ + "drm-sys", + "rustix", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" +dependencies = [ + "libc", + "linux-raw-sys 0.6.4", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "easy-parallel" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afbb9b0aef60e4f0d2b18129b6c0dff035a6f7dbbd17c2f38c1432102ee223c" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embed-resource" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bde55e389bea6a966bd467ad1ad7da0ae14546a5bc794d16d1e55e7fca44881" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.2", + "vswhom", + "winreg 0.51.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.1.0", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" +dependencies = [ + "colored", + "log", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib 0.18.5", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib 0.18.5", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib 0.18.5", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys 0.18.1", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generator" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b25e5b3e733153bcab35ee4671b46604b42516163cae442d1601cb716f2ac5" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows 0.53.0", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.18.1", + "glib 0.18.5", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +dependencies = [ + "glib-sys 0.16.3", + "gobject-sys 0.16.3", + "libc", + "system-deps", + "winapi 0.3.9", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", + "winapi 0.3.9", +] + +[[package]] +name = "glib" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.16.3", + "glib-macros 0.16.8", + "glib-sys 0.16.3", + "gobject-sys 0.16.3", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.4.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.18.1", + "glib-macros 0.18.5", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "glib-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gobject-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +dependencies = [ + "glib-sys 0.16.3", + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys 0.18.1", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib 0.18.5", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.10", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home-config" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27825a5c636b5efd43d1b76065198d4e7927951593202c88764bfd9693cf4cf0" +dependencies = [ + "dirs 4.0.0", + "serde", + "toml 0.5.11", +] + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.10", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa 1.0.10", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "infer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" +dependencies = [ + "cfb", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "interpolator" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib 0.18.5", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir 2.4.0", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "treediff", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.4.2", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib 0.18.5", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi 0.3.9", +] + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] + +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "linux-raw-sys" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "lofty" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75066eb1d25a7047fb2667edb410ae2592439ed81546f95c28b0a1c7d7d3818" +dependencies = [ + "byteorder", + "data-encoding", + "flate2", + "lofty_attr", + "log", + "ogg_pager", + "paste", +] + +[[package]] +name = "lofty_attr" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764b60e1ddd07e5665a6a17636a95cd7d8f3b86c73503a69c32979d05f72f3cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "manyhow" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b76546495d933baa165075b95c0a15e8f7ef75e53f56b19b7144d80fd52bd" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "manyhow-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba072c0eadade3160232e70893311f1f8903974488096e2eb8e48caba2f0cf1" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "muda" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453" +dependencies = [ + "cocoa 0.25.0", + "crossbeam-channel", + "gtk", + "keyboard-types", + "objc", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "museeks" +version = "0.20.0" +dependencies = [ + "anyhow", + "base64", + "bonsaidb", + "dirs 5.0.1", + "futures", + "home-config", + "lofty", + "log", + "nosleep", + "rayon", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-dialog", + "tauri-plugin-log", + "tauri-plugin-os", + "tauri-plugin-shell", + "tauri-plugin-window-state", + "thiserror", + "tokio", + "ts-rs", + "walkdir 2.4.0", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nebari" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d803ae55feaf2b1e177b2d4c3315468f4ccc215c5f9a83d31edfb862803f5b5e" +dependencies = [ + "arc-bytes", + "backtrace", + "byteorder", + "crc", + "flume 0.10.14", + "lru 0.7.8", + "num_cpus", + "once_cell", + "parking_lot", + "thiserror", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nosleep" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cedad558d018cdc0abbca10a799e91ed63fa442d4a567f91dc9dce775a3ebba" +dependencies = [ + "nosleep-mac-sys", + "nosleep-nix", + "nosleep-types", + "nosleep-windows", +] + +[[package]] +name = "nosleep-mac-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b2df065619dbb4fc6034967a7511b0bc4d2caa7688cd6ea0f5601edd7f3901" +dependencies = [ + "cc", + "nosleep-types", + "objc-foundation", + "objc_id", + "snafu", +] + +[[package]] +name = "nosleep-nix" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb4d94c4c2a46b6756dd6f2401c143cd2cb6b8324b0b151cff5de9dad3e59bd" +dependencies = [ + "dbus", + "nosleep-types", + "snafu", +] + +[[package]] +name = "nosleep-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2d4d904e07b1c33a67ca29e5ac82902e7bb867130d993e938f8c4402986449" +dependencies = [ + "snafu", +] + +[[package]] +name = "nosleep-windows" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2570bd99af538bf408bfaec6f14a63d7d89ad8fe82a17c0c43a31395da4c8d1e" +dependencies = [ + "nosleep-types", + "snafu", + "windows 0.36.1", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "ogg_pager" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c949d63b387b25c332f6e39d1762dd4b405008289dd7681f02c258b1294653ca" +dependencies = [ + "byteorder", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "open" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a083c0c7e5e4a8ec4176346cf61f67ac674e8bfb059d9226e1c54a96b377c12" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "ordered-varint" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9cc9f18ab4bad1e01726bda1259feb8f11e5e76308708a966b4c0136e9db34c" + +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi 0.3.9", +] + +[[package]] +name = "os_pipe" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib 0.18.5", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plist" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" +dependencies = [ + "base64", + "indexmap 2.2.3", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "pot" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df842bdb3b0553a411589e64aaa1a7d0c0259f72fabcedfaa841683ae3019d80" +dependencies = [ + "byteorder", + "half", + "serde", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro-utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "quote-use" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7b5abe3fe82fdeeb93f44d66a7b444dedf2e4827defb0a8e69c437b2de2ef94" +dependencies = [ + "quote", + "quote-use-macros", + "syn 2.0.51", +] + +[[package]] +name = "quote-use-macros" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ea44c7e20f16017a76a245bb42188517e13d16dcb1aa18044bc406cdc3f4af" +dependencies = [ + "derive-where", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom 0.2.12", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rfd" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373d2fc6310e2d14943d4e66ebed5b774a2b6b3b1610e7377edf124fb2760d6b" +dependencies = [ + "ashpd", + "block", + "dispatch", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "gtk-sys", + "js-sys", + "log", + "objc", + "objc-foundation", + "objc_id", + "raw-window-handle 0.6.0", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.34.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39449a79f45e8da28c57c341891b69a183044b29518bb8f86dbac9df60bb7df" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "same-file" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" +dependencies = [ + "kernel32-sys", + "winapi 0.2.8", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa 1.0.10", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.10", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.3", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" +dependencies = [ + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shared_child" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "snafu" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071916a85d1db274b4ed57af3a14afb66bd836ae7f82ebb6f1fd3455107830d9" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases 0.2.0", + "cocoa 0.25.0", + "core-graphics 0.23.1", + "drm", + "fastrand", + "foreign-types 0.5.0", + "js-sys", + "log", + "memmap2", + "objc", + "raw-window-handle 0.6.0", + "redox_syscall", + "rustix", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.52.0", + "x11rb", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib 0.18.5", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "swift-rs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204" +dependencies = [ + "base64", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sys-locale" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +dependencies = [ + "libc", +] + +[[package]] +name = "sysinfo" +version = "0.29.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "winapi 0.3.9", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d9325da2dd7ebd48a8a433c64240079b15dbe1249da04c72557611bcd08d1c" +dependencies = [ + "bitflags 1.3.2", + "cocoa 0.25.0", + "core-foundation", + "core-graphics 0.23.1", + "crossbeam-channel", + "dispatch", + "dlopen2", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "image", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "png", + "raw-window-handle 0.6.0", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.52.0", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tauri" +version = "2.0.0-beta.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ff52a838cb7c37ae344399a51eddb2669817142150a53dd47fe5c9c8f74daf" +dependencies = [ + "anyhow", + "bytes", + "cocoa 0.25.0", + "dirs-next", + "embed_plist", + "futures-util", + "getrandom 0.2.12", + "glob", + "gtk", + "heck", + "http", + "http-range", + "jni", + "libc", + "log", + "mime", + "muda", + "objc", + "percent-encoding", + "raw-window-handle 0.6.0", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "state", + "static_assertions", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows 0.52.0", +] + +[[package]] +name = "tauri-build" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fcbbc1a1b4d71d6f4084cb292f77e46b26b8bdb6a8fd7783d2059bf41dcc9e0" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs-next", + "glob", + "heck", + "json-patch", + "quote", + "schemars", + "semver", + "serde", + "serde_json", + "tauri-codegen", + "tauri-utils", + "tauri-winres", + "toml 0.8.2", + "walkdir 2.4.0", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28990f57cb70a6c884810d22bfcf121ac04482b47c58f32487ec3f8f0746ee36" +dependencies = [ + "base64", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.51", + "tauri-utils", + "thiserror", + "time", + "url", + "uuid", + "walkdir 2.4.0", +] + +[[package]] +name = "tauri-macros" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c565fb3e1817401f4c90bc82c70184dbd65f11c01cc8d538c7d04b49245266c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.51", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f58cf6b5e57a00edcec44ba0a54239a6a826ba068e4c931bf7f0f6014adbbc" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils", + "toml 0.8.2", + "walkdir 1.0.7", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed2ac81ffb8fd76d854578784491bbf8d8baba274fa9e18c0bbdc4ba00b0013" +dependencies = [ + "glib 0.16.9", + "log", + "raw-window-handle 0.6.0", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a556b50d761ec405087bf67e82b3936ddeaf39d4d602a443e2a0d2df6cdb2a" +dependencies = [ + "anyhow", + "glob", + "schemars", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror", + "url", + "uuid", +] + +[[package]] +name = "tauri-plugin-log" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a4d375f893c1defba8ac08877b430e7a87440a4cb972e06168074f5481cf003" +dependencies = [ + "android_logger", + "byte-unit", + "cocoa 0.24.1", + "fern", + "log", + "objc", + "serde", + "serde_json", + "serde_repr", + "swift-rs", + "tauri", + "tauri-plugin", + "time", +] + +[[package]] +name = "tauri-plugin-os" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9bd936030047ce0143dda585f908f4e8433089c4aa95ef182959283483abfb" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb42a100bd4b6adad6f07455f54c5916d32be79d81e5914478ece45a04560cad" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror", +] + +[[package]] +name = "tauri-plugin-window-state" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18ea69612514fd82a41e42079a7783f78a326c413aaede065f0a8000922f5449" +dependencies = [ + "bincode", + "bitflags 2.4.2", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror", +] + +[[package]] +name = "tauri-runtime" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d265cd2e7b319030ac9386a8bf711f8da3da292eef2a8f478a44df2042660dfd" +dependencies = [ + "gtk", + "http", + "jni", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "tauri-utils", + "thiserror", + "url", + "windows 0.52.0", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f428ea2dedaf129bbb72e5b2aaf7f44e541b46d5a67c8bdcbbe4f85f59b2e428" +dependencies = [ + "cocoa 0.25.0", + "gtk", + "http", + "jni", + "percent-encoding", + "raw-window-handle 0.6.0", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows 0.52.0", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66ed6c646be32b5e9df6f1380352f1ecdd720b149b35f53ba0bfc60b065b1e29" +dependencies = [ + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "glob", + "heck", + "html5ever", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "proc-macro2", + "quote", + "schemars", + "semver", + "serde", + "serde_json", + "serde_with", + "swift-rs", + "thiserror", + "toml 0.8.2", + "url", + "walkdir 2.4.0", +] + +[[package]] +name = "tauri-winres" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.8", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa 1.0.10", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4098d49269baa034a8d1eae9bd63e9fa532148d772121dace3bcd6a6c98eb6d" +dependencies = [ + "as-raw-xcb-connection", + "ctor", + "libloading 0.8.1", + "tracing", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.3", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.2.3", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "transmog" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a7f05cba0120a41e81c7309f084e8b1014118ed19857d6e878c79f0fc4efac" + +[[package]] +name = "transmog-pot" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f777f5fd9b33fa0fd78c5b5ce0e23273098a4f3ff37e3a9b22733b43e71f914e" +dependencies = [ + "pot", + "serde", + "transmog", +] + +[[package]] +name = "transmog-versions" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8950fe6741bdec0c5efb79db30d0f976951653996ba7bcf81763d04ea014135c" +dependencies = [ + "ordered-varint", + "thiserror", + "transmog", +] + +[[package]] +name = "tray-icon" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4d9ddd4a7c0f3b6862af1c4911b529a49db4ee89310d3a258859c2f5053fdd" +dependencies = [ + "cocoa 0.25.0", + "core-graphics 0.23.1", + "crossbeam-channel", + "dirs-next", + "libappindicator", + "muda", + "objc", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "treediff" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" +dependencies = [ + "serde_json", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "trybuild" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + +[[package]] +name = "ts-rs" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2cae1fc5d05d47aa24b64f9a4f7cba24cdc9187a2084dd97ac57bef5eccae6" +dependencies = [ + "thiserror", + "ts-rs-macros", +] + +[[package]] +name = "ts-rs-macros" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f7f9b821696963053a89a7bd8b292dc34420aea8294d7b225274d488f3ec92" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.51", + "termcolor", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi 0.3.9", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "value-bag" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" +dependencies = [ + "kernel32-sys", + "same-file 0.1.3", + "winapi 0.2.8", +] + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file 1.0.6", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.51", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "watchable" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45b42a2f611916b5965120a9cde2b60f2db4454826dd9ad5e6f47c24a5b3b259" +dependencies = [ + "event-listener 4.0.3", + "futures-util", + "parking_lot", + "thiserror", +] + +[[package]] +name = "wayland-backend" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +dependencies = [ + "bitflags 2.4.2", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys 0.18.1", + "glib 0.18.5", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys 0.18.1", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.52.0", + "windows-core 0.52.0", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "webview2-com-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +dependencies = [ + "thiserror", + "windows 0.52.0", + "windows-core 0.52.0", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33082acd404763b315866e14a0d5193f3422c81086657583937a750cdd3ec340" +dependencies = [ + "cocoa 0.25.0", + "objc", + "raw-window-handle 0.6.0", + "windows-sys 0.52.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53b97a83176b369b0eb2fd8158d4ae215357d02df9d40c1e1bf1879c5482c80" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement", + "windows-interface", + "windows-targets 0.52.3", +] + +[[package]] +name = "windows" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" +dependencies = [ + "windows-core 0.53.0", + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-core" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.3", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +dependencies = [ + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winreg" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e" +dependencies = [ + "base64", + "block", + "cfg_aliases 0.1.1", + "cocoa 0.25.0", + "core-graphics 0.23.1", + "crossbeam-channel", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.52.0", + "windows-implement", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.1", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + +[[package]] +name = "xdg-home" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "derivative", + "enumflags2", + "event-listener 5.1.0", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 000000000..c60565c57 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,39 @@ +# https://doc.rust-lang.org/cargo/reference/manifest.html + +[package] +name = "museeks" +version = "0.20.0" +description = "A simple, clean and cross-platform music player" +authors = ["Pierre de la Martinière "] +license = "MIT" +repository = "https://github.com/martpie/museeks" +edition = "2021" + +[build-dependencies] +tauri-build = { version = "2.0.0-beta.5", features = ["codegen"] } + +[dependencies] +tauri = { version = "2.0.0-beta.7", features = ["protocol-asset"] } +tauri-plugin-dialog = "2.0.0-beta.1" +tauri-plugin-os = "2.0.0-beta.1" +tauri-plugin-log = {version = "2.0.0-beta.1", features = ["colored"] } +tauri-plugin-shell = "2.0.0-beta.1" +tauri-plugin-window-state = "2.0.0-beta.1" + +# non-Tauri dependencies +anyhow = "1.0.80" +base64 = "0.21.7" +bonsaidb = { version = "0.5.0", features = ["local", "async"] } +dirs = "5.0.1" +futures = "0.3.30" +home-config = { version = "0.6.0", features = ["toml"] } +log = "0.4.20" +lofty = "0.18.2" +nosleep = "0.2.1" +rayon = "1.8.1" +serde_json = "1.0.114" +serde = { version = "1.0.197", features = ["derive"] } +thiserror = "1.0.57" +tokio = { version = "1.36.0", features = ["full"] } +ts-rs = "7.1.1" +walkdir = "2.4.0" diff --git a/src-tauri/Info.plist b/src-tauri/Info.plist new file mode 100644 index 000000000..fe253ec7b --- /dev/null +++ b/src-tauri/Info.plist @@ -0,0 +1,10 @@ + + + + + NSCameraUsageDescription + Request camera access for WebRTC + NSMicrophoneUsageDescription + Request microphone access for WebRTC + + diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 000000000..a85255aba --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,28 @@ +fn main() { + tauri_build::try_build( + tauri_build::Attributes::new() + .codegen(tauri_build::CodegenContext::new()) + .plugin( + "config", + tauri_build::InlinedPlugin::new().commands(&["get_config", "set_config"]), + ) + .plugin( + "database", + tauri_build::InlinedPlugin::new().commands(&[ + "import_tracks_to_library", + "get_all_tracks", + "get_all_playlists", + "get_cover_as_base64", + ]), + ) + .plugin( + "default-view", + tauri_build::InlinedPlugin::new().commands(&["set"]), + ) + .plugin( + "sleepblocker", + tauri_build::InlinedPlugin::new().commands(&["enable", "disable"]), + ), + ) + .expect("Failed to run tauri-build"); +} diff --git a/src-tauri/capabilities/main.json b/src-tauri/capabilities/main.json new file mode 100644 index 000000000..11740cc68 --- /dev/null +++ b/src-tauri/capabilities/main.json @@ -0,0 +1,34 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "main-capability", + "description": "Capability for the main window", + "windows": ["main"], + "permissions": [ + "path:default", + "event:default", + "window:default", + "app:default", + "resources:default", + "menu:default", + "tray:default", + "shell:allow-open", + "dialog:allow-open", + "dialog:allow-ask", + "window:allow-start-dragging", + "os:allow-os-type", + "app:allow-version", + "app:allow-tauri-version", + "log:allow-log", + "window:allow-show", + "config:allow-set-config", + "config:allow-get-config", + "database:allow-import-tracks-to-library", + "database:allow-get-all-tracks", + "database:allow-get-all-playlists", + "database:allow-get-cover-as-base64", + "default-view:allow-set", + "sleepblocker:allow-enable", + "sleepblocker:allow-disable" + ], + "platforms": ["linux", "macOS", "windows"] +} diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json new file mode 100644 index 000000000..803e783c0 --- /dev/null +++ b/src-tauri/gen/schemas/capabilities.json @@ -0,0 +1 @@ +{"main-capability":{"identifier":"main-capability","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["path:default","event:default","window:default","app:default","resources:default","menu:default","tray:default","shell:allow-open","dialog:allow-open","dialog:allow-ask","window:allow-start-dragging","os:allow-os-type","app:allow-version","app:allow-tauri-version","log:allow-log","window:allow-show","config:allow-set-config","config:allow-get-config","database:allow-import-tracks-to-library","database:allow-get-all-tracks","database:allow-get-all-playlists","database:allow-get-cover-as-base64","default-view:allow-set","sleepblocker:allow-enable","sleepblocker:allow-disable"],"platforms":["linux","macOS","windows"]}} \ 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 000000000..fa3a2b08c --- /dev/null +++ b/src-tauri/gen/schemas/desktop-schema.json @@ -0,0 +1,2563 @@ +{ + "$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": "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 separate windows or plugins functionality from each other at runtime.\n\nIf a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create trust groups and reduce impact of vulnerabilities in certain plugins or windows. Windows can be added to a capability by exact name or glob patterns like *, admin-* or main-window.", + "type": "object", + "required": [ + "identifier", + "permissions", + "windows" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.", + "type": "string" + }, + "description": { + "description": "Description of the capability.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.", + "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 uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that uses this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + } + }, + "platforms": { + "description": "Target platforms this capability applies. By default all platforms applies.", + "default": [ + "linux", + "macOS", + "windows", + "android", + "iOS" + ], + "type": "array", + "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. Can use glob patterns.", + "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", + "oneOf": [ + { + "type": "object", + "required": [ + "identifier" + ], + "properties": { + "identifier": { + "oneOf": [ + { + "description": "shell:allow-execute -> Enables the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-execute" + ] + }, + { + "description": "shell:allow-kill -> Enables the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-kill" + ] + }, + { + "description": "shell:allow-open -> Enables the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-open" + ] + }, + { + "description": "shell:allow-stdin-write -> Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-stdin-write" + ] + }, + { + "description": "shell:deny-execute -> Denies the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-execute" + ] + }, + { + "description": "shell:deny-kill -> Denies the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-kill" + ] + }, + { + "description": "shell:deny-open -> Denies the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-open" + ] + }, + { + "description": "shell:deny-stdin-write -> Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-stdin-write" + ] + } + ] + }, + "allow": { + "items": { + "title": "Entry", + "description": "A command allowed to be executed by the webview API.", + "type": "object", + "required": [ + "args", + "command", + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellAllowedArgs" + } + ] + }, + "command": { + "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`, `$APP`, `$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" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + } + } + }, + "deny": { + "items": { + "title": "Entry", + "description": "A command allowed to be executed by the webview API.", + "type": "object", + "required": [ + "args", + "command", + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellAllowedArgs" + } + ] + }, + "command": { + "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`, `$APP`, `$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" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + } + } + } + } + } + ] + } + ] + }, + "Identifier": { + "oneOf": [ + { + "description": "app:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "app:default" + ] + }, + { + "description": "app:allow-app-hide -> Enables the app_hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-app-hide" + ] + }, + { + "description": "app:allow-app-show -> Enables the app_show command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-app-show" + ] + }, + { + "description": "app:allow-name -> Enables the name command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-name" + ] + }, + { + "description": "app:allow-tauri-version -> Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-tauri-version" + ] + }, + { + "description": "app:allow-version -> Enables the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-version" + ] + }, + { + "description": "app:deny-app-hide -> Denies the app_hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-app-hide" + ] + }, + { + "description": "app:deny-app-show -> Denies the app_show command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-app-show" + ] + }, + { + "description": "app:deny-name -> Denies the name command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-name" + ] + }, + { + "description": "app:deny-tauri-version -> Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-tauri-version" + ] + }, + { + "description": "app:deny-version -> Denies the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-version" + ] + }, + { + "description": "config:allow-get-config -> Enables the get_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:allow-get-config" + ] + }, + { + "description": "config:allow-set-config -> Enables the set_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:allow-set-config" + ] + }, + { + "description": "config:deny-get-config -> Denies the get_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:deny-get-config" + ] + }, + { + "description": "config:deny-set-config -> Denies the set_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:deny-set-config" + ] + }, + { + "description": "database:allow-get-all-playlists -> Enables the get_all_playlists command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-get-all-playlists" + ] + }, + { + "description": "database:allow-get-all-tracks -> Enables the get_all_tracks command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-get-all-tracks" + ] + }, + { + "description": "database:allow-get-cover-as-base64 -> Enables the get_cover_as_base64 command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-get-cover-as-base64" + ] + }, + { + "description": "database:allow-import-tracks-to-library -> Enables the import_tracks_to_library command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-import-tracks-to-library" + ] + }, + { + "description": "database:deny-get-all-playlists -> Denies the get_all_playlists command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-get-all-playlists" + ] + }, + { + "description": "database:deny-get-all-tracks -> Denies the get_all_tracks command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-get-all-tracks" + ] + }, + { + "description": "database:deny-get-cover-as-base64 -> Denies the get_cover_as_base64 command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-get-cover-as-base64" + ] + }, + { + "description": "database:deny-import-tracks-to-library -> Denies the import_tracks_to_library command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-import-tracks-to-library" + ] + }, + { + "description": "default-view:allow-set -> Enables the set command without any pre-configured scope.", + "type": "string", + "enum": [ + "default-view:allow-set" + ] + }, + { + "description": "default-view:deny-set -> Denies the set command without any pre-configured scope.", + "type": "string", + "enum": [ + "default-view:deny-set" + ] + }, + { + "description": "dialog:allow-ask -> Enables the ask command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-ask" + ] + }, + { + "description": "dialog:allow-confirm -> Enables the confirm command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-confirm" + ] + }, + { + "description": "dialog:allow-message -> Enables the message command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-message" + ] + }, + { + "description": "dialog:allow-open -> Enables the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-open" + ] + }, + { + "description": "dialog:allow-save -> Enables the save command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-save" + ] + }, + { + "description": "dialog:deny-ask -> Denies the ask command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-ask" + ] + }, + { + "description": "dialog:deny-confirm -> Denies the confirm command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-confirm" + ] + }, + { + "description": "dialog:deny-message -> Denies the message command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-message" + ] + }, + { + "description": "dialog:deny-open -> Denies the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-open" + ] + }, + { + "description": "dialog:deny-save -> Denies the save command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-save" + ] + }, + { + "description": "event:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "event:default" + ] + }, + { + "description": "event:allow-emit -> Enables the emit command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-emit" + ] + }, + { + "description": "event:allow-emit-to -> Enables the emit_to command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-emit-to" + ] + }, + { + "description": "event:allow-listen -> Enables the listen command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-listen" + ] + }, + { + "description": "event:allow-unlisten -> Enables the unlisten command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-unlisten" + ] + }, + { + "description": "event:deny-emit -> Denies the emit command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-emit" + ] + }, + { + "description": "event:deny-emit-to -> Denies the emit_to command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-emit-to" + ] + }, + { + "description": "event:deny-listen -> Denies the listen command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-listen" + ] + }, + { + "description": "event:deny-unlisten -> Denies the unlisten command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-unlisten" + ] + }, + { + "description": "log:default -> Allows the log command", + "type": "string", + "enum": [ + "log:default" + ] + }, + { + "description": "log:allow-log -> Enables the log command without any pre-configured scope.", + "type": "string", + "enum": [ + "log:allow-log" + ] + }, + { + "description": "log:deny-log -> Denies the log command without any pre-configured scope.", + "type": "string", + "enum": [ + "log:deny-log" + ] + }, + { + "description": "menu:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "menu:default" + ] + }, + { + "description": "menu:allow-append -> Enables the append command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-append" + ] + }, + { + "description": "menu:allow-create-default -> Enables the create_default command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-create-default" + ] + }, + { + "description": "menu:allow-get -> Enables the get command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-get" + ] + }, + { + "description": "menu:allow-insert -> Enables the insert command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-insert" + ] + }, + { + "description": "menu:allow-is-checked -> Enables the is_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-is-checked" + ] + }, + { + "description": "menu:allow-is-enabled -> Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-is-enabled" + ] + }, + { + "description": "menu:allow-items -> Enables the items command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-items" + ] + }, + { + "description": "menu:allow-new -> Enables the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-new" + ] + }, + { + "description": "menu:allow-popup -> Enables the popup command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-popup" + ] + }, + { + "description": "menu:allow-prepend -> Enables the prepend command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-prepend" + ] + }, + { + "description": "menu:allow-remove -> Enables the remove command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-remove" + ] + }, + { + "description": "menu:allow-remove-at -> Enables the remove_at command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-remove-at" + ] + }, + { + "description": "menu:allow-set-accelerator -> Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-accelerator" + ] + }, + { + "description": "menu:allow-set-as-app-menu -> Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-app-menu" + ] + }, + { + "description": "menu:allow-set-as-help-menu-for-nsapp -> Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-help-menu-for-nsapp" + ] + }, + { + "description": "menu:allow-set-as-window-menu -> Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-window-menu" + ] + }, + { + "description": "menu:allow-set-as-windows-menu-for-nsapp -> Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-windows-menu-for-nsapp" + ] + }, + { + "description": "menu:allow-set-checked -> Enables the set_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-checked" + ] + }, + { + "description": "menu:allow-set-enabled -> Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-enabled" + ] + }, + { + "description": "menu:allow-set-icon -> Enables the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-icon" + ] + }, + { + "description": "menu:allow-set-text -> Enables the set_text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-text" + ] + }, + { + "description": "menu:allow-text -> Enables the text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-text" + ] + }, + { + "description": "menu:deny-append -> Denies the append command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-append" + ] + }, + { + "description": "menu:deny-create-default -> Denies the create_default command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-create-default" + ] + }, + { + "description": "menu:deny-get -> Denies the get command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-get" + ] + }, + { + "description": "menu:deny-insert -> Denies the insert command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-insert" + ] + }, + { + "description": "menu:deny-is-checked -> Denies the is_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-is-checked" + ] + }, + { + "description": "menu:deny-is-enabled -> Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-is-enabled" + ] + }, + { + "description": "menu:deny-items -> Denies the items command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-items" + ] + }, + { + "description": "menu:deny-new -> Denies the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-new" + ] + }, + { + "description": "menu:deny-popup -> Denies the popup command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-popup" + ] + }, + { + "description": "menu:deny-prepend -> Denies the prepend command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-prepend" + ] + }, + { + "description": "menu:deny-remove -> Denies the remove command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-remove" + ] + }, + { + "description": "menu:deny-remove-at -> Denies the remove_at command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-remove-at" + ] + }, + { + "description": "menu:deny-set-accelerator -> Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-accelerator" + ] + }, + { + "description": "menu:deny-set-as-app-menu -> Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-app-menu" + ] + }, + { + "description": "menu:deny-set-as-help-menu-for-nsapp -> Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-help-menu-for-nsapp" + ] + }, + { + "description": "menu:deny-set-as-window-menu -> Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-window-menu" + ] + }, + { + "description": "menu:deny-set-as-windows-menu-for-nsapp -> Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-windows-menu-for-nsapp" + ] + }, + { + "description": "menu:deny-set-checked -> Denies the set_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-checked" + ] + }, + { + "description": "menu:deny-set-enabled -> Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-enabled" + ] + }, + { + "description": "menu:deny-set-icon -> Denies the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-icon" + ] + }, + { + "description": "menu:deny-set-text -> Denies the set_text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-text" + ] + }, + { + "description": "menu:deny-text -> Denies the text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-text" + ] + }, + { + "description": "os:allow-arch -> Enables the arch command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-arch" + ] + }, + { + "description": "os:allow-exe-extension -> Enables the exe_extension command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-exe-extension" + ] + }, + { + "description": "os:allow-family -> Enables the family command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-family" + ] + }, + { + "description": "os:allow-hostname -> Enables the hostname command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-hostname" + ] + }, + { + "description": "os:allow-locale -> Enables the locale command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-locale" + ] + }, + { + "description": "os:allow-os-type -> Enables the os_type command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-os-type" + ] + }, + { + "description": "os:allow-platform -> Enables the platform command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-platform" + ] + }, + { + "description": "os:allow-version -> Enables the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-version" + ] + }, + { + "description": "os:deny-arch -> Denies the arch command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-arch" + ] + }, + { + "description": "os:deny-exe-extension -> Denies the exe_extension command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-exe-extension" + ] + }, + { + "description": "os:deny-family -> Denies the family command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-family" + ] + }, + { + "description": "os:deny-hostname -> Denies the hostname command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-hostname" + ] + }, + { + "description": "os:deny-locale -> Denies the locale command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-locale" + ] + }, + { + "description": "os:deny-os-type -> Denies the os_type command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-os-type" + ] + }, + { + "description": "os:deny-platform -> Denies the platform command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-platform" + ] + }, + { + "description": "os:deny-version -> Denies the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-version" + ] + }, + { + "description": "path:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "path:default" + ] + }, + { + "description": "path:allow-basename -> Enables the basename command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-basename" + ] + }, + { + "description": "path:allow-dirname -> Enables the dirname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-dirname" + ] + }, + { + "description": "path:allow-extname -> Enables the extname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-extname" + ] + }, + { + "description": "path:allow-is-absolute -> Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-is-absolute" + ] + }, + { + "description": "path:allow-join -> Enables the join command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-join" + ] + }, + { + "description": "path:allow-normalize -> Enables the normalize command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-normalize" + ] + }, + { + "description": "path:allow-resolve -> Enables the resolve command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-resolve" + ] + }, + { + "description": "path:allow-resolve-directory -> Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-resolve-directory" + ] + }, + { + "description": "path:deny-basename -> Denies the basename command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-basename" + ] + }, + { + "description": "path:deny-dirname -> Denies the dirname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-dirname" + ] + }, + { + "description": "path:deny-extname -> Denies the extname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-extname" + ] + }, + { + "description": "path:deny-is-absolute -> Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-is-absolute" + ] + }, + { + "description": "path:deny-join -> Denies the join command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-join" + ] + }, + { + "description": "path:deny-normalize -> Denies the normalize command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-normalize" + ] + }, + { + "description": "path:deny-resolve -> Denies the resolve command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-resolve" + ] + }, + { + "description": "path:deny-resolve-directory -> Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-resolve-directory" + ] + }, + { + "description": "resources:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "resources:default" + ] + }, + { + "description": "resources:allow-close -> Enables the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "resources:allow-close" + ] + }, + { + "description": "resources:deny-close -> Denies the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "resources:deny-close" + ] + }, + { + "description": "shell:allow-execute -> Enables the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-execute" + ] + }, + { + "description": "shell:allow-kill -> Enables the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-kill" + ] + }, + { + "description": "shell:allow-open -> Enables the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-open" + ] + }, + { + "description": "shell:allow-stdin-write -> Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-stdin-write" + ] + }, + { + "description": "shell:deny-execute -> Denies the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-execute" + ] + }, + { + "description": "shell:deny-kill -> Denies the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-kill" + ] + }, + { + "description": "shell:deny-open -> Denies the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-open" + ] + }, + { + "description": "shell:deny-stdin-write -> Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-stdin-write" + ] + }, + { + "description": "sleepblocker:allow-disable -> Enables the disable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:allow-disable" + ] + }, + { + "description": "sleepblocker:allow-enable -> Enables the enable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:allow-enable" + ] + }, + { + "description": "sleepblocker:deny-disable -> Denies the disable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:deny-disable" + ] + }, + { + "description": "sleepblocker:deny-enable -> Denies the enable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:deny-enable" + ] + }, + { + "description": "tray:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "tray:default" + ] + }, + { + "description": "tray:allow-new -> Enables the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-new" + ] + }, + { + "description": "tray:allow-set-icon -> Enables the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-icon" + ] + }, + { + "description": "tray:allow-set-icon-as-template -> Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-icon-as-template" + ] + }, + { + "description": "tray:allow-set-menu -> Enables the set_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-menu" + ] + }, + { + "description": "tray:allow-set-show-menu-on-left-click -> Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-show-menu-on-left-click" + ] + }, + { + "description": "tray:allow-set-temp-dir-path -> Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-temp-dir-path" + ] + }, + { + "description": "tray:allow-set-title -> Enables the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-title" + ] + }, + { + "description": "tray:allow-set-tooltip -> Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-tooltip" + ] + }, + { + "description": "tray:allow-set-visible -> Enables the set_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-visible" + ] + }, + { + "description": "tray:deny-new -> Denies the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-new" + ] + }, + { + "description": "tray:deny-set-icon -> Denies the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-icon" + ] + }, + { + "description": "tray:deny-set-icon-as-template -> Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-icon-as-template" + ] + }, + { + "description": "tray:deny-set-menu -> Denies the set_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-menu" + ] + }, + { + "description": "tray:deny-set-show-menu-on-left-click -> Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-show-menu-on-left-click" + ] + }, + { + "description": "tray:deny-set-temp-dir-path -> Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-temp-dir-path" + ] + }, + { + "description": "tray:deny-set-title -> Denies the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-title" + ] + }, + { + "description": "tray:deny-set-tooltip -> Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-tooltip" + ] + }, + { + "description": "tray:deny-set-visible -> Denies the set_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-visible" + ] + }, + { + "description": "webview:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "webview:default" + ] + }, + { + "description": "webview:allow-create-webview -> Enables the create_webview command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-create-webview" + ] + }, + { + "description": "webview:allow-create-webview-window -> Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-create-webview-window" + ] + }, + { + "description": "webview:allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-internal-toggle-devtools" + ] + }, + { + "description": "webview:allow-print -> Enables the print command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-print" + ] + }, + { + "description": "webview:allow-reparent -> Enables the reparent command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-reparent" + ] + }, + { + "description": "webview:allow-set-webview-focus -> Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-set-webview-focus" + ] + }, + { + "description": "webview:allow-set-webview-position -> Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-set-webview-position" + ] + }, + { + "description": "webview:allow-set-webview-size -> Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-set-webview-size" + ] + }, + { + "description": "webview:allow-webview-close -> Enables the webview_close command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-webview-close" + ] + }, + { + "description": "webview:allow-webview-position -> Enables the webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-webview-position" + ] + }, + { + "description": "webview:allow-webview-size -> Enables the webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-webview-size" + ] + }, + { + "description": "webview:deny-create-webview -> Denies the create_webview command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-create-webview" + ] + }, + { + "description": "webview:deny-create-webview-window -> Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-create-webview-window" + ] + }, + { + "description": "webview:deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-internal-toggle-devtools" + ] + }, + { + "description": "webview:deny-print -> Denies the print command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-print" + ] + }, + { + "description": "webview:deny-reparent -> Denies the reparent command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-reparent" + ] + }, + { + "description": "webview:deny-set-webview-focus -> Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-set-webview-focus" + ] + }, + { + "description": "webview:deny-set-webview-position -> Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-set-webview-position" + ] + }, + { + "description": "webview:deny-set-webview-size -> Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-set-webview-size" + ] + }, + { + "description": "webview:deny-webview-close -> Denies the webview_close command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-webview-close" + ] + }, + { + "description": "webview:deny-webview-position -> Denies the webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-webview-position" + ] + }, + { + "description": "webview:deny-webview-size -> Denies the webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-webview-size" + ] + }, + { + "description": "window:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "window:default" + ] + }, + { + "description": "window:allow-available-monitors -> Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-available-monitors" + ] + }, + { + "description": "window:allow-center -> Enables the center command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-center" + ] + }, + { + "description": "window:allow-close -> Enables the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-close" + ] + }, + { + "description": "window:allow-create -> Enables the create command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-create" + ] + }, + { + "description": "window:allow-current-monitor -> Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-current-monitor" + ] + }, + { + "description": "window:allow-destroy -> Enables the destroy command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-destroy" + ] + }, + { + "description": "window:allow-hide -> Enables the hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-hide" + ] + }, + { + "description": "window:allow-inner-position -> Enables the inner_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-inner-position" + ] + }, + { + "description": "window:allow-inner-size -> Enables the inner_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-inner-size" + ] + }, + { + "description": "window:allow-internal-toggle-maximize -> Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-internal-toggle-maximize" + ] + }, + { + "description": "window:allow-is-closable -> Enables the is_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-closable" + ] + }, + { + "description": "window:allow-is-decorated -> Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-decorated" + ] + }, + { + "description": "window:allow-is-focused -> Enables the is_focused command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-focused" + ] + }, + { + "description": "window:allow-is-fullscreen -> Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-fullscreen" + ] + }, + { + "description": "window:allow-is-maximizable -> Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-maximizable" + ] + }, + { + "description": "window:allow-is-maximized -> Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-maximized" + ] + }, + { + "description": "window:allow-is-minimizable -> Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-minimizable" + ] + }, + { + "description": "window:allow-is-minimized -> Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-minimized" + ] + }, + { + "description": "window:allow-is-resizable -> Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-resizable" + ] + }, + { + "description": "window:allow-is-visible -> Enables the is_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-visible" + ] + }, + { + "description": "window:allow-maximize -> Enables the maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-maximize" + ] + }, + { + "description": "window:allow-minimize -> Enables the minimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-minimize" + ] + }, + { + "description": "window:allow-outer-position -> Enables the outer_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-outer-position" + ] + }, + { + "description": "window:allow-outer-size -> Enables the outer_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-outer-size" + ] + }, + { + "description": "window:allow-primary-monitor -> Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-primary-monitor" + ] + }, + { + "description": "window:allow-request-user-attention -> Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-request-user-attention" + ] + }, + { + "description": "window:allow-scale-factor -> Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-scale-factor" + ] + }, + { + "description": "window:allow-set-always-on-bottom -> Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-always-on-bottom" + ] + }, + { + "description": "window:allow-set-always-on-top -> Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-always-on-top" + ] + }, + { + "description": "window:allow-set-closable -> Enables the set_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-closable" + ] + }, + { + "description": "window:allow-set-content-protected -> Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-content-protected" + ] + }, + { + "description": "window:allow-set-cursor-grab -> Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-grab" + ] + }, + { + "description": "window:allow-set-cursor-icon -> Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-icon" + ] + }, + { + "description": "window:allow-set-cursor-position -> Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-position" + ] + }, + { + "description": "window:allow-set-cursor-visible -> Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-visible" + ] + }, + { + "description": "window:allow-set-decorations -> Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-decorations" + ] + }, + { + "description": "window:allow-set-effects -> Enables the set_effects command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-effects" + ] + }, + { + "description": "window:allow-set-focus -> Enables the set_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-focus" + ] + }, + { + "description": "window:allow-set-fullscreen -> Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-fullscreen" + ] + }, + { + "description": "window:allow-set-icon -> Enables the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-icon" + ] + }, + { + "description": "window:allow-set-ignore-cursor-events -> Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-ignore-cursor-events" + ] + }, + { + "description": "window:allow-set-max-size -> Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-max-size" + ] + }, + { + "description": "window:allow-set-maximizable -> Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-maximizable" + ] + }, + { + "description": "window:allow-set-min-size -> Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-min-size" + ] + }, + { + "description": "window:allow-set-minimizable -> Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-minimizable" + ] + }, + { + "description": "window:allow-set-position -> Enables the set_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-position" + ] + }, + { + "description": "window:allow-set-progress-bar -> Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-progress-bar" + ] + }, + { + "description": "window:allow-set-resizable -> Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-resizable" + ] + }, + { + "description": "window:allow-set-shadow -> Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-shadow" + ] + }, + { + "description": "window:allow-set-size -> Enables the set_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-size" + ] + }, + { + "description": "window:allow-set-skip-taskbar -> Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-skip-taskbar" + ] + }, + { + "description": "window:allow-set-title -> Enables the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-title" + ] + }, + { + "description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-visible-on-all-workspaces" + ] + }, + { + "description": "window:allow-show -> Enables the show command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-show" + ] + }, + { + "description": "window:allow-start-dragging -> Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-start-dragging" + ] + }, + { + "description": "window:allow-theme -> Enables the theme command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-theme" + ] + }, + { + "description": "window:allow-title -> Enables the title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-title" + ] + }, + { + "description": "window:allow-toggle-maximize -> Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-toggle-maximize" + ] + }, + { + "description": "window:allow-unmaximize -> Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-unmaximize" + ] + }, + { + "description": "window:allow-unminimize -> Enables the unminimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-unminimize" + ] + }, + { + "description": "window:deny-available-monitors -> Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-available-monitors" + ] + }, + { + "description": "window:deny-center -> Denies the center command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-center" + ] + }, + { + "description": "window:deny-close -> Denies the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-close" + ] + }, + { + "description": "window:deny-create -> Denies the create command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-create" + ] + }, + { + "description": "window:deny-current-monitor -> Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-current-monitor" + ] + }, + { + "description": "window:deny-destroy -> Denies the destroy command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-destroy" + ] + }, + { + "description": "window:deny-hide -> Denies the hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-hide" + ] + }, + { + "description": "window:deny-inner-position -> Denies the inner_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-inner-position" + ] + }, + { + "description": "window:deny-inner-size -> Denies the inner_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-inner-size" + ] + }, + { + "description": "window:deny-internal-toggle-maximize -> Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-internal-toggle-maximize" + ] + }, + { + "description": "window:deny-is-closable -> Denies the is_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-closable" + ] + }, + { + "description": "window:deny-is-decorated -> Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-decorated" + ] + }, + { + "description": "window:deny-is-focused -> Denies the is_focused command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-focused" + ] + }, + { + "description": "window:deny-is-fullscreen -> Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-fullscreen" + ] + }, + { + "description": "window:deny-is-maximizable -> Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-maximizable" + ] + }, + { + "description": "window:deny-is-maximized -> Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-maximized" + ] + }, + { + "description": "window:deny-is-minimizable -> Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-minimizable" + ] + }, + { + "description": "window:deny-is-minimized -> Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-minimized" + ] + }, + { + "description": "window:deny-is-resizable -> Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-resizable" + ] + }, + { + "description": "window:deny-is-visible -> Denies the is_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-visible" + ] + }, + { + "description": "window:deny-maximize -> Denies the maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-maximize" + ] + }, + { + "description": "window:deny-minimize -> Denies the minimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-minimize" + ] + }, + { + "description": "window:deny-outer-position -> Denies the outer_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-outer-position" + ] + }, + { + "description": "window:deny-outer-size -> Denies the outer_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-outer-size" + ] + }, + { + "description": "window:deny-primary-monitor -> Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-primary-monitor" + ] + }, + { + "description": "window:deny-request-user-attention -> Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-request-user-attention" + ] + }, + { + "description": "window:deny-scale-factor -> Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-scale-factor" + ] + }, + { + "description": "window:deny-set-always-on-bottom -> Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-always-on-bottom" + ] + }, + { + "description": "window:deny-set-always-on-top -> Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-always-on-top" + ] + }, + { + "description": "window:deny-set-closable -> Denies the set_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-closable" + ] + }, + { + "description": "window:deny-set-content-protected -> Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-content-protected" + ] + }, + { + "description": "window:deny-set-cursor-grab -> Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-grab" + ] + }, + { + "description": "window:deny-set-cursor-icon -> Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-icon" + ] + }, + { + "description": "window:deny-set-cursor-position -> Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-position" + ] + }, + { + "description": "window:deny-set-cursor-visible -> Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-visible" + ] + }, + { + "description": "window:deny-set-decorations -> Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-decorations" + ] + }, + { + "description": "window:deny-set-effects -> Denies the set_effects command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-effects" + ] + }, + { + "description": "window:deny-set-focus -> Denies the set_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-focus" + ] + }, + { + "description": "window:deny-set-fullscreen -> Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-fullscreen" + ] + }, + { + "description": "window:deny-set-icon -> Denies the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-icon" + ] + }, + { + "description": "window:deny-set-ignore-cursor-events -> Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-ignore-cursor-events" + ] + }, + { + "description": "window:deny-set-max-size -> Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-max-size" + ] + }, + { + "description": "window:deny-set-maximizable -> Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-maximizable" + ] + }, + { + "description": "window:deny-set-min-size -> Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-min-size" + ] + }, + { + "description": "window:deny-set-minimizable -> Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-minimizable" + ] + }, + { + "description": "window:deny-set-position -> Denies the set_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-position" + ] + }, + { + "description": "window:deny-set-progress-bar -> Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-progress-bar" + ] + }, + { + "description": "window:deny-set-resizable -> Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-resizable" + ] + }, + { + "description": "window:deny-set-shadow -> Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-shadow" + ] + }, + { + "description": "window:deny-set-size -> Denies the set_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-size" + ] + }, + { + "description": "window:deny-set-skip-taskbar -> Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-skip-taskbar" + ] + }, + { + "description": "window:deny-set-title -> Denies the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-title" + ] + }, + { + "description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-visible-on-all-workspaces" + ] + }, + { + "description": "window:deny-show -> Denies the show command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-show" + ] + }, + { + "description": "window:deny-start-dragging -> Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-start-dragging" + ] + }, + { + "description": "window:deny-theme -> Denies the theme command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-theme" + ] + }, + { + "description": "window:deny-title -> Denies the title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-title" + ] + }, + { + "description": "window:deny-toggle-maximize -> Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-toggle-maximize" + ] + }, + { + "description": "window:deny-unmaximize -> Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-unmaximize" + ] + }, + { + "description": "window:deny-unminimize -> Denies the unminimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-unminimize" + ] + }, + { + "description": "window-state:allow-restore-window-state -> Enables the restore_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:allow-restore-window-state" + ] + }, + { + "description": "window-state:allow-save-window-state -> Enables the save_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:allow-save-window-state" + ] + }, + { + "description": "window-state:deny-restore-window-state -> Denies the restore_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:deny-restore-window-state" + ] + }, + { + "description": "window-state:deny-save-window-state -> Denies the save_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:deny-save-window-state" + ] + } + ] + }, + "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" + ] + } + ] + }, + "ShellAllowedArg": { + "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": { + "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\n[regex]: https://docs.rs/regex/latest/regex/#syntax", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellAllowedArgs": { + "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 [`ShellAllowedArg`] 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 [`ShellAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellAllowedArg" + } + } + ] + } + } +} \ 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 000000000..fa3a2b08c --- /dev/null +++ b/src-tauri/gen/schemas/macOS-schema.json @@ -0,0 +1,2563 @@ +{ + "$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": "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 separate windows or plugins functionality from each other at runtime.\n\nIf a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create trust groups and reduce impact of vulnerabilities in certain plugins or windows. Windows can be added to a capability by exact name or glob patterns like *, admin-* or main-window.", + "type": "object", + "required": [ + "identifier", + "permissions", + "windows" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.", + "type": "string" + }, + "description": { + "description": "Description of the capability.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.", + "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 uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that uses this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + } + }, + "platforms": { + "description": "Target platforms this capability applies. By default all platforms applies.", + "default": [ + "linux", + "macOS", + "windows", + "android", + "iOS" + ], + "type": "array", + "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. Can use glob patterns.", + "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", + "oneOf": [ + { + "type": "object", + "required": [ + "identifier" + ], + "properties": { + "identifier": { + "oneOf": [ + { + "description": "shell:allow-execute -> Enables the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-execute" + ] + }, + { + "description": "shell:allow-kill -> Enables the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-kill" + ] + }, + { + "description": "shell:allow-open -> Enables the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-open" + ] + }, + { + "description": "shell:allow-stdin-write -> Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-stdin-write" + ] + }, + { + "description": "shell:deny-execute -> Denies the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-execute" + ] + }, + { + "description": "shell:deny-kill -> Denies the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-kill" + ] + }, + { + "description": "shell:deny-open -> Denies the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-open" + ] + }, + { + "description": "shell:deny-stdin-write -> Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-stdin-write" + ] + } + ] + }, + "allow": { + "items": { + "title": "Entry", + "description": "A command allowed to be executed by the webview API.", + "type": "object", + "required": [ + "args", + "command", + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellAllowedArgs" + } + ] + }, + "command": { + "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`, `$APP`, `$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" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + } + } + }, + "deny": { + "items": { + "title": "Entry", + "description": "A command allowed to be executed by the webview API.", + "type": "object", + "required": [ + "args", + "command", + "name", + "sidecar" + ], + "properties": { + "args": { + "description": "The allowed arguments for the command execution.", + "allOf": [ + { + "$ref": "#/definitions/ShellAllowedArgs" + } + ] + }, + "command": { + "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`, `$APP`, `$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" + }, + "sidecar": { + "description": "If this command is a sidecar command.", + "type": "boolean" + } + } + } + } + } + } + ] + } + ] + }, + "Identifier": { + "oneOf": [ + { + "description": "app:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "app:default" + ] + }, + { + "description": "app:allow-app-hide -> Enables the app_hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-app-hide" + ] + }, + { + "description": "app:allow-app-show -> Enables the app_show command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-app-show" + ] + }, + { + "description": "app:allow-name -> Enables the name command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-name" + ] + }, + { + "description": "app:allow-tauri-version -> Enables the tauri_version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-tauri-version" + ] + }, + { + "description": "app:allow-version -> Enables the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:allow-version" + ] + }, + { + "description": "app:deny-app-hide -> Denies the app_hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-app-hide" + ] + }, + { + "description": "app:deny-app-show -> Denies the app_show command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-app-show" + ] + }, + { + "description": "app:deny-name -> Denies the name command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-name" + ] + }, + { + "description": "app:deny-tauri-version -> Denies the tauri_version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-tauri-version" + ] + }, + { + "description": "app:deny-version -> Denies the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "app:deny-version" + ] + }, + { + "description": "config:allow-get-config -> Enables the get_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:allow-get-config" + ] + }, + { + "description": "config:allow-set-config -> Enables the set_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:allow-set-config" + ] + }, + { + "description": "config:deny-get-config -> Denies the get_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:deny-get-config" + ] + }, + { + "description": "config:deny-set-config -> Denies the set_config command without any pre-configured scope.", + "type": "string", + "enum": [ + "config:deny-set-config" + ] + }, + { + "description": "database:allow-get-all-playlists -> Enables the get_all_playlists command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-get-all-playlists" + ] + }, + { + "description": "database:allow-get-all-tracks -> Enables the get_all_tracks command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-get-all-tracks" + ] + }, + { + "description": "database:allow-get-cover-as-base64 -> Enables the get_cover_as_base64 command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-get-cover-as-base64" + ] + }, + { + "description": "database:allow-import-tracks-to-library -> Enables the import_tracks_to_library command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:allow-import-tracks-to-library" + ] + }, + { + "description": "database:deny-get-all-playlists -> Denies the get_all_playlists command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-get-all-playlists" + ] + }, + { + "description": "database:deny-get-all-tracks -> Denies the get_all_tracks command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-get-all-tracks" + ] + }, + { + "description": "database:deny-get-cover-as-base64 -> Denies the get_cover_as_base64 command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-get-cover-as-base64" + ] + }, + { + "description": "database:deny-import-tracks-to-library -> Denies the import_tracks_to_library command without any pre-configured scope.", + "type": "string", + "enum": [ + "database:deny-import-tracks-to-library" + ] + }, + { + "description": "default-view:allow-set -> Enables the set command without any pre-configured scope.", + "type": "string", + "enum": [ + "default-view:allow-set" + ] + }, + { + "description": "default-view:deny-set -> Denies the set command without any pre-configured scope.", + "type": "string", + "enum": [ + "default-view:deny-set" + ] + }, + { + "description": "dialog:allow-ask -> Enables the ask command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-ask" + ] + }, + { + "description": "dialog:allow-confirm -> Enables the confirm command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-confirm" + ] + }, + { + "description": "dialog:allow-message -> Enables the message command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-message" + ] + }, + { + "description": "dialog:allow-open -> Enables the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-open" + ] + }, + { + "description": "dialog:allow-save -> Enables the save command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:allow-save" + ] + }, + { + "description": "dialog:deny-ask -> Denies the ask command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-ask" + ] + }, + { + "description": "dialog:deny-confirm -> Denies the confirm command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-confirm" + ] + }, + { + "description": "dialog:deny-message -> Denies the message command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-message" + ] + }, + { + "description": "dialog:deny-open -> Denies the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-open" + ] + }, + { + "description": "dialog:deny-save -> Denies the save command without any pre-configured scope.", + "type": "string", + "enum": [ + "dialog:deny-save" + ] + }, + { + "description": "event:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "event:default" + ] + }, + { + "description": "event:allow-emit -> Enables the emit command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-emit" + ] + }, + { + "description": "event:allow-emit-to -> Enables the emit_to command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-emit-to" + ] + }, + { + "description": "event:allow-listen -> Enables the listen command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-listen" + ] + }, + { + "description": "event:allow-unlisten -> Enables the unlisten command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:allow-unlisten" + ] + }, + { + "description": "event:deny-emit -> Denies the emit command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-emit" + ] + }, + { + "description": "event:deny-emit-to -> Denies the emit_to command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-emit-to" + ] + }, + { + "description": "event:deny-listen -> Denies the listen command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-listen" + ] + }, + { + "description": "event:deny-unlisten -> Denies the unlisten command without any pre-configured scope.", + "type": "string", + "enum": [ + "event:deny-unlisten" + ] + }, + { + "description": "log:default -> Allows the log command", + "type": "string", + "enum": [ + "log:default" + ] + }, + { + "description": "log:allow-log -> Enables the log command without any pre-configured scope.", + "type": "string", + "enum": [ + "log:allow-log" + ] + }, + { + "description": "log:deny-log -> Denies the log command without any pre-configured scope.", + "type": "string", + "enum": [ + "log:deny-log" + ] + }, + { + "description": "menu:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "menu:default" + ] + }, + { + "description": "menu:allow-append -> Enables the append command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-append" + ] + }, + { + "description": "menu:allow-create-default -> Enables the create_default command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-create-default" + ] + }, + { + "description": "menu:allow-get -> Enables the get command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-get" + ] + }, + { + "description": "menu:allow-insert -> Enables the insert command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-insert" + ] + }, + { + "description": "menu:allow-is-checked -> Enables the is_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-is-checked" + ] + }, + { + "description": "menu:allow-is-enabled -> Enables the is_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-is-enabled" + ] + }, + { + "description": "menu:allow-items -> Enables the items command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-items" + ] + }, + { + "description": "menu:allow-new -> Enables the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-new" + ] + }, + { + "description": "menu:allow-popup -> Enables the popup command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-popup" + ] + }, + { + "description": "menu:allow-prepend -> Enables the prepend command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-prepend" + ] + }, + { + "description": "menu:allow-remove -> Enables the remove command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-remove" + ] + }, + { + "description": "menu:allow-remove-at -> Enables the remove_at command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-remove-at" + ] + }, + { + "description": "menu:allow-set-accelerator -> Enables the set_accelerator command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-accelerator" + ] + }, + { + "description": "menu:allow-set-as-app-menu -> Enables the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-app-menu" + ] + }, + { + "description": "menu:allow-set-as-help-menu-for-nsapp -> Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-help-menu-for-nsapp" + ] + }, + { + "description": "menu:allow-set-as-window-menu -> Enables the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-window-menu" + ] + }, + { + "description": "menu:allow-set-as-windows-menu-for-nsapp -> Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-as-windows-menu-for-nsapp" + ] + }, + { + "description": "menu:allow-set-checked -> Enables the set_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-checked" + ] + }, + { + "description": "menu:allow-set-enabled -> Enables the set_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-enabled" + ] + }, + { + "description": "menu:allow-set-icon -> Enables the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-icon" + ] + }, + { + "description": "menu:allow-set-text -> Enables the set_text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-set-text" + ] + }, + { + "description": "menu:allow-text -> Enables the text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:allow-text" + ] + }, + { + "description": "menu:deny-append -> Denies the append command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-append" + ] + }, + { + "description": "menu:deny-create-default -> Denies the create_default command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-create-default" + ] + }, + { + "description": "menu:deny-get -> Denies the get command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-get" + ] + }, + { + "description": "menu:deny-insert -> Denies the insert command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-insert" + ] + }, + { + "description": "menu:deny-is-checked -> Denies the is_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-is-checked" + ] + }, + { + "description": "menu:deny-is-enabled -> Denies the is_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-is-enabled" + ] + }, + { + "description": "menu:deny-items -> Denies the items command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-items" + ] + }, + { + "description": "menu:deny-new -> Denies the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-new" + ] + }, + { + "description": "menu:deny-popup -> Denies the popup command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-popup" + ] + }, + { + "description": "menu:deny-prepend -> Denies the prepend command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-prepend" + ] + }, + { + "description": "menu:deny-remove -> Denies the remove command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-remove" + ] + }, + { + "description": "menu:deny-remove-at -> Denies the remove_at command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-remove-at" + ] + }, + { + "description": "menu:deny-set-accelerator -> Denies the set_accelerator command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-accelerator" + ] + }, + { + "description": "menu:deny-set-as-app-menu -> Denies the set_as_app_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-app-menu" + ] + }, + { + "description": "menu:deny-set-as-help-menu-for-nsapp -> Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-help-menu-for-nsapp" + ] + }, + { + "description": "menu:deny-set-as-window-menu -> Denies the set_as_window_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-window-menu" + ] + }, + { + "description": "menu:deny-set-as-windows-menu-for-nsapp -> Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-as-windows-menu-for-nsapp" + ] + }, + { + "description": "menu:deny-set-checked -> Denies the set_checked command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-checked" + ] + }, + { + "description": "menu:deny-set-enabled -> Denies the set_enabled command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-enabled" + ] + }, + { + "description": "menu:deny-set-icon -> Denies the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-icon" + ] + }, + { + "description": "menu:deny-set-text -> Denies the set_text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-set-text" + ] + }, + { + "description": "menu:deny-text -> Denies the text command without any pre-configured scope.", + "type": "string", + "enum": [ + "menu:deny-text" + ] + }, + { + "description": "os:allow-arch -> Enables the arch command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-arch" + ] + }, + { + "description": "os:allow-exe-extension -> Enables the exe_extension command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-exe-extension" + ] + }, + { + "description": "os:allow-family -> Enables the family command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-family" + ] + }, + { + "description": "os:allow-hostname -> Enables the hostname command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-hostname" + ] + }, + { + "description": "os:allow-locale -> Enables the locale command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-locale" + ] + }, + { + "description": "os:allow-os-type -> Enables the os_type command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-os-type" + ] + }, + { + "description": "os:allow-platform -> Enables the platform command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-platform" + ] + }, + { + "description": "os:allow-version -> Enables the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:allow-version" + ] + }, + { + "description": "os:deny-arch -> Denies the arch command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-arch" + ] + }, + { + "description": "os:deny-exe-extension -> Denies the exe_extension command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-exe-extension" + ] + }, + { + "description": "os:deny-family -> Denies the family command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-family" + ] + }, + { + "description": "os:deny-hostname -> Denies the hostname command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-hostname" + ] + }, + { + "description": "os:deny-locale -> Denies the locale command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-locale" + ] + }, + { + "description": "os:deny-os-type -> Denies the os_type command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-os-type" + ] + }, + { + "description": "os:deny-platform -> Denies the platform command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-platform" + ] + }, + { + "description": "os:deny-version -> Denies the version command without any pre-configured scope.", + "type": "string", + "enum": [ + "os:deny-version" + ] + }, + { + "description": "path:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "path:default" + ] + }, + { + "description": "path:allow-basename -> Enables the basename command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-basename" + ] + }, + { + "description": "path:allow-dirname -> Enables the dirname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-dirname" + ] + }, + { + "description": "path:allow-extname -> Enables the extname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-extname" + ] + }, + { + "description": "path:allow-is-absolute -> Enables the is_absolute command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-is-absolute" + ] + }, + { + "description": "path:allow-join -> Enables the join command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-join" + ] + }, + { + "description": "path:allow-normalize -> Enables the normalize command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-normalize" + ] + }, + { + "description": "path:allow-resolve -> Enables the resolve command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-resolve" + ] + }, + { + "description": "path:allow-resolve-directory -> Enables the resolve_directory command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:allow-resolve-directory" + ] + }, + { + "description": "path:deny-basename -> Denies the basename command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-basename" + ] + }, + { + "description": "path:deny-dirname -> Denies the dirname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-dirname" + ] + }, + { + "description": "path:deny-extname -> Denies the extname command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-extname" + ] + }, + { + "description": "path:deny-is-absolute -> Denies the is_absolute command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-is-absolute" + ] + }, + { + "description": "path:deny-join -> Denies the join command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-join" + ] + }, + { + "description": "path:deny-normalize -> Denies the normalize command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-normalize" + ] + }, + { + "description": "path:deny-resolve -> Denies the resolve command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-resolve" + ] + }, + { + "description": "path:deny-resolve-directory -> Denies the resolve_directory command without any pre-configured scope.", + "type": "string", + "enum": [ + "path:deny-resolve-directory" + ] + }, + { + "description": "resources:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "resources:default" + ] + }, + { + "description": "resources:allow-close -> Enables the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "resources:allow-close" + ] + }, + { + "description": "resources:deny-close -> Denies the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "resources:deny-close" + ] + }, + { + "description": "shell:allow-execute -> Enables the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-execute" + ] + }, + { + "description": "shell:allow-kill -> Enables the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-kill" + ] + }, + { + "description": "shell:allow-open -> Enables the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-open" + ] + }, + { + "description": "shell:allow-stdin-write -> Enables the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:allow-stdin-write" + ] + }, + { + "description": "shell:deny-execute -> Denies the execute command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-execute" + ] + }, + { + "description": "shell:deny-kill -> Denies the kill command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-kill" + ] + }, + { + "description": "shell:deny-open -> Denies the open command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-open" + ] + }, + { + "description": "shell:deny-stdin-write -> Denies the stdin_write command without any pre-configured scope.", + "type": "string", + "enum": [ + "shell:deny-stdin-write" + ] + }, + { + "description": "sleepblocker:allow-disable -> Enables the disable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:allow-disable" + ] + }, + { + "description": "sleepblocker:allow-enable -> Enables the enable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:allow-enable" + ] + }, + { + "description": "sleepblocker:deny-disable -> Denies the disable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:deny-disable" + ] + }, + { + "description": "sleepblocker:deny-enable -> Denies the enable command without any pre-configured scope.", + "type": "string", + "enum": [ + "sleepblocker:deny-enable" + ] + }, + { + "description": "tray:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "tray:default" + ] + }, + { + "description": "tray:allow-new -> Enables the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-new" + ] + }, + { + "description": "tray:allow-set-icon -> Enables the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-icon" + ] + }, + { + "description": "tray:allow-set-icon-as-template -> Enables the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-icon-as-template" + ] + }, + { + "description": "tray:allow-set-menu -> Enables the set_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-menu" + ] + }, + { + "description": "tray:allow-set-show-menu-on-left-click -> Enables the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-show-menu-on-left-click" + ] + }, + { + "description": "tray:allow-set-temp-dir-path -> Enables the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-temp-dir-path" + ] + }, + { + "description": "tray:allow-set-title -> Enables the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-title" + ] + }, + { + "description": "tray:allow-set-tooltip -> Enables the set_tooltip command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-tooltip" + ] + }, + { + "description": "tray:allow-set-visible -> Enables the set_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:allow-set-visible" + ] + }, + { + "description": "tray:deny-new -> Denies the new command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-new" + ] + }, + { + "description": "tray:deny-set-icon -> Denies the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-icon" + ] + }, + { + "description": "tray:deny-set-icon-as-template -> Denies the set_icon_as_template command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-icon-as-template" + ] + }, + { + "description": "tray:deny-set-menu -> Denies the set_menu command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-menu" + ] + }, + { + "description": "tray:deny-set-show-menu-on-left-click -> Denies the set_show_menu_on_left_click command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-show-menu-on-left-click" + ] + }, + { + "description": "tray:deny-set-temp-dir-path -> Denies the set_temp_dir_path command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-temp-dir-path" + ] + }, + { + "description": "tray:deny-set-title -> Denies the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-title" + ] + }, + { + "description": "tray:deny-set-tooltip -> Denies the set_tooltip command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-tooltip" + ] + }, + { + "description": "tray:deny-set-visible -> Denies the set_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "tray:deny-set-visible" + ] + }, + { + "description": "webview:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "webview:default" + ] + }, + { + "description": "webview:allow-create-webview -> Enables the create_webview command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-create-webview" + ] + }, + { + "description": "webview:allow-create-webview-window -> Enables the create_webview_window command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-create-webview-window" + ] + }, + { + "description": "webview:allow-internal-toggle-devtools -> Enables the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-internal-toggle-devtools" + ] + }, + { + "description": "webview:allow-print -> Enables the print command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-print" + ] + }, + { + "description": "webview:allow-reparent -> Enables the reparent command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-reparent" + ] + }, + { + "description": "webview:allow-set-webview-focus -> Enables the set_webview_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-set-webview-focus" + ] + }, + { + "description": "webview:allow-set-webview-position -> Enables the set_webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-set-webview-position" + ] + }, + { + "description": "webview:allow-set-webview-size -> Enables the set_webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-set-webview-size" + ] + }, + { + "description": "webview:allow-webview-close -> Enables the webview_close command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-webview-close" + ] + }, + { + "description": "webview:allow-webview-position -> Enables the webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-webview-position" + ] + }, + { + "description": "webview:allow-webview-size -> Enables the webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:allow-webview-size" + ] + }, + { + "description": "webview:deny-create-webview -> Denies the create_webview command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-create-webview" + ] + }, + { + "description": "webview:deny-create-webview-window -> Denies the create_webview_window command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-create-webview-window" + ] + }, + { + "description": "webview:deny-internal-toggle-devtools -> Denies the internal_toggle_devtools command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-internal-toggle-devtools" + ] + }, + { + "description": "webview:deny-print -> Denies the print command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-print" + ] + }, + { + "description": "webview:deny-reparent -> Denies the reparent command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-reparent" + ] + }, + { + "description": "webview:deny-set-webview-focus -> Denies the set_webview_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-set-webview-focus" + ] + }, + { + "description": "webview:deny-set-webview-position -> Denies the set_webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-set-webview-position" + ] + }, + { + "description": "webview:deny-set-webview-size -> Denies the set_webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-set-webview-size" + ] + }, + { + "description": "webview:deny-webview-close -> Denies the webview_close command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-webview-close" + ] + }, + { + "description": "webview:deny-webview-position -> Denies the webview_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-webview-position" + ] + }, + { + "description": "webview:deny-webview-size -> Denies the webview_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "webview:deny-webview-size" + ] + }, + { + "description": "window:default -> Default permissions for the plugin.", + "type": "string", + "enum": [ + "window:default" + ] + }, + { + "description": "window:allow-available-monitors -> Enables the available_monitors command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-available-monitors" + ] + }, + { + "description": "window:allow-center -> Enables the center command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-center" + ] + }, + { + "description": "window:allow-close -> Enables the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-close" + ] + }, + { + "description": "window:allow-create -> Enables the create command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-create" + ] + }, + { + "description": "window:allow-current-monitor -> Enables the current_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-current-monitor" + ] + }, + { + "description": "window:allow-destroy -> Enables the destroy command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-destroy" + ] + }, + { + "description": "window:allow-hide -> Enables the hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-hide" + ] + }, + { + "description": "window:allow-inner-position -> Enables the inner_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-inner-position" + ] + }, + { + "description": "window:allow-inner-size -> Enables the inner_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-inner-size" + ] + }, + { + "description": "window:allow-internal-toggle-maximize -> Enables the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-internal-toggle-maximize" + ] + }, + { + "description": "window:allow-is-closable -> Enables the is_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-closable" + ] + }, + { + "description": "window:allow-is-decorated -> Enables the is_decorated command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-decorated" + ] + }, + { + "description": "window:allow-is-focused -> Enables the is_focused command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-focused" + ] + }, + { + "description": "window:allow-is-fullscreen -> Enables the is_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-fullscreen" + ] + }, + { + "description": "window:allow-is-maximizable -> Enables the is_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-maximizable" + ] + }, + { + "description": "window:allow-is-maximized -> Enables the is_maximized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-maximized" + ] + }, + { + "description": "window:allow-is-minimizable -> Enables the is_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-minimizable" + ] + }, + { + "description": "window:allow-is-minimized -> Enables the is_minimized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-minimized" + ] + }, + { + "description": "window:allow-is-resizable -> Enables the is_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-resizable" + ] + }, + { + "description": "window:allow-is-visible -> Enables the is_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-is-visible" + ] + }, + { + "description": "window:allow-maximize -> Enables the maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-maximize" + ] + }, + { + "description": "window:allow-minimize -> Enables the minimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-minimize" + ] + }, + { + "description": "window:allow-outer-position -> Enables the outer_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-outer-position" + ] + }, + { + "description": "window:allow-outer-size -> Enables the outer_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-outer-size" + ] + }, + { + "description": "window:allow-primary-monitor -> Enables the primary_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-primary-monitor" + ] + }, + { + "description": "window:allow-request-user-attention -> Enables the request_user_attention command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-request-user-attention" + ] + }, + { + "description": "window:allow-scale-factor -> Enables the scale_factor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-scale-factor" + ] + }, + { + "description": "window:allow-set-always-on-bottom -> Enables the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-always-on-bottom" + ] + }, + { + "description": "window:allow-set-always-on-top -> Enables the set_always_on_top command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-always-on-top" + ] + }, + { + "description": "window:allow-set-closable -> Enables the set_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-closable" + ] + }, + { + "description": "window:allow-set-content-protected -> Enables the set_content_protected command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-content-protected" + ] + }, + { + "description": "window:allow-set-cursor-grab -> Enables the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-grab" + ] + }, + { + "description": "window:allow-set-cursor-icon -> Enables the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-icon" + ] + }, + { + "description": "window:allow-set-cursor-position -> Enables the set_cursor_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-position" + ] + }, + { + "description": "window:allow-set-cursor-visible -> Enables the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-cursor-visible" + ] + }, + { + "description": "window:allow-set-decorations -> Enables the set_decorations command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-decorations" + ] + }, + { + "description": "window:allow-set-effects -> Enables the set_effects command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-effects" + ] + }, + { + "description": "window:allow-set-focus -> Enables the set_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-focus" + ] + }, + { + "description": "window:allow-set-fullscreen -> Enables the set_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-fullscreen" + ] + }, + { + "description": "window:allow-set-icon -> Enables the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-icon" + ] + }, + { + "description": "window:allow-set-ignore-cursor-events -> Enables the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-ignore-cursor-events" + ] + }, + { + "description": "window:allow-set-max-size -> Enables the set_max_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-max-size" + ] + }, + { + "description": "window:allow-set-maximizable -> Enables the set_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-maximizable" + ] + }, + { + "description": "window:allow-set-min-size -> Enables the set_min_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-min-size" + ] + }, + { + "description": "window:allow-set-minimizable -> Enables the set_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-minimizable" + ] + }, + { + "description": "window:allow-set-position -> Enables the set_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-position" + ] + }, + { + "description": "window:allow-set-progress-bar -> Enables the set_progress_bar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-progress-bar" + ] + }, + { + "description": "window:allow-set-resizable -> Enables the set_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-resizable" + ] + }, + { + "description": "window:allow-set-shadow -> Enables the set_shadow command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-shadow" + ] + }, + { + "description": "window:allow-set-size -> Enables the set_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-size" + ] + }, + { + "description": "window:allow-set-skip-taskbar -> Enables the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-skip-taskbar" + ] + }, + { + "description": "window:allow-set-title -> Enables the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-title" + ] + }, + { + "description": "window:allow-set-visible-on-all-workspaces -> Enables the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-set-visible-on-all-workspaces" + ] + }, + { + "description": "window:allow-show -> Enables the show command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-show" + ] + }, + { + "description": "window:allow-start-dragging -> Enables the start_dragging command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-start-dragging" + ] + }, + { + "description": "window:allow-theme -> Enables the theme command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-theme" + ] + }, + { + "description": "window:allow-title -> Enables the title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-title" + ] + }, + { + "description": "window:allow-toggle-maximize -> Enables the toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-toggle-maximize" + ] + }, + { + "description": "window:allow-unmaximize -> Enables the unmaximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-unmaximize" + ] + }, + { + "description": "window:allow-unminimize -> Enables the unminimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:allow-unminimize" + ] + }, + { + "description": "window:deny-available-monitors -> Denies the available_monitors command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-available-monitors" + ] + }, + { + "description": "window:deny-center -> Denies the center command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-center" + ] + }, + { + "description": "window:deny-close -> Denies the close command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-close" + ] + }, + { + "description": "window:deny-create -> Denies the create command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-create" + ] + }, + { + "description": "window:deny-current-monitor -> Denies the current_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-current-monitor" + ] + }, + { + "description": "window:deny-destroy -> Denies the destroy command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-destroy" + ] + }, + { + "description": "window:deny-hide -> Denies the hide command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-hide" + ] + }, + { + "description": "window:deny-inner-position -> Denies the inner_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-inner-position" + ] + }, + { + "description": "window:deny-inner-size -> Denies the inner_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-inner-size" + ] + }, + { + "description": "window:deny-internal-toggle-maximize -> Denies the internal_toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-internal-toggle-maximize" + ] + }, + { + "description": "window:deny-is-closable -> Denies the is_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-closable" + ] + }, + { + "description": "window:deny-is-decorated -> Denies the is_decorated command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-decorated" + ] + }, + { + "description": "window:deny-is-focused -> Denies the is_focused command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-focused" + ] + }, + { + "description": "window:deny-is-fullscreen -> Denies the is_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-fullscreen" + ] + }, + { + "description": "window:deny-is-maximizable -> Denies the is_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-maximizable" + ] + }, + { + "description": "window:deny-is-maximized -> Denies the is_maximized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-maximized" + ] + }, + { + "description": "window:deny-is-minimizable -> Denies the is_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-minimizable" + ] + }, + { + "description": "window:deny-is-minimized -> Denies the is_minimized command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-minimized" + ] + }, + { + "description": "window:deny-is-resizable -> Denies the is_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-resizable" + ] + }, + { + "description": "window:deny-is-visible -> Denies the is_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-is-visible" + ] + }, + { + "description": "window:deny-maximize -> Denies the maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-maximize" + ] + }, + { + "description": "window:deny-minimize -> Denies the minimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-minimize" + ] + }, + { + "description": "window:deny-outer-position -> Denies the outer_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-outer-position" + ] + }, + { + "description": "window:deny-outer-size -> Denies the outer_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-outer-size" + ] + }, + { + "description": "window:deny-primary-monitor -> Denies the primary_monitor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-primary-monitor" + ] + }, + { + "description": "window:deny-request-user-attention -> Denies the request_user_attention command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-request-user-attention" + ] + }, + { + "description": "window:deny-scale-factor -> Denies the scale_factor command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-scale-factor" + ] + }, + { + "description": "window:deny-set-always-on-bottom -> Denies the set_always_on_bottom command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-always-on-bottom" + ] + }, + { + "description": "window:deny-set-always-on-top -> Denies the set_always_on_top command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-always-on-top" + ] + }, + { + "description": "window:deny-set-closable -> Denies the set_closable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-closable" + ] + }, + { + "description": "window:deny-set-content-protected -> Denies the set_content_protected command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-content-protected" + ] + }, + { + "description": "window:deny-set-cursor-grab -> Denies the set_cursor_grab command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-grab" + ] + }, + { + "description": "window:deny-set-cursor-icon -> Denies the set_cursor_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-icon" + ] + }, + { + "description": "window:deny-set-cursor-position -> Denies the set_cursor_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-position" + ] + }, + { + "description": "window:deny-set-cursor-visible -> Denies the set_cursor_visible command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-cursor-visible" + ] + }, + { + "description": "window:deny-set-decorations -> Denies the set_decorations command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-decorations" + ] + }, + { + "description": "window:deny-set-effects -> Denies the set_effects command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-effects" + ] + }, + { + "description": "window:deny-set-focus -> Denies the set_focus command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-focus" + ] + }, + { + "description": "window:deny-set-fullscreen -> Denies the set_fullscreen command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-fullscreen" + ] + }, + { + "description": "window:deny-set-icon -> Denies the set_icon command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-icon" + ] + }, + { + "description": "window:deny-set-ignore-cursor-events -> Denies the set_ignore_cursor_events command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-ignore-cursor-events" + ] + }, + { + "description": "window:deny-set-max-size -> Denies the set_max_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-max-size" + ] + }, + { + "description": "window:deny-set-maximizable -> Denies the set_maximizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-maximizable" + ] + }, + { + "description": "window:deny-set-min-size -> Denies the set_min_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-min-size" + ] + }, + { + "description": "window:deny-set-minimizable -> Denies the set_minimizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-minimizable" + ] + }, + { + "description": "window:deny-set-position -> Denies the set_position command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-position" + ] + }, + { + "description": "window:deny-set-progress-bar -> Denies the set_progress_bar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-progress-bar" + ] + }, + { + "description": "window:deny-set-resizable -> Denies the set_resizable command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-resizable" + ] + }, + { + "description": "window:deny-set-shadow -> Denies the set_shadow command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-shadow" + ] + }, + { + "description": "window:deny-set-size -> Denies the set_size command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-size" + ] + }, + { + "description": "window:deny-set-skip-taskbar -> Denies the set_skip_taskbar command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-skip-taskbar" + ] + }, + { + "description": "window:deny-set-title -> Denies the set_title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-title" + ] + }, + { + "description": "window:deny-set-visible-on-all-workspaces -> Denies the set_visible_on_all_workspaces command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-set-visible-on-all-workspaces" + ] + }, + { + "description": "window:deny-show -> Denies the show command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-show" + ] + }, + { + "description": "window:deny-start-dragging -> Denies the start_dragging command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-start-dragging" + ] + }, + { + "description": "window:deny-theme -> Denies the theme command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-theme" + ] + }, + { + "description": "window:deny-title -> Denies the title command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-title" + ] + }, + { + "description": "window:deny-toggle-maximize -> Denies the toggle_maximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-toggle-maximize" + ] + }, + { + "description": "window:deny-unmaximize -> Denies the unmaximize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-unmaximize" + ] + }, + { + "description": "window:deny-unminimize -> Denies the unminimize command without any pre-configured scope.", + "type": "string", + "enum": [ + "window:deny-unminimize" + ] + }, + { + "description": "window-state:allow-restore-window-state -> Enables the restore_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:allow-restore-window-state" + ] + }, + { + "description": "window-state:allow-save-window-state -> Enables the save_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:allow-save-window-state" + ] + }, + { + "description": "window-state:deny-restore-window-state -> Denies the restore_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:deny-restore-window-state" + ] + }, + { + "description": "window-state:deny-save-window-state -> Denies the save_window_state command without any pre-configured scope.", + "type": "string", + "enum": [ + "window-state:deny-save-window-state" + ] + } + ] + }, + "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" + ] + } + ] + }, + "ShellAllowedArg": { + "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": { + "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\n[regex]: https://docs.rs/regex/latest/regex/#syntax", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "ShellAllowedArgs": { + "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 [`ShellAllowedArg`] 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 [`ShellAllowedArg`] that are valid to call for the command configuration.", + "type": "array", + "items": { + "$ref": "#/definitions/ShellAllowedArg" + } + } + ] + } + } +} \ No newline at end of file diff --git a/src-tauri/gen/schemas/plugin-manifests.json b/src-tauri/gen/schemas/plugin-manifests.json new file mode 100644 index 000000000..a11d07d69 --- /dev/null +++ b/src-tauri/gen/schemas/plugin-manifests.json @@ -0,0 +1 @@ +{"app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version"]},"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-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"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-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"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},"config":{"default_permission":null,"permissions":{"allow-get-config":{"identifier":"allow-get-config","description":"Enables the get_config command without any pre-configured scope.","commands":{"allow":["get_config"],"deny":[]}},"allow-set-config":{"identifier":"allow-set-config","description":"Enables the set_config command without any pre-configured scope.","commands":{"allow":["set_config"],"deny":[]}},"deny-get-config":{"identifier":"deny-get-config","description":"Denies the get_config command without any pre-configured scope.","commands":{"allow":[],"deny":["get_config"]}},"deny-set-config":{"identifier":"deny-set-config","description":"Denies the set_config command without any pre-configured scope.","commands":{"allow":[],"deny":["set_config"]}}},"permission_sets":{},"global_scope_schema":null},"database":{"default_permission":null,"permissions":{"allow-get-all-playlists":{"identifier":"allow-get-all-playlists","description":"Enables the get_all_playlists command without any pre-configured scope.","commands":{"allow":["get_all_playlists"],"deny":[]}},"allow-get-all-tracks":{"identifier":"allow-get-all-tracks","description":"Enables the get_all_tracks command without any pre-configured scope.","commands":{"allow":["get_all_tracks"],"deny":[]}},"allow-get-cover-as-base64":{"identifier":"allow-get-cover-as-base64","description":"Enables the get_cover_as_base64 command without any pre-configured scope.","commands":{"allow":["get_cover_as_base64"],"deny":[]}},"allow-import-tracks-to-library":{"identifier":"allow-import-tracks-to-library","description":"Enables the import_tracks_to_library command without any pre-configured scope.","commands":{"allow":["import_tracks_to_library"],"deny":[]}},"deny-get-all-playlists":{"identifier":"deny-get-all-playlists","description":"Denies the get_all_playlists command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_playlists"]}},"deny-get-all-tracks":{"identifier":"deny-get-all-tracks","description":"Denies the get_all_tracks command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_tracks"]}},"deny-get-cover-as-base64":{"identifier":"deny-get-cover-as-base64","description":"Denies the get_cover_as_base64 command without any pre-configured scope.","commands":{"allow":[],"deny":["get_cover_as_base64"]}},"deny-import-tracks-to-library":{"identifier":"deny-import-tracks-to-library","description":"Denies the import_tracks_to_library command without any pre-configured scope.","commands":{"allow":[],"deny":["import_tracks_to_library"]}}},"permission_sets":{},"global_scope_schema":null},"default-view":{"default_permission":null,"permissions":{"allow-set":{"identifier":"allow-set","description":"Enables the set command without any pre-configured scope.","commands":{"allow":["set"],"deny":[]}},"deny-set":{"identifier":"deny-set","description":"Denies the set command without any pre-configured scope.","commands":{"allow":[],"deny":["set"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":null,"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},"event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","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},"log":{"default_permission":{"identifier":"default","description":"Allows the log command","permissions":["allow-log"]},"permissions":{"allow-log":{"identifier":"allow-log","description":"Enables the log command without any pre-configured scope.","commands":{"allow":["log"],"deny":[]}},"deny-log":{"identifier":"deny-log","description":"Denies the log command without any pre-configured scope.","commands":{"allow":[],"deny":["log"]}}},"permission_sets":{},"global_scope_schema":null},"menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":[]},"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},"os":{"default_permission":null,"permissions":{"allow-arch":{"identifier":"allow-arch","description":"Enables the arch command without any pre-configured scope.","commands":{"allow":["arch"],"deny":[]}},"allow-exe-extension":{"identifier":"allow-exe-extension","description":"Enables the exe_extension command without any pre-configured scope.","commands":{"allow":["exe_extension"],"deny":[]}},"allow-family":{"identifier":"allow-family","description":"Enables the family command without any pre-configured scope.","commands":{"allow":["family"],"deny":[]}},"allow-hostname":{"identifier":"allow-hostname","description":"Enables the hostname command without any pre-configured scope.","commands":{"allow":["hostname"],"deny":[]}},"allow-locale":{"identifier":"allow-locale","description":"Enables the locale command without any pre-configured scope.","commands":{"allow":["locale"],"deny":[]}},"allow-os-type":{"identifier":"allow-os-type","description":"Enables the os_type command without any pre-configured scope.","commands":{"allow":["os_type"],"deny":[]}},"allow-platform":{"identifier":"allow-platform","description":"Enables the platform command without any pre-configured scope.","commands":{"allow":["platform"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-arch":{"identifier":"deny-arch","description":"Denies the arch command without any pre-configured scope.","commands":{"allow":[],"deny":["arch"]}},"deny-exe-extension":{"identifier":"deny-exe-extension","description":"Denies the exe_extension command without any pre-configured scope.","commands":{"allow":[],"deny":["exe_extension"]}},"deny-family":{"identifier":"deny-family","description":"Denies the family command without any pre-configured scope.","commands":{"allow":[],"deny":["family"]}},"deny-hostname":{"identifier":"deny-hostname","description":"Denies the hostname command without any pre-configured scope.","commands":{"allow":[],"deny":["hostname"]}},"deny-locale":{"identifier":"deny-locale","description":"Denies the locale command without any pre-configured scope.","commands":{"allow":[],"deny":["locale"]}},"deny-os-type":{"identifier":"deny-os-type","description":"Denies the os_type command without any pre-configured scope.","commands":{"allow":[],"deny":["os_type"]}},"deny-platform":{"identifier":"deny-platform","description":"Denies the platform command without any pre-configured scope.","commands":{"allow":[],"deny":["platform"]}},"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},"path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","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},"resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","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},"shell":{"default_permission":null,"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-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-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#","definitions":{"ShellAllowedArg":{"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":{"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\n[regex]: https://docs.rs/regex/latest/regex/#syntax","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellAllowedArg"},"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 [`ShellAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"A command allowed to be executed by the webview API.","properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellAllowedArgs"}],"description":"The allowed arguments for the command execution."},"command":{"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`, `$APP`, `$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"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["args","command","name","sidecar"],"title":"Entry","type":"object"}},"sleepblocker":{"default_permission":null,"permissions":{"allow-disable":{"identifier":"allow-disable","description":"Enables the disable command without any pre-configured scope.","commands":{"allow":["disable"],"deny":[]}},"allow-enable":{"identifier":"allow-enable","description":"Enables the enable command without any pre-configured scope.","commands":{"allow":["enable"],"deny":[]}},"deny-disable":{"identifier":"deny-disable","description":"Denies the disable command without any pre-configured scope.","commands":{"allow":[],"deny":["disable"]}},"deny-enable":{"identifier":"deny-enable","description":"Denies the enable command without any pre-configured scope.","commands":{"allow":[],"deny":["enable"]}}},"permission_sets":{},"global_scope_schema":null},"tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":[]},"permissions":{"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"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-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"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},"webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"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-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-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-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"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-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"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-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-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-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"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-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},"window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["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-title","allow-current-monitor","allow-primary-monitor","allow-available-monitors","allow-theme","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-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"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-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-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-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-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-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"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-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-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"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-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"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-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-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"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-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-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-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-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-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"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-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-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"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-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"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-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},"window-state":{"default_permission":null,"permissions":{"allow-restore-window-state":{"identifier":"allow-restore-window-state","description":"Enables the restore_window_state command without any pre-configured scope.","commands":{"allow":["restore_window_state"],"deny":[]}},"allow-save-window-state":{"identifier":"allow-save-window-state","description":"Enables the save_window_state command without any pre-configured scope.","commands":{"allow":["save_window_state"],"deny":[]}},"deny-restore-window-state":{"identifier":"deny-restore-window-state","description":"Denies the restore_window_state command without any pre-configured scope.","commands":{"allow":[],"deny":["restore_window_state"]}},"deny-save-window-state":{"identifier":"deny-save-window-state","description":"Denies the save_window_state command without any pre-configured scope.","commands":{"allow":[],"deny":["save_window_state"]}}},"permission_sets":{},"global_scope_schema":null}} \ 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 000000000..f5b259b08 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 000000000..ace5d6284 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 000000000..0271dc331 Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 000000000..1207cae2d 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 000000000..bf6a33647 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 000000000..b0d136bf4 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 000000000..6e9b0e073 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 000000000..5e96f04f6 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 000000000..6cb0a5a9f 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 000000000..ee795844a 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 000000000..a18f49170 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 000000000..73a81d5c8 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 000000000..d3b10dff9 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/app-icon.bak.png b/src-tauri/icons/app-icon.bak.png new file mode 100644 index 000000000..337eb7607 Binary files /dev/null and b/src-tauri/icons/app-icon.bak.png differ diff --git a/src-tauri/icons/app-icon.png b/src-tauri/icons/app-icon.png new file mode 100644 index 000000000..69d52210d Binary files /dev/null and b/src-tauri/icons/app-icon.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 000000000..24fdd88fc 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 000000000..3cad002b9 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 000000000..a68f1d50e Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/constants.rs b/src-tauri/src/constants.rs new file mode 100644 index 000000000..2c64803ee --- /dev/null +++ b/src-tauri/src/constants.rs @@ -0,0 +1,8 @@ +pub const SUPPORTED_TRACKS_EXTENSIONS: [&str; 12] = [ + "mp3", "mp4", "aac", "m4a", "3gp", "wav", /* mp3 / mp4 */ + "ogg", "ogv", "ogm", "opus", /* Opus */ + "flac", /* Flac */ + "webm", /* Web media */ +]; + +// pub const SUPPORTED_PLAYLISTS_EXTENSIONS: [&str; 1] = [".m3u"]; diff --git a/src-tauri/src/libs/error.rs b/src-tauri/src/libs/error.rs new file mode 100644 index 000000000..49fbf29d5 --- /dev/null +++ b/src-tauri/src/libs/error.rs @@ -0,0 +1,39 @@ +use anyhow::Result; +use serde::{ser::Serializer, Serialize}; +use thiserror::Error; + +/** + * Create the error type that represents all errors possible in our program + * Stolen from https://github.com/tauri-apps/tauri/discussions/3913 + */ +#[derive(Debug, Error)] +pub enum MuseeksError { + #[error(transparent)] + Lofty(#[from] lofty::LoftyError), + + #[error(transparent)] + Database(#[from] bonsaidb::core::Error), + + #[error(transparent)] + LocalDatabase(#[from] bonsaidb::local::Error), + + #[error(transparent)] + NoSleep(#[from] nosleep::Error), + + #[error(transparent)] + Command(#[from] anyhow::Error), +} + +/** + * Let's make anyhow's errors Tauri friendly, so they can be used for commands + */ +impl Serialize for MuseeksError { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(self.to_string().as_ref()) + } +} + +pub type AnyResult = Result; diff --git a/src-tauri/src/libs/mod.rs b/src-tauri/src/libs/mod.rs new file mode 100644 index 000000000..caf91b357 --- /dev/null +++ b/src-tauri/src/libs/mod.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod utils; diff --git a/src-tauri/src/libs/utils.rs b/src-tauri/src/libs/utils.rs new file mode 100644 index 000000000..bf4ead74e --- /dev/null +++ b/src-tauri/src/libs/utils.rs @@ -0,0 +1,104 @@ +/** + * Small utility to display time metrics with a log message + */ +use log::info; +use std::{ffi::OsStr, path::PathBuf, time::Instant}; +use walkdir::WalkDir; + +/** + * Small helper to compute the execution time of some code + */ +pub struct TimeLogger { + start_time: Instant, + message: String, +} + +impl TimeLogger { + pub fn new(message: String) -> Self { + TimeLogger { + start_time: Instant::now(), + message, + } + } + + pub fn complete(&self) { + let duration = self.start_time.elapsed(); + info!("{} in {:.2?}", self.message, duration); + } +} + +/** + * Get the app configuration/storage directory + */ +pub fn get_app_storage_dir() -> PathBuf { + let path = dirs::home_dir().expect("Get home dir"); + path.join(".museeks") +} + +/** + * Check if a directory or a file is visible or not + */ +fn is_dir_visible(entry: &walkdir::DirEntry) -> bool { + entry + .file_name() + .to_str() + .map(|s| !s.starts_with(".")) + .unwrap_or(false) +} + +/** + * Take an entry and filter out: + * - directories + * - non-allowed extensions + */ +fn is_entry_valid( + result: std::result::Result, + allowed_extensions: &[&str], +) -> Option { + // If the user does not have access to the file + if result.is_err() { + return None; + } + + let entry = result.unwrap(); + let file_type = entry.file_type(); + + let extension = entry + .path() + .extension() + .and_then(OsStr::to_str) + .unwrap_or(""); + + let is_file = file_type.is_file(); + let has_valid_extension = allowed_extensions.contains(&extension); + + if is_file && has_valid_extension { + // Only return the file path, that's what we're interested in + return entry.path().to_str().map(str::to_string); + } + + return None; +} + +/** + * Scan multiple directories and filter files by extension + */ +pub fn scan_dirs(paths: &Vec, allowed_extensions: &[&str]) -> Vec { + paths + .iter() + .map(|path| scan_dir(path, allowed_extensions)) + .flatten() + .collect() +} + +/** + * Scan directory and filter files by extension + */ +pub fn scan_dir(path: &String, allowed_extensions: &[&str]) -> Vec { + WalkDir::new(path) + .follow_links(true) + .into_iter() + .filter_entry(|entry| is_dir_visible(entry)) + .filter_map(|entry| is_entry_valid(entry, allowed_extensions)) + .collect() +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 000000000..8b314c927 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,48 @@ +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +mod constants; +mod libs; +mod plugins; + +use log::LevelFilter; +use tauri_plugin_log::fern::colors::ColoredLevelConfig; +use tauri_plugin_log::{Target, TargetKind}; +use tauri_plugin_shell; + +/** + * The beast + */ +#[tokio::main] +async fn main() { + // Is there any way to instantiate that in the plugin directly? + let db = plugins::database::setup().await.ok().unwrap(); + + tauri::Builder::default() + // Custom integrations + .plugin(plugins::app_menu::init()) + .plugin(plugins::config::init()) + .plugin(plugins::database::init(db)) + .plugin(plugins::debug::init()) + .plugin(plugins::default_view::init()) + .plugin(plugins::sleepblocker::init()) + // Tauri integrations with the Operating System + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_os::init()) + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_window_state::Builder::default().build()) + .plugin( + tauri_plugin_log::Builder::default() + .targets([ + Target::new(TargetKind::Stdout), + Target::new(TargetKind::Webview), + ]) + .level(LevelFilter::Info) + .with_colors(ColoredLevelConfig::default()) + .build(), + ) + // TODO: single instance + // TODO: tauri-plugin-theme + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/plugins/app_menu.rs b/src-tauri/src/plugins/app_menu.rs new file mode 100644 index 000000000..130f09224 --- /dev/null +++ b/src-tauri/src/plugins/app_menu.rs @@ -0,0 +1,197 @@ +// use tauri::menu::{AboutMetadataBuilder, MenuBuilder, SubmenuBuilder}; +// use tauri::plugin::{Builder, TauriPlugin}; +// use tauri::Runtime; + +// const APP_NAME: &str = "Museeks"; // TODO use app.package instead + +// fn get_initial_menu() -> MenuBuilder { +// let menu = Menu::with_items([ +// Submenu::new("USELESS", Menu::new()).into(), +// Submenu::new( +// APP_NAME, +// Menu::new() +// .add_native_item(MenuItem::About(String::from(APP_NAME), about_metadata)) +// .add_native_item(MenuItem::Separator) +// .add_native_item(MenuItem::Services) +// .add_native_item(MenuItem::Separator) +// .add_native_item(MenuItem::Hide) +// .add_native_item(MenuItem::HideOthers) +// .add_native_item(MenuItem::ShowAll) +// .add_native_item(MenuItem::Separator) +// .add_native_item(MenuItem::Quit), +// ) +// .into(), +// Submenu::new("File", Menu::new().add_native_item(MenuItem::CloseWindow)).into(), +// Submenu::new( +// "Edit", +// Menu::new() +// .add_native_item(MenuItem::Undo) +// .add_native_item(MenuItem::Redo) +// .add_native_item(MenuItem::Separator) +// .add_native_item(MenuItem::Cut) +// .add_native_item(MenuItem::Copy) +// .add_native_item(MenuItem::Paste) +// .add_native_item(MenuItem::SelectAll), +// ) +// .into(), +// Submenu::new( +// "View", +// Menu::new() +// // TODO: +// // - jump to track +// // - separator +// // - go to library +// // - go to playlists +// // - separator +// // - reload +// // - force reload +// // - toggle developer tools +// // - separator +// // - actual size +// // - zoom in +// // - zoom out +// // - actual +// .add_native_item(MenuItem::EnterFullScreen), +// ) +// .into(), +// Submenu::new( +// "Window", +// Menu::new() +// .add_native_item(MenuItem::Minimize) +// .add_native_item(MenuItem::Zoom), +// ) +// .into(), +// Submenu::new("Help", Menu::new()).into(), +// ]); + +// return menu; +// } + +/** + * The actual plugin + */ +// pub fn init() -> TauriPlugin { +// Builder::::new("global_menu") +// .on_window_ready(|_window| { +// // TODO: not working +// // let handle = window.app_handle(); + +// // let about_metadata = AboutMetadataBuilder::new() +// // .version("0.20.0".into()) // TODO: Automate all that? +// // .authors(Some(vec!["Pierre de la Martinière".to_owned()])) +// // .license("MIT".into()) +// // .website("https://museeks.io".into()) +// // .website_label("website".into()) +// // .build(); + +// // let app_menu = SubmenuBuilder::new(handle, "Test") +// // .about(Some(about_metadata)) +// // .build() +// // .unwrap(); + +// // let menu = MenuBuilder::new(handle).item(&app_menu).build().unwrap(); + +// // window.set_menu(menu).unwrap(); +// }) +// .build() +// } +use tauri::{ + menu::{AboutMetadataBuilder, MenuBuilder, MenuItemBuilder, SubmenuBuilder}, + plugin::{Builder, TauriPlugin}, + Manager, Runtime, +}; + +// #[cfg(not(target_os = "macos"))] +// #[command] +// pub fn toggle(window: tauri::Window) { +// if window.is_menu_visible().unwrap_or_default() { +// let _ = window.hide_menu(); +// } else { +// let _ = window.show_menu(); +// } +// } + +#[cfg(target_os = "macos")] +pub struct AppMenu(pub std::sync::Mutex>>); + +// pub struct PopupMenu(tauri::menu::Menu); + +// #[cfg(target_os = "macos")] +// #[command] +// pub fn toggle( +// app: tauri::AppHandle, +// app_menu: tauri::State<'_, crate::AppMenu>, +// ) { +// if let Some(menu) = app.remove_menu().unwrap() { +// app_menu.0.lock().unwrap().replace(menu); +// } else { +// app.set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu")) +// .unwrap(); +// } +// } + +// #[command] +// pub fn popup( +// window: tauri::Window, +// popup_menu: tauri::State<'_, crate::PopupMenu>, +// ) { +// window.popup_menu(&popup_menu.0).unwrap(); +// } + +pub fn init() -> TauriPlugin { + Builder::new("app-menu") + .invoke_handler(tauri::generate_handler![/*popup, toggle*/]) + .setup(|app_handle, _api| { + let about_metadata = AboutMetadataBuilder::new() + .version("0.20.0".into()) // TODO: Automate all that? + .authors(Some(vec!["Pierre de la Martinière".to_owned()])) + .license("MIT".into()) + .website("https://museeks.io".into()) + .website_label("museeks.io".into()) + .build(); + + let about_metadata_2 = AboutMetadataBuilder::new() + .version("0.20.1".into()) // TODO: Automate all that? + .authors(Some(vec!["Pierre de la Martinière".to_owned()])) + .license("MIT".into()) + .website("https://museeks.io".into()) + .website_label("museeks.io".into()) + .build(); + + let app_menu = SubmenuBuilder::new(app_handle, "Museeks") + .about(Some(about_metadata)) + .item(&MenuItemBuilder::new("lala").build(app_handle).unwrap()) + .build() + .unwrap(); + + let app_menu_2 = SubmenuBuilder::new(app_handle, "Test2") + .about(Some(about_metadata_2)) + .build() + .unwrap(); + + let menu = MenuBuilder::new(app_handle) + .item(&app_menu) + .item(&app_menu_2) + .build() + .unwrap(); + + app_handle.set_menu(menu).unwrap(); + + // let webview = app_handle.get_ + // tauri::menu::Menu::default(app.handle())? + + // #[cfg(target_os = "macos")] + // app_handle.manage(AppMenu::(Default::default())); + + // app_handle.manage(PopupMenu( + // tauri::menu::MenuBuilder::new(app) + // .check("check", "Tauri is awesome!") + // .text("text", "Do something") + // .copy() + // .build()?, + // )); + + Ok(()) + }) + .build() +} diff --git a/src-tauri/src/plugins/config.rs b/src-tauri/src/plugins/config.rs new file mode 100644 index 000000000..4fa24e8fe --- /dev/null +++ b/src-tauri/src/plugins/config.rs @@ -0,0 +1,172 @@ +/** + * Module in charge of persisting and returning the config to/from the filesystem + */ +use home_config::HomeConfig; +use log::info; +use serde::{Deserialize, Serialize}; +use std::{path::PathBuf, sync::RwLock}; +use tauri::plugin::{Builder, TauriPlugin}; +use tauri::{Manager, Runtime, State}; +use ts_rs::TS; + +use crate::libs::utils::get_app_storage_dir; + +#[derive(Serialize, Deserialize, Debug, Clone, TS)] +#[ts(export, export_to = "../src/generated/typings/Repeat.ts")] +pub enum Repeat { + All, + One, + None, +} + +#[derive(Serialize, Deserialize, Debug, Clone, TS)] +#[ts(export, export_to = "../src/generated/typings/SortBy.ts")] +pub enum SortBy { + Artist, + Album, + Title, + Duration, + Genre, +} + +#[derive(Serialize, Deserialize, Debug, Clone, TS)] +#[ts(export, export_to = "../src/generated/typings/SortOrder.ts")] +pub enum SortOrder { + Asc, + Dsc, +} + +#[derive(Serialize, Deserialize, Debug, Clone, TS)] +#[ts(export, export_to = "../src/generated/typings/DefaultView.ts")] +pub enum DefaultView { + Library, + Playlists, +} + +#[derive(Serialize, Deserialize, Debug, Clone, TS)] +#[ts(export, export_to = "../src/generated/typings/Config.ts")] +pub struct Config { + pub theme: String, + pub audio_volume: f32, + pub audio_playback_rate: f32, + pub audio_output_device: String, + pub audio_muted: bool, + pub audio_shuffle: bool, + pub audio_repeat: Repeat, + pub default_view: DefaultView, + pub library_sort_by: SortBy, + pub library_sort_order: SortOrder, + pub library_folders: Vec, // Not used yet + pub sleepblocker: bool, + pub auto_update_checker: bool, + pub minimize_to_tray: bool, + pub notifications: bool, + pub track_view_density: String, +} + +impl Config { + pub fn default() -> Self { + Config { + theme: "__system".to_owned(), + audio_volume: 1.0, + audio_playback_rate: 1.0, + audio_output_device: "default".to_owned(), + audio_muted: false, + audio_shuffle: false, + audio_repeat: Repeat::None, + default_view: DefaultView::Library, + library_sort_by: SortBy::Artist, + library_sort_order: SortOrder::Asc, + library_folders: vec![], + sleepblocker: false, + auto_update_checker: true, + minimize_to_tray: false, + notifications: false, + track_view_density: "normal".to_owned(), + } + } +} + +#[derive(Debug)] +pub struct ConfigManager { + manager: HomeConfig, + pub data: RwLock, +} + +impl ConfigManager { + pub fn get(&self) -> Config { + self.data.read().unwrap().clone() + } + + pub fn update(&self, config: Config) { + let mut writer = self.data.write().unwrap(); + *writer = config; + std::mem::drop(writer); + self.save(); + } + + pub fn set_sleepblocker(&self, sleepblocker: bool) { + let mut writer = self.data.write().unwrap(); + writer.sleepblocker = sleepblocker; + std::mem::drop(writer); + self.save(); + } + + pub fn set_default_view(&self, default_view: DefaultView) { + let mut writer = self.data.write().unwrap(); + writer.default_view = default_view; + std::mem::drop(writer); + self.save(); + } + + fn save(&self) { + let config = self.data.read().unwrap(); + self.manager.save_toml(config.clone()).unwrap(); + info!("Config updated"); + } +} + +#[tauri::command] +pub fn get_config(config_manager: State) -> Config { + config_manager.get() +} + +#[tauri::command] +pub fn set_config(config_manager: State, config: Config) { + config_manager.update(config); +} + +pub fn init() -> TauriPlugin { + Builder::::new("config") + .invoke_handler(tauri::generate_handler![get_config, set_config,]) + .setup(|app_handle, _api| { + let conf_path = get_app_storage_dir(); + let manager = HomeConfig::with_file(conf_path.join("config.toml")); + let existing_config = manager.toml::(); + + // TODO: add missing keys here + + let config = match existing_config { + Ok(config) => ConfigManager { + manager, + data: RwLock::new(config), + }, + Err(_) => { + // The config does not exist, so let's instantiate it with defaults + // Potential issue: if the config is extended, the defaults will be + // reloaded + let default_config = Config::default(); + manager.save_toml(&default_config).unwrap(); + + ConfigManager { + manager, + data: RwLock::new(default_config), + } + } + }; + + app_handle.manage(config); + Ok(()) + }) + .build() +} diff --git a/src-tauri/src/plugins/database.rs b/src-tauri/src/plugins/database.rs new file mode 100644 index 000000000..067469d5b --- /dev/null +++ b/src-tauri/src/plugins/database.rs @@ -0,0 +1,398 @@ +use base64::prelude::*; +use bonsaidb::core::connection::{AsyncConnection, AsyncStorageConnection}; +use bonsaidb::core::document::{BorrowedDocument, Emit}; +use bonsaidb::core::schema::{ + Collection, MapReduce, ReduceResult, SerializedCollection, View, ViewMapResult, + ViewMappedValue, ViewSchema, +}; +use bonsaidb::core::transaction::{Operation, Transaction}; +use bonsaidb::local::config::{Builder as BonsaiBuilder, StorageConfiguration}; +use bonsaidb::local::AsyncDatabase; +use bonsaidb::local::AsyncStorage; +use lofty::{Accessor, AudioFile, MimeType, TaggedFileExt}; +use log::{debug, info, warn}; +use rayon::prelude::*; +use serde::{Deserialize, Serialize}; +use tauri::plugin::{Builder, TauriPlugin}; +use tauri::{Manager, Runtime, State}; +use ts_rs::TS; + +use crate::constants; +use crate::libs::error::AnyResult; +use crate::libs::utils::{get_app_storage_dir, scan_dirs, TimeLogger}; + +const INSERTION_BATCH: usize = 200; + +/** ---------------------------------------------------------------------------- + * Databases + * exposes databases for tracks and playlists + * TODO: + * - Export all needed structs to a single file: ts-rs#59 + * -------------------------------------------------------------------------- */ +#[derive(Debug)] +pub struct DB { + pub tracks: AsyncDatabase, + pub playlists: AsyncDatabase, +} + +impl DB { + /** + * Get all the tracks (and their content) from the database + */ + pub async fn get_all_tracks(&self) -> AnyResult>> { + let timer = TimeLogger::new("Retrieved all tracks from DB".into()); + + let collection = self.tracks.collection::(); + let docs = collection.all().await?; + + timer.complete(); + + let decode_timer = TimeLogger::new("Decoded all tracks content".into()); + + let mut tracks = vec![]; + + for doc in docs { + let deserialized = Track::document_contents(&doc)?; + let parsed_document = Doc { + id: doc.header.id.to_string(), + doc: deserialized, + }; + + tracks.push(parsed_document); + } + + decode_timer.complete(); + + Ok(tracks) + } + + /** + * Insert a new track in the DB, will fail in case there is a duplicate unique + * key (like track.path) + * + * Doc: https://github.com/khonsulabs/bonsaidb/blob/main/examples/basic-local/examples/basic-local-multidb.rs + */ + pub async fn insert_track(&self, tracks: &Vec) -> AnyResult<()> { + // BonsaiDB does not work well (as of today) with a lot of very small + // insertions, so let's insert tracks by batch instead then + let batches: Vec> = tracks.chunks(INSERTION_BATCH).map(|x| x.to_vec()).collect(); + + info!("Splitting tracks in {} batche(s)", batches.len()); + + for batch in batches { + let mut tx = Transaction::new(); + + for track in batch { + tx.push(Operation::push_serialized::(&track)?); + } + + // Let's goooo + tx.apply_async(&self.tracks).await?; + } + + Ok(()) + } + + /** + * Get all the playlists (and their content) from the database + */ + pub async fn get_all_playlists(&self) -> AnyResult>> { + let timer = TimeLogger::new("Retrieved all playlists from DB".into()); + + let collection = self.playlists.collection::(); + let docs = collection.all().await?; + + timer.complete(); + + let decode_timer = TimeLogger::new("Decoded all platlists content".into()); + + let mut playlists = vec![]; + + for doc in docs { + let deserialized = Playlist::document_contents(&doc)?; + let parsed_document = Doc { + id: doc.header.id.to_string(), + doc: deserialized, + }; + + playlists.push(parsed_document); + } + + decode_timer.complete(); + + Ok(playlists) + } + + // TODO: find tracks by IDs + // TODO: find playlist by ID +} + +/** ---------------------------------------------------------------------------- + * Track + * represent a single track, id and path should be unique + * -------------------------------------------------------------------------- */ +#[derive(Debug, Clone, Serialize, Deserialize, Collection, TS)] +#[collection(name = "tracks", views = [TracksByPath])] +#[ts(export, export_to = "../src/generated/typings/Track.ts")] +pub struct Track { + pub title: String, + pub album: String, + pub artists: Vec, + pub genres: Vec, + pub year: Option, + pub duration: u32, + pub track: NumberOf, + pub disk: NumberOf, + pub path: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts(export, export_to = "../src/generated/typings/NumberOf.ts")] +pub struct NumberOf { + pub no: Option, + pub of: Option, +} + +// TODO: unique is not workinf +// TrackByPath is a DB view to quickly access a Track by its location on the +// filesystem. Mostly used to query some data DB. +#[derive(Debug, Clone, View, ViewSchema)] +#[view(collection = Track, key = String, value = usize, name = "by-path")] +pub struct TracksByPath; + +impl MapReduce for TracksByPath { + fn map<'doc>(&self, document: &'doc BorrowedDocument<'_>) -> ViewMapResult<'doc, Self> { + let track = Track::document_contents(document)?; + document.header.emit_key_and_value(track.path, 1) + } + + fn reduce( + &self, + mappings: &[ViewMappedValue], + _rereduce: bool, + ) -> ReduceResult { + Ok(mappings.iter().map(|mapping| mapping.value).sum()) + } +} + +/** ---------------------------------------------------------------------------- + * Playlist + * represent a playlist, that has a name and a list of tracks + * -------------------------------------------------------------------------- */ + +#[derive(Debug, Clone, Serialize, Deserialize, Collection, TS)] +#[collection(name = "playlists")] +#[ts(export, export_to = "../src/generated/typings/Playlist.ts")] +pub struct Playlist { + pub name: String, + pub tracks: Vec, // vector of IDs + pub import_path: String, +} + +/** ---------------------------------------------------------------------------- + * Struct Helpers + * -------------------------------------------------------------------------- */ + +// Struct helper useful to include the DB ID in addition to the actual fields +// of a document +#[derive(Debug, Serialize, Deserialize, TS)] +#[ts(export, export_to = "../src/generated/typings/Doc.ts")] +pub struct Doc { + pub id: String, + pub doc: T, +} + +/** ---------------------------------------------------------------------------- + * Commands + * -------------------------------------------------------------------------- */ + +/** + * Popup a directory picker dialog, scan the selected folders, extract all + * ID3 tags from it, and update the DB accordingly. + */ +#[tauri::command] +async fn import_tracks_to_library( + db: State<'_, DB>, + import_paths: Vec, +) -> AnyResult>> { + info!("Importing path {}", import_paths.join(", ")); + + let paths = scan_dirs(&import_paths, &constants::SUPPORTED_TRACKS_EXTENSIONS); + let task_count = paths.len(); + + // Let's get all tracks ID3 + info!("Importing ID3 tags from {} files", task_count); + let scan_logger = TimeLogger::new("Scanned all id3 tags".into()); + + let tracks = &paths + .par_iter() + .map(|path| -> Option { + let saved_path = path.to_string(); // Why do I need to copy this? + + // TODO: make sure we don't save tracks that are already in DB + // TODO: friendly log progress to console + set progres on UI + + let track = match lofty::read_from_path(&path) { + Ok(tagged_file) => { + let tag = tagged_file.primary_tag()?; + + let title = tag.title(); + let album = tag.album(); + let artist = tag.artist(); + let genre = tag.genre(); + + Some(Track { + title: title.as_deref().unwrap_or("Unknown").to_string(), + album: album.as_deref().unwrap_or("Unknown").to_string(), + // TODO: get multiple artists/genres: https://github.com/Serial-ATA/lofty-rs/issues/55 + artists: vec![artist.as_deref().unwrap_or("Unknown Artist")] + .iter() + .map(|&s| s.into()) + .collect(), + genres: vec![genre.as_deref().unwrap_or("")] + .iter() + .map(|&s| s.into()) + .collect(), + year: tag.year(), + duration: u32::try_from(tagged_file.properties().duration().as_secs()) + .unwrap_or(0), + track: NumberOf { + no: tag.track(), + of: tag.track_total(), + }, + disk: NumberOf { + no: tag.disk(), + of: tag.disk_total(), + }, + path: path.to_string(), + }) + } + Err(err) => { + warn!("Failed to get ID3 tags: \"{}\". File {}", err, saved_path); + None + } + }; + + return track; + }) + .flatten() + .collect::>(); + + info!("{} tracks successfully scanned", tracks.len()); + info!("{} tracks failed to be scanned", paths.len() - tracks.len()); + scan_logger.complete(); + + let db_insert_logger = TimeLogger::new("Inserted tracks".into()); + + // Insert all tracks in the DB + let result = db.insert_track(tracks).await; + + if result.is_err() { + warn!("Something went wrong when inserting tracks"); + } + + db_insert_logger.complete(); + + let tracks = db.get_all_tracks().await.unwrap(); + + Ok(tracks) +} + +/** + * Return all the tracks from the DB + */ +#[tauri::command] +async fn get_all_tracks(db: State<'_, DB>) -> AnyResult>> { + db.get_all_tracks().await +} + +/** + * Return all the tracks from the DB + */ +#[tauri::command] +async fn get_all_playlists(db: State<'_, DB>) -> AnyResult>> { + db.get_all_playlists().await +} + +/** + * Try to get the cover as a base64 image for a given track path + */ +#[tauri::command] +fn get_cover_as_base64(path: String) -> AnyResult> { + // TODO: if None, scan folder for cover.jpg/png instead + let tagged_file = lofty::read_from_path(path); + + if tagged_file.is_err() { + return Ok(None); + } + + let binding = tagged_file.unwrap(); + let tag = binding.primary_tag(); + + let (format, data) = match tag { + Some(tag) => { + let maybe_cover = tag.pictures().first(); + match maybe_cover { + Some(cover) => { + let format = match cover.mime_type() { + Some(MimeType::Png) => Some("png".to_string()), + Some(MimeType::Jpeg) => Some("jpg".to_string()), + Some(MimeType::Tiff) => Some("tiff".to_string()), + Some(MimeType::Bmp) => Some("bmp".to_string()), + Some(MimeType::Gif) => Some("gif".to_string()), + _ => None, + }; + (format, BASE64_STANDARD.encode(&cover.data())) + } + None => return Ok(None), + } + } + None => return Ok(None), + }; + + if format.is_none() { + debug!("Cover has no format"); + return Ok(None); + } + + debug!("Cover was found (base 64)"); + Ok(Some(format!("data:{};base64,{}", format.unwrap(), data))) +} + +/** + * Database setup + * Doc: https://github.com/khonsulabs/bonsaidb/blob/main/examples/basic-local/examples/basic-local-multidb.rs + */ +pub async fn setup() -> AnyResult { + let conf_path = get_app_storage_dir(); + let storage_configuration = StorageConfiguration::new(conf_path.join("main.bonsaidb")) + .with_schema::()? + .with_schema::()?; + + let storage = AsyncStorage::open(storage_configuration).await?; + + let db_tracks = storage.create_database::("tracks", true).await?; + let db_playlists = storage + .create_database::("playlists", true) + .await?; + + Ok(DB { + tracks: db_tracks, + playlists: db_playlists, + }) +} + +pub fn init(db: DB) -> TauriPlugin { + Builder::::new("database") + .invoke_handler(tauri::generate_handler![ + import_tracks_to_library, + get_all_tracks, + get_all_playlists, + get_cover_as_base64 + ]) + .setup(|app_handle, _api| { + app_handle.manage(db); + Ok(()) + }) + .build() +} diff --git a/src-tauri/src/plugins/debug.rs b/src-tauri/src/plugins/debug.rs new file mode 100644 index 000000000..0ce09740c --- /dev/null +++ b/src-tauri/src/plugins/debug.rs @@ -0,0 +1,13 @@ +use tauri::plugin::{Builder, TauriPlugin}; +use tauri::Runtime; + +pub fn init() -> TauriPlugin { + Builder::::new("debug") + .on_webview_ready(|window| { + #[cfg(debug_assertions)] + { + window.open_devtools(); + } + }) + .build() +} diff --git a/src-tauri/src/plugins/default_view.rs b/src-tauri/src/plugins/default_view.rs new file mode 100644 index 000000000..a58f973dd --- /dev/null +++ b/src-tauri/src/plugins/default_view.rs @@ -0,0 +1,38 @@ +use log::info; +use tauri::plugin::{Builder, TauriPlugin}; +use tauri::{Manager, Runtime, State}; + +use crate::libs::error::AnyResult; +use crate::plugins::config::{ConfigManager, DefaultView}; + +#[tauri::command] +pub fn set(config_manager: State, default_view: DefaultView) -> AnyResult<()> { + info!("Default view set to '{:?}'", default_view); + config_manager.set_default_view(default_view); + Ok(()) +} + +/** + * Set the default view on application load based on user preference + */ +pub fn init() -> TauriPlugin { + Builder::::new("default-view") + .invoke_handler(tauri::generate_handler![set]) + .on_webview_ready(|mut window| { + if window.label().eq("main") { + let config_manager = window.state::(); + let mut url = window.url(); + let default_view = config_manager.get().default_view; + + let fragment = match default_view { + DefaultView::Library => "/library", + DefaultView::Playlists => "/playlists", + }; + + info!("Navigating to '{}'", fragment); + url.set_fragment(Some(fragment)); + window.navigate(url); + } + }) + .build() +} diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs new file mode 100644 index 000000000..382d004f9 --- /dev/null +++ b/src-tauri/src/plugins/mod.rs @@ -0,0 +1,20 @@ +/** + * Those are a bunch of Tauri plugins used to interact with the Operating Systems + * features, like global menu, sleep-blocker, dock, thumbar, etc. + * + * It also holds the different DB creations and various helpers. + */ +pub mod debug; + +/** + * Stores + */ +pub mod config; +pub mod database; + +/** + * Settings-related plugins + */ +pub mod app_menu; +pub mod default_view; +pub mod sleepblocker; diff --git a/src-tauri/src/plugins/sleepblocker.rs b/src-tauri/src/plugins/sleepblocker.rs new file mode 100644 index 000000000..0fc21a166 --- /dev/null +++ b/src-tauri/src/plugins/sleepblocker.rs @@ -0,0 +1,58 @@ +use std::sync::Mutex; + +use log::info; +use nosleep::{NoSleep, NoSleepType}; +use tauri::plugin::{Builder, TauriPlugin}; +use tauri::{Manager, Runtime, State}; + +use crate::libs::error::AnyResult; +use crate::plugins::config::ConfigManager; + +pub struct NoSleepInstance(Mutex); + +#[tauri::command] +pub fn enable( + config_manager: State, + nosleep: State, +) -> AnyResult<()> { + config_manager.set_sleepblocker(true); + + nosleep + .0 + .lock() + .unwrap() // TODO: extend AnyResult and use ? instead + .start(NoSleepType::PreventUserIdleSystemSleep)?; + + info!("Enabled sleepblocker"); + Ok(()) +} + +#[tauri::command] +pub fn disable( + config_manager: State, + nosleep: State, +) -> AnyResult<()> { + config_manager.set_sleepblocker(false); + + nosleep + .0 + .lock() + .unwrap() // TODO: extend AnyResult and use ? instead + .stop()?; + + info!("Disabled sleepblocker"); + Ok(()) +} + +/** + * Plugin in charge of preventing the app from going to sleep + */ +pub fn init() -> TauriPlugin { + Builder::::new("sleepblocker") + .invoke_handler(tauri::generate_handler![enable, disable]) + .setup(|app_handle, _api| { + app_handle.manage(NoSleepInstance(Mutex::new(NoSleep::new().unwrap()))); + Ok(()) + }) + .build() +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 000000000..605258038 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,47 @@ +{ + "productName": "museeks", + "version": "0.20.0", + "identifier": "is.museeks.app", + "build": { + "beforeDevCommand": "yarn dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "yarn build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "label": "main", + "title": "Museeks", + "visible": false, + "hiddenTitle": true, + "titleBarStyle": "Overlay", + "width": 900, + "minWidth": 900, + "height": 550, + "minHeight": 550, + "fullscreen": false, + "resizable": true, + "fileDropEnabled": false + } + ], + "security": { + "assetProtocol": { + "enable": true, + "scope": ["**/*"] + }, + "csp": "default-src 'none'; img-src 'self' data:; media-src 'self' asset: https://asset.localhost http://asset.localhost; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src ipc: http://ipc.localhost 'self' https://api.github.com; font-src 'self' data:" + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/src/shared/assets/icons/player-next.svg b/src/assets/icons/player-next.svg similarity index 100% rename from src/shared/assets/icons/player-next.svg rename to src/assets/icons/player-next.svg diff --git a/src/shared/assets/icons/player-pause.svg b/src/assets/icons/player-pause.svg similarity index 100% rename from src/shared/assets/icons/player-pause.svg rename to src/assets/icons/player-pause.svg diff --git a/src/shared/assets/icons/player-play.svg b/src/assets/icons/player-play.svg similarity index 100% rename from src/shared/assets/icons/player-play.svg rename to src/assets/icons/player-play.svg diff --git a/src/shared/assets/icons/player-previous.svg b/src/assets/icons/player-previous.svg similarity index 100% rename from src/shared/assets/icons/player-previous.svg rename to src/assets/icons/player-previous.svg diff --git a/src/shared/assets/icons/player-queue.svg b/src/assets/icons/player-queue.svg similarity index 100% rename from src/shared/assets/icons/player-queue.svg rename to src/assets/icons/player-queue.svg diff --git a/src/shared/assets/icons/player-repeat-one.svg b/src/assets/icons/player-repeat-one.svg similarity index 100% rename from src/shared/assets/icons/player-repeat-one.svg rename to src/assets/icons/player-repeat-one.svg diff --git a/src/shared/assets/icons/player-repeat.svg b/src/assets/icons/player-repeat.svg similarity index 100% rename from src/shared/assets/icons/player-repeat.svg rename to src/assets/icons/player-repeat.svg diff --git a/src/shared/assets/icons/player-shuffle.svg b/src/assets/icons/player-shuffle.svg similarity index 100% rename from src/shared/assets/icons/player-shuffle.svg rename to src/assets/icons/player-shuffle.svg diff --git a/src/shared/assets/icons/windows/backward-disabled.ico b/src/assets/icons/windows/backward-disabled.ico similarity index 100% rename from src/shared/assets/icons/windows/backward-disabled.ico rename to src/assets/icons/windows/backward-disabled.ico diff --git a/src/shared/assets/icons/windows/backward.ico b/src/assets/icons/windows/backward.ico similarity index 100% rename from src/shared/assets/icons/windows/backward.ico rename to src/assets/icons/windows/backward.ico diff --git a/src/shared/assets/icons/windows/forward-disabled.ico b/src/assets/icons/windows/forward-disabled.ico similarity index 100% rename from src/shared/assets/icons/windows/forward-disabled.ico rename to src/assets/icons/windows/forward-disabled.ico diff --git a/src/shared/assets/icons/windows/forward.ico b/src/assets/icons/windows/forward.ico similarity index 100% rename from src/shared/assets/icons/windows/forward.ico rename to src/assets/icons/windows/forward.ico diff --git a/src/shared/assets/icons/windows/pause-disabled.ico b/src/assets/icons/windows/pause-disabled.ico similarity index 100% rename from src/shared/assets/icons/windows/pause-disabled.ico rename to src/assets/icons/windows/pause-disabled.ico diff --git a/src/shared/assets/icons/windows/pause.ico b/src/assets/icons/windows/pause.ico similarity index 100% rename from src/shared/assets/icons/windows/pause.ico rename to src/assets/icons/windows/pause.ico diff --git a/src/shared/assets/icons/windows/play-disabled.ico b/src/assets/icons/windows/play-disabled.ico similarity index 100% rename from src/shared/assets/icons/windows/play-disabled.ico rename to src/assets/icons/windows/play-disabled.ico diff --git a/src/shared/assets/icons/windows/play.ico b/src/assets/icons/windows/play.ico similarity index 100% rename from src/shared/assets/icons/windows/play.ico rename to src/assets/icons/windows/play.ico diff --git a/src/shared/assets/logos/museeks-128.png b/src/assets/logos/museeks-128.png similarity index 100% rename from src/shared/assets/logos/museeks-128.png rename to src/assets/logos/museeks-128.png diff --git a/src/shared/assets/logos/museeks-128@2x.png b/src/assets/logos/museeks-128@2x.png similarity index 100% rename from src/shared/assets/logos/museeks-128@2x.png rename to src/assets/logos/museeks-128@2x.png diff --git a/src/shared/assets/logos/museeks-32.png b/src/assets/logos/museeks-32.png similarity index 100% rename from src/shared/assets/logos/museeks-32.png rename to src/assets/logos/museeks-32.png diff --git a/src/shared/assets/logos/museeks-32@2x.png b/src/assets/logos/museeks-32@2x.png similarity index 100% rename from src/shared/assets/logos/museeks-32@2x.png rename to src/assets/logos/museeks-32@2x.png diff --git a/src/shared/assets/logos/museeks-48.png b/src/assets/logos/museeks-48.png similarity index 100% rename from src/shared/assets/logos/museeks-48.png rename to src/assets/logos/museeks-48.png diff --git a/src/shared/assets/logos/museeks-48@2x.png b/src/assets/logos/museeks-48@2x.png similarity index 100% rename from src/shared/assets/logos/museeks-48@2x.png rename to src/assets/logos/museeks-48@2x.png diff --git a/src/shared/assets/logos/museeks-64.png b/src/assets/logos/museeks-64.png similarity index 100% rename from src/shared/assets/logos/museeks-64.png rename to src/assets/logos/museeks-64.png diff --git a/src/shared/assets/logos/museeks-64@2x.png b/src/assets/logos/museeks-64@2x.png similarity index 100% rename from src/shared/assets/logos/museeks-64@2x.png rename to src/assets/logos/museeks-64@2x.png diff --git a/src/shared/assets/logos/museeks-tray-dark.png b/src/assets/logos/museeks-tray-dark.png similarity index 100% rename from src/shared/assets/logos/museeks-tray-dark.png rename to src/assets/logos/museeks-tray-dark.png diff --git a/src/shared/assets/logos/museeks-tray-dark@2x.png b/src/assets/logos/museeks-tray-dark@2x.png similarity index 100% rename from src/shared/assets/logos/museeks-tray-dark@2x.png rename to src/assets/logos/museeks-tray-dark@2x.png diff --git a/src/shared/assets/logos/museeks-tray.ico b/src/assets/logos/museeks-tray.ico similarity index 100% rename from src/shared/assets/logos/museeks-tray.ico rename to src/assets/logos/museeks-tray.ico diff --git a/src/shared/assets/logos/museeks-tray.png b/src/assets/logos/museeks-tray.png similarity index 100% rename from src/shared/assets/logos/museeks-tray.png rename to src/assets/logos/museeks-tray.png diff --git a/src/shared/assets/logos/museeks.icns b/src/assets/logos/museeks.icns similarity index 100% rename from src/shared/assets/logos/museeks.icns rename to src/assets/logos/museeks.icns diff --git a/src/shared/assets/logos/museeks.ico b/src/assets/logos/museeks.ico similarity index 100% rename from src/shared/assets/logos/museeks.ico rename to src/assets/logos/museeks.ico diff --git a/src/shared/assets/logos/museeks.png b/src/assets/logos/museeks.png similarity index 100% rename from src/shared/assets/logos/museeks.png rename to src/assets/logos/museeks.png diff --git a/src/shared/assets/placeholder.png b/src/assets/placeholder.png similarity index 100% rename from src/shared/assets/placeholder.png rename to src/assets/placeholder.png diff --git a/src/renderer/components/AudioOutputSelect/AudioOutputSelect.tsx b/src/components/AudioOutputSelect/AudioOutputSelect.tsx similarity index 65% rename from src/renderer/components/AudioOutputSelect/AudioOutputSelect.tsx rename to src/components/AudioOutputSelect/AudioOutputSelect.tsx index cfe2dac2e..9fd6e4769 100644 --- a/src/renderer/components/AudioOutputSelect/AudioOutputSelect.tsx +++ b/src/components/AudioOutputSelect/AudioOutputSelect.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; -import logger from '../../../shared/lib/logger'; +import logger from '../../lib/logger'; import * as Setting from '../Setting/Setting'; type Props = { @@ -15,9 +15,20 @@ export default function AudioOutputSelect(props: Props) { useEffect(() => { const refreshDevices = async () => { try { + // Webkit sucks, we need to request permissions for inputs, when we only + // need outputs + // const test = await navigator.mediaDevices.getUserMedia({ + // audio: true, + // video: false, + // }); + // console.log('media', test); const devices = await navigator.mediaDevices.enumerateDevices(); - - setDevices(devices.filter((device) => device.kind === 'audiooutput')); + // console.log('all devices', devices); + const audioDevices = devices.filter( + (device) => device.kind === 'audiooutput', + ); + // console.log('audioDevices', audioDevices); + setDevices(audioDevices); } catch (err) { setDevices([]); setHasError(true); @@ -34,17 +45,17 @@ export default function AudioOutputSelect(props: Props) { if (!devices) { return ( - + ); } if (hasError) { return ( - + ); } diff --git a/src/renderer/components/Cover/Cover.module.css b/src/components/Cover/Cover.module.css similarity index 100% rename from src/renderer/components/Cover/Cover.module.css rename to src/components/Cover/Cover.module.css diff --git a/src/renderer/components/Cover/Cover.tsx b/src/components/Cover/Cover.tsx similarity index 78% rename from src/renderer/components/Cover/Cover.tsx rename to src/components/Cover/Cover.tsx index ce2d1f324..b29b8166e 100644 --- a/src/renderer/components/Cover/Cover.tsx +++ b/src/components/Cover/Cover.tsx @@ -1,12 +1,13 @@ import { useEffect, useState } from 'react'; import * as AspectRatio from '@radix-ui/react-aspect-ratio'; +import { invoke } from '@tauri-apps/api/core'; -import { Track } from '../../../shared/types/museeks'; +import type { TrackDoc } from '../../generated/typings'; import styles from './Cover.module.css'; type Props = { - track: Track; + track: TrackDoc; }; export default function Cover(props: Props) { @@ -14,8 +15,11 @@ export default function Cover(props: Props) { useEffect(() => { const refreshCover = async () => { - const coverPath = await window.MuseeksAPI.covers.getCoverAsBase64( - props.track, + const coverPath: string | null = await invoke( + 'plugin:database|get_cover_as_base64', + { + path: props.track.doc.path, + }, ); setCoverPath(coverPath); }; diff --git a/src/renderer/components/DropzoneImport/DropzoneImport.module.css b/src/components/DropzoneImport/DropzoneImport.module.css similarity index 100% rename from src/renderer/components/DropzoneImport/DropzoneImport.module.css rename to src/components/DropzoneImport/DropzoneImport.module.css diff --git a/src/components/DropzoneImport/DropzoneImport.tsx b/src/components/DropzoneImport/DropzoneImport.tsx new file mode 100644 index 000000000..ea063636c --- /dev/null +++ b/src/components/DropzoneImport/DropzoneImport.tsx @@ -0,0 +1,54 @@ +import cx from 'classnames'; +// import { getCurrent } from '@tauri-apps/api/window'; +import { useState } from 'react'; + +import styles from './DropzoneImport.module.css'; + +type Props = { + // webview: Webview; +}; + +export default function DropzoneImport(props: Props) { + const [isShown, setIsShown] = useState(false); + + // const unlisten = await getCurrent().onFileDropEvent((event) => { + // if (event.payload.type === 'hover') { + // console.log('test'); + // setIsShown(true); + // } else if (event.payload.type === 'drop') { + // console.log('test1'); + // alert('drop'); + // setIsShown(false); + // } else { + // console.log('test2'); + // setIsShown(false); + // } + // }); + + // useEffect(() => { + // return unlisten; + // }, [unlisten]); + + const classes = cx(styles.dropzone, { + [styles.shown]: isShown, + }); + + // TODO: Fix this, drop files from TAURI instead + // const files = item.files.map((file) => file.path); + // libraryAPI + // .add(files) + // .then((/* _importedTracks */) => { + // // TODO: Import to playlist here + // }) + // .catch((err) => { + // logger.warn(err); + // }); + return ( +
+
Add music to the library
+
+ Drop files or folders anywhere +
+
+ ); +} diff --git a/src/renderer/components/Events/AppEvents.tsx b/src/components/Events/AppEvents.tsx similarity index 90% rename from src/renderer/components/Events/AppEvents.tsx rename to src/components/Events/AppEvents.tsx index 721d87b09..7329cad94 100644 --- a/src/renderer/components/Events/AppEvents.tsx +++ b/src/components/Events/AppEvents.tsx @@ -4,9 +4,9 @@ import type { IpcRendererEvent } from 'electron'; import player from '../../lib/player'; import { preventNativeDefault } from '../../lib/utils-events'; import SettingsAPI from '../../stores/SettingsAPI'; -import channels from '../../../shared/lib/ipc-channels'; -import type { Theme } from '../../../shared/types/museeks'; -import logger from '../../../shared/lib/logger'; +import channels from '../../lib/ipc-channels'; +import type { Theme } from '../../types/museeks'; +import logger from '../../lib/logger'; const { ipcRenderer } = window.ElectronAPI; diff --git a/src/renderer/components/Events/GlobalKeyBindings.tsx b/src/components/Events/GlobalKeyBindings.tsx similarity index 94% rename from src/renderer/components/Events/GlobalKeyBindings.tsx rename to src/components/Events/GlobalKeyBindings.tsx index bec92c54a..59cab3f84 100644 --- a/src/renderer/components/Events/GlobalKeyBindings.tsx +++ b/src/components/Events/GlobalKeyBindings.tsx @@ -13,7 +13,7 @@ function GlobalKeyBindings() { const navigate = useNavigate(); const playerAPI = usePlayerAPI(); - // App shortcuts (not using Electron's global shortcuts API to avoid conflicts + // App shortcuts (not using global shortcuts API to avoid conflicts // with other applications) const onKey = useCallback( async (e: KeyboardEvent) => { diff --git a/src/renderer/components/Events/IPCNavigationEvents.tsx b/src/components/Events/IPCNavigationEvents.tsx similarity index 91% rename from src/renderer/components/Events/IPCNavigationEvents.tsx rename to src/components/Events/IPCNavigationEvents.tsx index 93381a64f..d926c3a8c 100644 --- a/src/renderer/components/Events/IPCNavigationEvents.tsx +++ b/src/components/Events/IPCNavigationEvents.tsx @@ -2,9 +2,7 @@ import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { usePlayerAPI } from '../../stores/usePlayerStore'; -import channels from '../../../shared/lib/ipc-channels'; - -const { ipcRenderer } = window.ElectronAPI; +import channels from '../../lib/ipc-channels'; /** * Handle app-level IPC Navigation events diff --git a/src/renderer/components/Events/IPCPlayerEvents.tsx b/src/components/Events/IPCPlayerEvents.tsx similarity index 97% rename from src/renderer/components/Events/IPCPlayerEvents.tsx rename to src/components/Events/IPCPlayerEvents.tsx index 7755af7f3..a56cd662c 100644 --- a/src/renderer/components/Events/IPCPlayerEvents.tsx +++ b/src/components/Events/IPCPlayerEvents.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import channels from '../../../shared/lib/ipc-channels'; +import channels from '../../lib/ipc-channels'; import { usePlayerAPI } from '../../stores/usePlayerStore'; import useCurrentViewTracks from '../../hooks/useCurrentViewTracks'; import player from '../../lib/player'; diff --git a/src/renderer/components/Events/MediaSessionEvents.tsx b/src/components/Events/MediaSessionEvents.tsx similarity index 85% rename from src/renderer/components/Events/MediaSessionEvents.tsx rename to src/components/Events/MediaSessionEvents.tsx index da3006977..b80ceea93 100644 --- a/src/renderer/components/Events/MediaSessionEvents.tsx +++ b/src/components/Events/MediaSessionEvents.tsx @@ -1,4 +1,5 @@ import { useEffect } from 'react'; +import { invoke } from '@tauri-apps/api/core'; import { usePlayerAPI } from '../../stores/usePlayerStore'; import player from '../../lib/player'; @@ -48,12 +49,17 @@ async function syncArtwork() { const track = player.getTrack(); if (track) { - const cover = await window.MuseeksAPI.covers.getCoverAsBase64(track); + const cover: string | null = await invoke( + 'plugin:database|get_cover_as_base64', + { + path: track.doc.path, + }, + ); navigator.mediaSession.metadata = new MediaMetadata({ - title: track.title, - artist: track.artist.join(', '), - album: track.album, + title: track.doc.title, + artist: track.doc.artists.join(', '), + album: track.doc.album, artwork: cover ? [{ src: cover }] : undefined, }); } diff --git a/src/renderer/components/Events/PlayerEvents.tsx b/src/components/Events/PlayerEvents.tsx similarity index 88% rename from src/renderer/components/Events/PlayerEvents.tsx rename to src/components/Events/PlayerEvents.tsx index 69ae424cc..0fe6d46c3 100644 --- a/src/renderer/components/Events/PlayerEvents.tsx +++ b/src/components/Events/PlayerEvents.tsx @@ -66,22 +66,14 @@ function PlayerEvents() { } } - function incrementPlayCount() { - if (player.isThresholdReached()) { - const track = player.getTrack(); - if (track) libraryAPI.incrementPlayCount(track); - } - } // Bind player events // Audio Events player.getAudio().addEventListener('ended', playerAPI.next); player.getAudio().addEventListener('error', handleAudioError); - player.getAudio().addEventListener('timeupdate', incrementPlayCount); return function cleanup() { player.getAudio().removeEventListener('ended', playerAPI.next); player.getAudio().removeEventListener('error', handleAudioError); - player.getAudio().removeEventListener('timeupdate', incrementPlayCount); }; }, [libraryAPI, toastsAPI, playerAPI]); diff --git a/src/renderer/components/Footer/Footer.module.css b/src/components/Footer/Footer.module.css similarity index 100% rename from src/renderer/components/Footer/Footer.module.css rename to src/components/Footer/Footer.module.css diff --git a/src/renderer/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx similarity index 100% rename from src/renderer/components/Footer/Footer.tsx rename to src/components/Footer/Footer.tsx diff --git a/src/renderer/components/Header/Header.module.css b/src/components/Header/Header.module.css similarity index 83% rename from src/renderer/components/Header/Header.module.css rename to src/components/Header/Header.module.css index bf0e89536..6f84a2314 100644 --- a/src/renderer/components/Header/Header.module.css +++ b/src/components/Header/Header.module.css @@ -1,10 +1,12 @@ -:global(.os__darwin) .header__mainControls { +/** TODO: remove macos from here once we have inset window styles */ + +:global(.os__macos) .header__mainControls { padding-left: 65px; /* let some space for titleBarStyle */ } /* The native frame may be light, so we need to increase the contrast between the frame and the header */ -:global(.os__win32), +:global(.os__windows), :global(.os__linux) { .header { border-top: 1px solid var(--border-color); @@ -14,6 +16,7 @@ .header { border-bottom: solid 1px var(--border-color); background-color: var(--header-bg); + box-sizing: content-box; color: var(--header-color); padding: 0 10px; display: flex; @@ -21,9 +24,6 @@ justify-content: space-between; height: 50px; flex: 0 0 auto; - - /* Draggable region (zone able to move the window) */ - -webkit-app-region: drag; } .header__mainControls { @@ -31,10 +31,10 @@ display: flex; align-items: center; padding-right: 10px; + min-width: 240px; } .header__search { - -webkit-app-region: no-drag; margin-left: 12px; flex: 0 0 auto; display: flex; @@ -60,11 +60,9 @@ background: transparent; font-size: 14px; box-shadow: none; - -webkit-app-region: no-drag; } .queueContainer { - -webkit-app-region: no-drag; display: none; position: absolute; z-index: 1000; diff --git a/src/renderer/components/Header/Header.tsx b/src/components/Header/Header.tsx similarity index 74% rename from src/renderer/components/Header/Header.tsx rename to src/components/Header/Header.tsx index 98613f661..b5e159c53 100644 --- a/src/renderer/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -14,17 +14,17 @@ export default function Header() { const queueCursor = usePlayerStore((state) => state.queueCursor); return ( -
-
+
+
-
+
-
+
- @@ -42,7 +42,7 @@ export default function Header() {
-
+
diff --git a/src/renderer/components/PlayerControls/PlayerControls.module.css b/src/components/PlayerControls/PlayerControls.module.css similarity index 94% rename from src/renderer/components/PlayerControls/PlayerControls.module.css rename to src/components/PlayerControls/PlayerControls.module.css index f7c76039a..93c0b8144 100644 --- a/src/renderer/components/PlayerControls/PlayerControls.module.css +++ b/src/components/PlayerControls/PlayerControls.module.css @@ -1,5 +1,4 @@ .playerControls { - -webkit-app-region: no-drag; display: flex; align-items: center; position: relative; diff --git a/src/renderer/components/PlayerControls/PlayerControls.tsx b/src/components/PlayerControls/PlayerControls.tsx similarity index 94% rename from src/renderer/components/PlayerControls/PlayerControls.tsx rename to src/components/PlayerControls/PlayerControls.tsx index 8b8c0abb0..b0aeefb10 100644 --- a/src/renderer/components/PlayerControls/PlayerControls.tsx +++ b/src/components/PlayerControls/PlayerControls.tsx @@ -1,7 +1,7 @@ import Icon from 'react-fontawesome'; import VolumeControl from '../VolumeControl/VolumeControl'; -import { PlayerStatus } from '../../../shared/types/museeks'; +import { PlayerStatus } from '../../types/museeks'; import usePlayerStore, { usePlayerAPI } from '../../stores/usePlayerStore'; import styles from './PlayerControls.module.css'; diff --git a/src/renderer/components/PlayerOptionsButtons/ButtonRepeat.tsx b/src/components/PlayerOptionsButtons/ButtonRepeat.tsx similarity index 64% rename from src/renderer/components/PlayerOptionsButtons/ButtonRepeat.tsx rename to src/components/PlayerOptionsButtons/ButtonRepeat.tsx index 2ac2fb4c8..b175ee26d 100644 --- a/src/renderer/components/PlayerOptionsButtons/ButtonRepeat.tsx +++ b/src/components/PlayerOptionsButtons/ButtonRepeat.tsx @@ -1,26 +1,30 @@ import InlineSVG from 'svg-inline-react'; import cx from 'classnames'; -import { Repeat } from '../../../shared/types/museeks'; import icons from '../../lib/icons'; import usePlayerStore, { usePlayerAPI } from '../../stores/usePlayerStore'; +import { Repeat } from '../../generated/typings'; import styles from './common.module.css'; -const svgMap = { - [Repeat.ONE]: icons.REPEAT_ONE, - [Repeat.ALL]: icons.REPEAT, - [Repeat.NONE]: icons.REPEAT, - default: icons.REPEAT, -}; +function getIcon(repeat: Repeat) { + switch (repeat) { + case 'One': + return icons.REPEAT_ONE; + case 'None': + case 'All': + default: + return icons.REPEAT; + } +} export default function ButtonRepeat() { const repeat = usePlayerStore((state) => state.repeat); const playerAPI = usePlayerAPI(); - const svg = svgMap[repeat] || svgMap.default; + const svg = getIcon(repeat); const buttonClasses = cx(styles.button, { - [styles.active]: repeat === Repeat.ONE || repeat === Repeat.ALL, + [styles.active]: repeat === 'One' || repeat === 'All', }); return ( diff --git a/src/renderer/components/PlayerOptionsButtons/ButtonShuffle.tsx b/src/components/PlayerOptionsButtons/ButtonShuffle.tsx similarity index 100% rename from src/renderer/components/PlayerOptionsButtons/ButtonShuffle.tsx rename to src/components/PlayerOptionsButtons/ButtonShuffle.tsx diff --git a/src/renderer/components/PlayerOptionsButtons/common.module.css b/src/components/PlayerOptionsButtons/common.module.css similarity index 91% rename from src/renderer/components/PlayerOptionsButtons/common.module.css rename to src/components/PlayerOptionsButtons/common.module.css index 95a3a5949..a2e3257d7 100644 --- a/src/renderer/components/PlayerOptionsButtons/common.module.css +++ b/src/components/PlayerOptionsButtons/common.module.css @@ -7,11 +7,11 @@ } .button { - -webkit-app-region: no-drag; border: 0; color: inherit; background: transparent; font-size: 20px; + padding: 0 8px; &.active { .icon > svg { diff --git a/src/renderer/components/PlayingBar/PlayingBar.module.css b/src/components/PlayingBar/PlayingBar.module.css similarity index 93% rename from src/renderer/components/PlayingBar/PlayingBar.module.css rename to src/components/PlayingBar/PlayingBar.module.css index 84a8c9920..394505257 100644 --- a/src/renderer/components/PlayingBar/PlayingBar.module.css +++ b/src/components/PlayingBar/PlayingBar.module.css @@ -17,7 +17,6 @@ } .playerOptions { - -webkit-app-region: no-drag; flex-shrink: 0; display: flex; align-items: center; diff --git a/src/renderer/components/PlayingBar/PlayingBar.tsx b/src/components/PlayingBar/PlayingBar.tsx similarity index 100% rename from src/renderer/components/PlayingBar/PlayingBar.tsx rename to src/components/PlayingBar/PlayingBar.tsx diff --git a/src/renderer/components/PlayingBarInfo/PlayingBarInfo.module.css b/src/components/PlayingBarInfo/PlayingBarInfo.module.css similarity index 91% rename from src/renderer/components/PlayingBarInfo/PlayingBarInfo.module.css rename to src/components/PlayingBarInfo/PlayingBarInfo.module.css index 034f57a0a..49844521c 100644 --- a/src/renderer/components/PlayingBarInfo/PlayingBarInfo.module.css +++ b/src/components/PlayingBarInfo/PlayingBarInfo.module.css @@ -5,6 +5,7 @@ flex-direction: column; justify-content: flex-end; padding: 0 4px; + padding-left: 8px; } .playingBar__info__metas { @@ -12,6 +13,7 @@ display: flex; align-items: flex-end; justify-content: center; + pointer-events: none; } .metas { @@ -38,4 +40,5 @@ padding: 0 2px; font-variant-numeric: tabular-nums; white-space: nowrap; + opacity: 0.7; } diff --git a/src/renderer/components/PlayingBarInfo/PlayingBarInfo.tsx b/src/components/PlayingBarInfo/PlayingBarInfo.tsx similarity index 72% rename from src/renderer/components/PlayingBarInfo/PlayingBarInfo.tsx rename to src/components/PlayingBarInfo/PlayingBarInfo.tsx index c8bbd3c25..f965f2cb4 100644 --- a/src/renderer/components/PlayingBarInfo/PlayingBarInfo.tsx +++ b/src/components/PlayingBarInfo/PlayingBarInfo.tsx @@ -1,12 +1,12 @@ import * as utils from '../../lib/utils'; -import { TrackModel, Repeat } from '../../../shared/types/museeks'; import TrackProgress from '../TrackProgress/TrackProgress'; import usePlayingTrackCurrentTime from '../../hooks/usePlayingTrackCurrentTime'; +import { TrackDoc, Repeat } from '../../generated/typings'; import styles from './PlayingBarInfo.module.css'; type Props = { - trackPlaying: TrackModel; + trackPlaying: TrackDoc; shuffle: boolean; repeat: Repeat; }; @@ -16,22 +16,23 @@ export default function PlayingBarInfo(props: Props) { const elapsed = usePlayingTrackCurrentTime(); return ( -
+
{utils.parseDuration(elapsed)}
+
- {trackPlaying.title} + {trackPlaying.doc.title}
- {trackPlaying.artist.join(', ')} + {trackPlaying.doc.artists.join(', ')}  â€”  - {trackPlaying.album} + {trackPlaying.doc.album}
- {utils.parseDuration(trackPlaying.duration)} + {utils.parseDuration(trackPlaying.doc.duration)}
diff --git a/src/renderer/components/PlayingIndicator/PlayingIndicator.module.css b/src/components/PlayingIndicator/PlayingIndicator.module.css similarity index 100% rename from src/renderer/components/PlayingIndicator/PlayingIndicator.module.css rename to src/components/PlayingIndicator/PlayingIndicator.module.css diff --git a/src/renderer/components/PlayingIndicator/PlayingIndicator.tsx b/src/components/PlayingIndicator/PlayingIndicator.tsx similarity index 95% rename from src/renderer/components/PlayingIndicator/PlayingIndicator.tsx rename to src/components/PlayingIndicator/PlayingIndicator.tsx index b0d594d9c..671b17586 100644 --- a/src/renderer/components/PlayingIndicator/PlayingIndicator.tsx +++ b/src/components/PlayingIndicator/PlayingIndicator.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import Icon from 'react-fontawesome'; -import { PlayerStatus } from '../../../shared/types/museeks'; +import { PlayerStatus } from '../../types/museeks'; import usePlayerStore, { usePlayerAPI } from '../../stores/usePlayerStore'; import styles from './PlayingIndicator.module.css'; diff --git a/src/renderer/components/PlaylistsNav/PlaylistsNav.module.css b/src/components/PlaylistsNav/PlaylistsNav.module.css similarity index 100% rename from src/renderer/components/PlaylistsNav/PlaylistsNav.module.css rename to src/components/PlaylistsNav/PlaylistsNav.module.css diff --git a/src/renderer/components/PlaylistsNav/PlaylistsNav.tsx b/src/components/PlaylistsNav/PlaylistsNav.tsx similarity index 70% rename from src/renderer/components/PlaylistsNav/PlaylistsNav.tsx rename to src/components/PlaylistsNav/PlaylistsNav.tsx index 79f2d3363..657d606bf 100644 --- a/src/renderer/components/PlaylistsNav/PlaylistsNav.tsx +++ b/src/components/PlaylistsNav/PlaylistsNav.tsx @@ -1,59 +1,56 @@ /* eslint-disable jsx-a11y/no-autofocus */ -import type { MenuItemConstructorOptions } from 'electron'; import React, { useCallback, useState } from 'react'; import Icon from 'react-fontawesome'; import PlaylistsAPI from '../../stores/PlaylistsAPI'; import PlaylistsNavLink from '../PlaylistsNavLink/PlaylistsNavLink'; -import { PlaylistModel } from '../../../shared/types/museeks'; +import { PlaylistDoc } from '../../generated/typings'; import styles from './PlaylistsNav.module.css'; -const { menu } = window.ElectronAPI; - type Props = { - playlists: PlaylistModel[]; + playlists: PlaylistDoc[]; }; export default function PlaylistsNav(props: Props) { const [renamed, setRenamed] = useState(null); const showContextMenu = useCallback((playlistID: string) => { - const template: MenuItemConstructorOptions[] = [ - { - label: 'Rename', - click: () => { - setRenamed(playlistID); - }, - }, - { - label: 'Delete', - click: async () => { - await PlaylistsAPI.remove(playlistID); - }, - }, - { - type: 'separator', - }, - { - label: 'Duplicate', - click: async () => { - await PlaylistsAPI.duplicate(playlistID); - }, - }, - { - type: 'separator', - }, - { - label: 'Export', - click: async () => { - await PlaylistsAPI.exportToM3u(playlistID); - }, - }, - ]; - - menu.showContextMenu(template); + // TODO: implement menu + // const template: MenuItemConstructorOptions[] = [ + // { + // label: 'Rename', + // click: () => { + // setRenamed(playlistID); + // }, + // }, + // { + // label: 'Delete', + // click: async () => { + // await PlaylistsAPI.remove(playlistID); + // }, + // }, + // { + // type: 'separator', + // }, + // { + // label: 'Duplicate', + // click: async () => { + // await PlaylistsAPI.duplicate(playlistID); + // }, + // }, + // { + // type: 'separator', + // }, + // { + // label: 'Export', + // click: async () => { + // await PlaylistsAPI.exportToM3u(playlistID); + // }, + // }, + // ]; + // menu.showContextMenu(template); }, []); const createPlaylist = useCallback(async () => { @@ -108,16 +105,15 @@ export default function PlaylistsNav(props: Props) { const { playlists } = props; - // TODO (y.solovyov): extract into separate method that returns items const nav = playlists.map((elem) => { let navItemContent; - if (elem._id === renamed) { + if (elem.id === renamed) { navItemContent = ( - {elem.name} + {elem.doc.name} ); } - return
{navItemContent}
; + return
{navItemContent}
; }); return ( diff --git a/src/renderer/components/PlaylistsNavLink/PlaylistsNavLink.module.css b/src/components/PlaylistsNavLink/PlaylistsNavLink.module.css similarity index 100% rename from src/renderer/components/PlaylistsNavLink/PlaylistsNavLink.module.css rename to src/components/PlaylistsNavLink/PlaylistsNavLink.module.css diff --git a/src/renderer/components/PlaylistsNavLink/PlaylistsNavLink.tsx b/src/components/PlaylistsNavLink/PlaylistsNavLink.tsx similarity index 100% rename from src/renderer/components/PlaylistsNavLink/PlaylistsNavLink.tsx rename to src/components/PlaylistsNavLink/PlaylistsNavLink.tsx diff --git a/src/renderer/components/ProgressBar/ProgressBar.module.css b/src/components/ProgressBar/ProgressBar.module.css similarity index 100% rename from src/renderer/components/ProgressBar/ProgressBar.module.css rename to src/components/ProgressBar/ProgressBar.module.css diff --git a/src/renderer/components/ProgressBar/ProgressBar.tsx b/src/components/ProgressBar/ProgressBar.tsx similarity index 100% rename from src/renderer/components/ProgressBar/ProgressBar.tsx rename to src/components/ProgressBar/ProgressBar.tsx diff --git a/src/renderer/components/Queue/Queue.module.css b/src/components/Queue/Queue.module.css similarity index 84% rename from src/renderer/components/Queue/Queue.module.css rename to src/components/Queue/Queue.module.css index 6b31f98d8..f7431944b 100644 --- a/src/renderer/components/Queue/Queue.module.css +++ b/src/components/Queue/Queue.module.css @@ -2,6 +2,7 @@ width: 300px; background: var(--queue-bg); border: solid 1px var(--border-color); + border-radius: var(--border-radius); text-overflow: ellipsis; overflow-x: hidden; font-size: 12px; diff --git a/src/renderer/components/Queue/Queue.tsx b/src/components/Queue/Queue.tsx similarity index 90% rename from src/renderer/components/Queue/Queue.tsx rename to src/components/Queue/Queue.tsx index 380cf9a6f..9da69eef4 100644 --- a/src/renderer/components/Queue/Queue.tsx +++ b/src/components/Queue/Queue.tsx @@ -2,12 +2,12 @@ import React, { useMemo } from 'react'; import QueueEmpty from '../QueueEmpty/QueueEmpty'; import QueueList from '../QueueList/QueueList'; -import { TrackModel } from '../../../shared/types/museeks'; +import { TrackDoc } from '../../generated/typings'; import styles from './Queue.module.css'; type Props = { - queue: TrackModel[]; + queue: TrackDoc[]; queueCursor: number | null; }; diff --git a/src/renderer/components/QueueEmpty/QueueEmpty.module.css b/src/components/QueueEmpty/QueueEmpty.module.css similarity index 100% rename from src/renderer/components/QueueEmpty/QueueEmpty.module.css rename to src/components/QueueEmpty/QueueEmpty.module.css diff --git a/src/renderer/components/QueueEmpty/QueueEmpty.tsx b/src/components/QueueEmpty/QueueEmpty.tsx similarity index 100% rename from src/renderer/components/QueueEmpty/QueueEmpty.tsx rename to src/components/QueueEmpty/QueueEmpty.tsx diff --git a/src/renderer/components/QueueList/QueueList.module.css b/src/components/QueueList/QueueList.module.css similarity index 100% rename from src/renderer/components/QueueList/QueueList.module.css rename to src/components/QueueList/QueueList.module.css diff --git a/src/renderer/components/QueueList/QueueList.tsx b/src/components/QueueList/QueueList.tsx similarity index 95% rename from src/renderer/components/QueueList/QueueList.tsx rename to src/components/QueueList/QueueList.tsx index eb5af15e8..a6811fa30 100644 --- a/src/renderer/components/QueueList/QueueList.tsx +++ b/src/components/QueueList/QueueList.tsx @@ -2,16 +2,16 @@ import React, { useCallback, useState } from 'react'; import QueueListItem from '../QueueListItem/QueueListItem'; import { getStatus } from '../../lib/utils-library'; -import { TrackModel } from '../../../shared/types/museeks'; import Button from '../../elements/Button/Button'; import { usePlayerAPI } from '../../stores/usePlayerStore'; +import { TrackDoc } from '../../generated/typings'; import styles from './QueueList.module.css'; const INITIAL_QUEUE_SIZE = 20; type Props = { - queue: TrackModel[]; + queue: TrackDoc[]; queueCursor: number; }; @@ -30,7 +30,7 @@ export default function QueueList(props: Props) { const dragStart = useCallback( (e: React.DragEvent, index: number) => { - e.dataTransfer.setData('text/html', props.queue[index]._id); + e.dataTransfer.setData('text/html', props.queue[index].id); e.dataTransfer.dropEffect = 'move'; e.dataTransfer.effectAllowed = 'move'; @@ -117,7 +117,7 @@ export default function QueueList(props: Props) {
{shownQueue.map((track, index) => ( , index: number) => void; onDragOver: (e: React.DragEvent, index: number) => void; onDragEnd: React.DragEventHandler; @@ -61,9 +61,9 @@ export default function QueueListItem(props: Props) { onDragEnd={props.onDragEnd} >
-
{track.title}
+
{track.doc.title}
- {track.artist} - {track.album} + {track.doc.artists[0]} - {track.doc.album}
); diff --git a/src/renderer/components/TracksList/TracksList.module.css b/src/components/TracksList/TracksList.module.css similarity index 100% rename from src/renderer/components/TracksList/TracksList.module.css rename to src/components/TracksList/TracksList.module.css diff --git a/src/renderer/components/TracksList/TracksList.tsx b/src/components/TracksList/TracksList.tsx similarity index 65% rename from src/renderer/components/TracksList/TracksList.tsx rename to src/components/TracksList/TracksList.tsx index 977ff9b66..14791a505 100644 --- a/src/renderer/components/TracksList/TracksList.tsx +++ b/src/components/TracksList/TracksList.tsx @@ -1,4 +1,3 @@ -import type { MenuItemConstructorOptions } from 'electron'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import Keybinding from 'react-keybinding-component'; import { useNavigate } from 'react-router-dom'; @@ -6,25 +5,18 @@ import { useVirtualizer } from '@tanstack/react-virtual'; import TrackRow from '../TrackRow/TrackRow'; import TracksListHeader from '../TracksListHeader/TracksListHeader'; -import PlaylistsAPI from '../../stores/PlaylistsAPI'; import { isLeftClick, isRightClick, isCtrlKey, isAltKey, } from '../../lib/utils-events'; -import { - Config, - PlaylistModel, - TrackModel, -} from '../../../shared/types/museeks'; import { usePlayerAPI } from '../../stores/usePlayerStore'; import useLibraryStore, { useLibraryAPI } from '../../stores/useLibraryStore'; +import { Config, PlaylistDoc, TrackDoc } from '../../generated/typings'; import styles from './TracksList.module.css'; -const { menu } = window.ElectronAPI; - const ROW_HEIGHT = 30; const ROW_HEIGHT_COMPACT = 24; @@ -34,10 +26,10 @@ const ROW_HEIGHT_COMPACT = 24; type Props = { type: string; - tracks: TrackModel[]; - tracksDensity: Config['tracksDensity']; + tracks: TrackDoc[]; + tracksDensity: Config['track_view_density']; trackPlayingID: string | null; - playlists: PlaylistModel[]; + playlists: PlaylistDoc[]; currentPlaylist?: string; reorderable?: boolean; onReorder?: ( @@ -79,7 +71,7 @@ export default function TracksList(props: Props) { return ROW_HEIGHT; } }, - getItemKey: (index) => tracks[index]._id, + getItemKey: (index) => tracks[index].id, }); const playerAPI = usePlayerAPI(); @@ -93,7 +85,7 @@ export default function TracksList(props: Props) { setSelected([trackPlayingID]); const playingTrackIndex = tracks.findIndex( - (track) => track._id === trackPlayingID, + (track) => track.id === trackPlayingID, ); if (playingTrackIndex >= 0) { @@ -118,25 +110,25 @@ export default function TracksList(props: Props) { * Keyboard navigations events/helpers */ const onEnter = useCallback( - async (index: number, tracks: TrackModel[]) => { - if (index !== -1) playerAPI.start(tracks, tracks[index]._id); + async (index: number, tracks: TrackDoc[]) => { + if (index !== -1) playerAPI.start(tracks, tracks[index].id); }, [playerAPI], ); - const onControlAll = useCallback((tracks: TrackModel[]) => { - setSelected(tracks.map((track) => track._id)); + const onControlAll = useCallback((tracks: TrackDoc[]) => { + setSelected(tracks.map((track) => track.id)); }, []); const onUp = useCallback( - (index: number, tracks: TrackModel[], shiftKeyPressed: boolean) => { + (index: number, tracks: TrackDoc[], shiftKeyPressed: boolean) => { const addedIndex = Math.max(0, index - 1); // Add to the selection if shift key is pressed let newSelected = selected; - if (shiftKeyPressed) newSelected = [tracks[addedIndex]._id, ...selected]; - else newSelected = [tracks[addedIndex]._id]; + if (shiftKeyPressed) newSelected = [tracks[addedIndex].id, ...selected]; + else newSelected = [tracks[addedIndex].id]; setSelected(newSelected); virtualizer.scrollToIndex(addedIndex); @@ -145,13 +137,13 @@ export default function TracksList(props: Props) { ); const onDown = useCallback( - (index: number, tracks: TrackModel[], shiftKeyPressed: boolean) => { + (index: number, tracks: TrackDoc[], shiftKeyPressed: boolean) => { const addedIndex = Math.min(tracks.length - 1, index + 1); // Add to the selection if shift key is pressed let newSelected = selected; - if (shiftKeyPressed) newSelected = [...selected, tracks[addedIndex]._id]; - else newSelected = [tracks[addedIndex]._id]; + if (shiftKeyPressed) newSelected = [...selected, tracks[addedIndex].id]; + else newSelected = [tracks[addedIndex].id]; setSelected(newSelected); virtualizer.scrollToIndex(addedIndex); @@ -162,7 +154,7 @@ export default function TracksList(props: Props) { const onKey = useCallback( async (e: KeyboardEvent) => { let firstSelectedTrackID = tracks.findIndex((track) => - selected.includes(track._id), + selected.includes(track.id), ); switch (e.code) { @@ -181,7 +173,7 @@ export default function TracksList(props: Props) { case 'ArrowDown': // This effectively becomes lastSelectedTrackID firstSelectedTrackID = tracks.findIndex( - (track) => selected[selected.length - 1] === track._id, + (track) => selected[selected.length - 1] === track.id, ); e.preventDefault(); onDown(firstSelectedTrackID, tracks, e.shiftKey); @@ -224,7 +216,7 @@ export default function TracksList(props: Props) { const sortSelected = useCallback( (a: string, b: string): number => { - const allTracksIDs = tracks.map((track) => track._id); + const allTracksIDs = tracks.map((track) => track.id); return allTracksIDs.indexOf(a) - allTracksIDs.indexOf(b); }, @@ -255,7 +247,7 @@ export default function TracksList(props: Props) { // Prefer destructuring for (let i = 0; i < tracks.length; i++) { - if (selected.includes(tracks[i]._id)) { + if (selected.includes(tracks[i].id)) { selectedInt.push(i); } } @@ -274,11 +266,11 @@ export default function TracksList(props: Props) { if (index < min) { for (let i = 0; i <= Math.abs(index - base); i++) { - newSelected.push(tracks[base - i]._id); + newSelected.push(tracks[base - i].id); } } else if (index > max) { for (let i = 0; i <= Math.abs(index - base); i++) { - newSelected.push(tracks[base + i]._id); + newSelected.push(tracks[base + i].id); } } @@ -343,147 +335,138 @@ export default function TracksList(props: Props) { */ const showContextMenu = useCallback( (_e: React.MouseEvent, index: number) => { - const selectedCount = selected.length; - const track = tracks[index]; - let shownPlaylists = playlists; - - // Hide current playlist if needed - if (type === 'playlist') { - shownPlaylists = playlists.filter( - (elem) => elem._id !== currentPlaylist, - ); - } - - const playlistTemplate: MenuItemConstructorOptions[] = []; - let addToQueueTemplate: MenuItemConstructorOptions[] = []; - - if (shownPlaylists) { - playlistTemplate.push( - { - label: 'Create new playlist...', - click: async () => { - await PlaylistsAPI.create('New playlist', selected); - }, - }, - { - type: 'separator', - }, - ); - - if (shownPlaylists.length === 0) { - playlistTemplate.push({ - label: 'No playlists', - enabled: false, - }); - } else { - shownPlaylists.forEach((playlist) => { - playlistTemplate.push({ - label: playlist.name, - click: async () => { - await PlaylistsAPI.addTracks(playlist._id, selected); - }, - }); - }); - } - } - - addToQueueTemplate = [ - { - label: 'Add to queue', - click: async () => { - playerAPI.addInQueue(selected); - }, - }, - { - label: 'Play next', - click: async () => { - playerAPI.addNextInQueue(selected); - }, - }, - { - type: 'separator', - }, - ]; - - const template: MenuItemConstructorOptions[] = [ - { - label: - selectedCount > 1 - ? `${selectedCount} tracks selected` - : `${selectedCount} track selected`, - enabled: false, - }, - { - type: 'separator', - }, - ...addToQueueTemplate, - { - label: 'Add to playlist', - submenu: playlistTemplate, - }, - { - type: 'separator', - }, - ]; - - for (const artist of track.artist) { - template.push({ - label: `Search for "${artist}" `, - click: () => { - libraryAPI.search(track.artist[0]); - }, - }); - } - - template.push({ - label: `Search for "${track.album}"`, - click: () => { - libraryAPI.search(track.album); - }, - }); - - if (type === 'playlist' && currentPlaylist) { - template.push( - { - type: 'separator', - }, - { - label: 'Remove from playlist', - click: async () => { - await PlaylistsAPI.removeTracks(currentPlaylist, selected); - }, - }, - ); - } - - template.push( - { - type: 'separator', - }, - { - label: 'Edit track', - click: () => { - navigate(`/details/${track._id}`); - }, - }, - { - type: 'separator', - }, - { - label: 'Show in file manager', - click: () => { - window.MuseeksAPI.library.showTrackInFolder(track); - }, - }, - { - label: 'Remove from library', - click: () => { - libraryAPI.remove(selected); - }, - }, - ); - - menu.showContextMenu(template); + // const selectedCount = selected.length; + // const track = tracks[index]; + // let shownPlaylists = playlists; + // // Hide current playlist if needed + // if (type === "playlist") { + // shownPlaylists = playlists.filter( + // (elem) => elem.id !== currentPlaylist + // ); + // } + // const playlistTemplate: MenuItemConstructorOptions[] = []; + // let addToQueueTemplate: MenuItemConstructorOptions[] = []; + // if (shownPlaylists) { + // playlistTemplate.push( + // { + // label: "Create new playlist...", + // click: async () => { + // await PlaylistsAPI.create("New playlist", selected); + // }, + // }, + // { + // type: "separator", + // } + // ); + // if (shownPlaylists.length === 0) { + // playlistTemplate.push({ + // label: "No playlists", + // enabled: false, + // }); + // } else { + // shownPlaylists.forEach((playlist) => { + // playlistTemplate.push({ + // label: playlist.doc.name, + // click: async () => { + // await PlaylistsAPI.addTracks(playlist.id, selected); + // }, + // }); + // }); + // } + // } + // addToQueueTemplate = [ + // { + // label: "Add to queue", + // click: async () => { + // playerAPI.addInQueue(selected); + // }, + // }, + // { + // label: "Play next", + // click: async () => { + // playerAPI.addNextInQueue(selected); + // }, + // }, + // { + // type: "separator", + // }, + // ]; + // const template: MenuItemConstructorOptions[] = [ + // { + // label: + // selectedCount > 1 + // ? `${selectedCount} tracks selected` + // : `${selectedCount} track selected`, + // enabled: false, + // }, + // { + // type: "separator", + // }, + // ...addToQueueTemplate, + // { + // label: "Add to playlist", + // submenu: playlistTemplate, + // }, + // { + // type: "separator", + // }, + // ]; + // for (const artist of track.doc.artists) { + // template.push({ + // label: `Search for "${artist}" `, + // click: () => { + // libraryAPI.search(artist); + // }, + // }); + // } + // template.push({ + // label: `Search for "${track.doc.album}"`, + // click: () => { + // libraryAPI.search(track.doc.album); + // }, + // }); + // if (type === "playlist" && currentPlaylist) { + // template.push( + // { + // type: "separator", + // }, + // { + // label: "Remove from playlist", + // click: async () => { + // await PlaylistsAPI.removeTracks(currentPlaylist, selected); + // }, + // } + // ); + // } + // template.push( + // { + // type: "separator", + // }, + // { + // label: "Edit track", + // click: () => { + // navigate(`/details/${track.id}`); + // }, + // }, + // { + // type: "separator", + // }, + // { + // label: "Show in file manager", + // click: () => { + // // TODO: + // // window.MuseeksAPI.library.showTrackInFolder(track); + // }, + // }, + // { + // label: "Remove from library", + // click: () => { + // libraryAPI.remove(selected); + // }, + // } + // ); + // TODO: + // menu.showContextMenu(template); }, [ currentPlaylist, @@ -518,18 +501,16 @@ export default function TracksList(props: Props) { return ( { - if (sort && sort.by === sortType) { - if (sort.order === SortOrder.ASC) { +const getIcon = ( + sortBy: SortBy, + sortOrder: SortOrder, + sortByTarget: SortBy, +) => { + if (sortBy === sortByTarget) { + if (sortOrder === 'Asc') { return 'angle-up'; } @@ -23,7 +27,8 @@ type Props = { export default function TracksListHeader(props: Props) { const { enableSort } = props; - const sort = useLibraryStore((state) => state.sort); + const sortBy = useLibraryStore((state) => state.sortBy); + const sortOrder = useLibraryStore((state) => state.sortOrder); return (
@@ -34,32 +39,32 @@ export default function TracksListHeader(props: Props) {
); diff --git a/src/renderer/components/TracksListHeaderCell/TracksListHeaderCell.module.css b/src/components/TracksListHeaderCell/TracksListHeaderCell.module.css similarity index 100% rename from src/renderer/components/TracksListHeaderCell/TracksListHeaderCell.module.css rename to src/components/TracksListHeaderCell/TracksListHeaderCell.module.css diff --git a/src/renderer/components/TracksListHeaderCell/TracksListHeaderCell.tsx b/src/components/TracksListHeaderCell/TracksListHeaderCell.tsx similarity index 95% rename from src/renderer/components/TracksListHeaderCell/TracksListHeaderCell.tsx rename to src/components/TracksListHeaderCell/TracksListHeaderCell.tsx index 3b15f3ed7..ddb79a956 100644 --- a/src/renderer/components/TracksListHeaderCell/TracksListHeaderCell.tsx +++ b/src/components/TracksListHeaderCell/TracksListHeaderCell.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react'; import cx from 'classnames'; import Icon from 'react-fontawesome'; -import { SortBy } from '../../../shared/types/museeks'; +import { SortBy } from '../../generated/typings'; import { useLibraryAPI } from '../../stores/useLibraryStore'; import styles from './TracksListHeaderCell.module.css'; diff --git a/src/renderer/components/VolumeControl/VolumeControl.module.css b/src/components/VolumeControl/VolumeControl.module.css similarity index 97% rename from src/renderer/components/VolumeControl/VolumeControl.module.css rename to src/components/VolumeControl/VolumeControl.module.css index 8725192b1..bb3ea9b5f 100644 --- a/src/renderer/components/VolumeControl/VolumeControl.module.css +++ b/src/components/VolumeControl/VolumeControl.module.css @@ -4,7 +4,6 @@ } .volumeControl { - -webkit-app-region: no-drag; background-color: var(--header-bg); position: absolute; z-index: 10; diff --git a/src/renderer/components/VolumeControl/VolumeControl.tsx b/src/components/VolumeControl/VolumeControl.tsx similarity index 100% rename from src/renderer/components/VolumeControl/VolumeControl.tsx rename to src/components/VolumeControl/VolumeControl.tsx diff --git a/src/constants/sort-orders.ts b/src/constants/sort-orders.ts new file mode 100644 index 000000000..511810d01 --- /dev/null +++ b/src/constants/sort-orders.ts @@ -0,0 +1,115 @@ +import { SortOrder, SortBy, TrackDoc } from '../generated/typings'; +import { Path } from '../types/museeks'; + +// For perforances reasons, otherwise _.orderBy will perform weird check +// the is far more resource/time impactful +const parseArtist = (t: TrackDoc): string => { + return t.doc.artists[0].toString(); +}; +const parseGenre = (t: TrackDoc): string => t.doc.genres[0].toString(); + +type TrackKeys = Path; +type IterateeFunction = (track: TrackDoc) => string; +export type SortTuple = [ + Array, + Array<'asc' | 'desc'>, +]; + +// Declarations +const sortOrders: Record> = { + Artist: { + Asc: [ + // Default + [parseArtist, 'doc.year', 'doc.album', 'doc.disk.no', 'doc.track.no'], + ['asc'], + ], + Dsc: [ + [parseArtist, 'doc.year', 'doc.album', 'doc.disk.no', 'doc.track.no'], + ['desc'], + ], + }, + Title: { + Asc: [ + [ + 'doc.title', + parseArtist, + 'doc.year', + 'doc.album', + 'doc.disk.no', + 'doc.track.no', + ], + ['asc'], + ], + Dsc: [ + [ + 'doc.title', + parseArtist, + 'doc.year', + 'doc.album', + 'doc.disk.no', + 'doc.track.no', + ], + ['desc'], + ], + }, + Duration: { + Asc: [ + [ + 'doc.duration', + parseArtist, + 'doc.year', + 'doc.album', + 'doc.disk.no', + 'doc.track.no', + ], + ['asc'], + ], + Dsc: [ + [ + 'doc.duration', + parseArtist, + 'doc.year', + 'doc.album', + 'doc.disk.no', + 'doc.track.no', + ], + ['desc'], + ], + }, + Album: { + Asc: [ + ['doc.album', parseArtist, 'doc.year', 'doc.disk.no', 'doc.track.no'], + ['asc'], + ], + Dsc: [ + ['doc.album', parseArtist, 'doc.year', 'doc.disk.no', 'doc.track.no'], + ['desc'], + ], + }, + Genre: { + Asc: [ + [ + parseGenre, + parseArtist, + 'doc.year', + 'doc.album', + 'doc.disk.no', + 'doc.track.no', + ], + ['asc'], + ], + Dsc: [ + [ + parseGenre, + parseArtist, + 'doc.year', + 'doc.album', + 'doc.disk.no', + 'doc.track.no', + ], + ['desc'], + ], + }, +}; + +export default sortOrders; diff --git a/src/renderer/elements/Button/Button.module.css b/src/elements/Button/Button.module.css similarity index 90% rename from src/renderer/elements/Button/Button.module.css rename to src/elements/Button/Button.module.css index 9d98ca285..b93fe9175 100644 --- a/src/renderer/elements/Button/Button.module.css +++ b/src/elements/Button/Button.module.css @@ -7,6 +7,10 @@ cursor: pointer; &:active { + opacity: 0.7; + } + + &[disabled] { opacity: 0.5; } } diff --git a/src/renderer/elements/Button/Button.tsx b/src/elements/Button/Button.tsx similarity index 100% rename from src/renderer/elements/Button/Button.tsx rename to src/elements/Button/Button.tsx diff --git a/src/renderer/elements/ExternalLink/ExternalLink.module.css b/src/elements/ExternalLink/ExternalLink.module.css similarity index 100% rename from src/renderer/elements/ExternalLink/ExternalLink.module.css rename to src/elements/ExternalLink/ExternalLink.module.css diff --git a/src/renderer/elements/ExternalLink/ExternalLink.tsx b/src/elements/ExternalLink/ExternalLink.tsx similarity index 85% rename from src/renderer/elements/ExternalLink/ExternalLink.tsx rename to src/elements/ExternalLink/ExternalLink.tsx index 635819c5a..d97cdac59 100644 --- a/src/renderer/elements/ExternalLink/ExternalLink.tsx +++ b/src/elements/ExternalLink/ExternalLink.tsx @@ -1,9 +1,8 @@ import React, { useCallback } from 'react'; +import { open } from '@tauri-apps/plugin-shell'; import styles from './ExternalLink.module.css'; -const { shell } = window.MuseeksAPI; - type Props = { children: string; href: string; @@ -13,7 +12,7 @@ export default function ExternalLink(props: Props) { const openLink = useCallback( (e: React.SyntheticEvent) => { e.preventDefault(); - shell.openExternal(props.href); + open(props.href); }, [props.href], ); diff --git a/src/renderer/elements/Heart/Heart.module.css b/src/elements/Heart/Heart.module.css similarity index 100% rename from src/renderer/elements/Heart/Heart.module.css rename to src/elements/Heart/Heart.module.css diff --git a/src/renderer/elements/Heart/Heart.tsx b/src/elements/Heart/Heart.tsx similarity index 100% rename from src/renderer/elements/Heart/Heart.tsx rename to src/elements/Heart/Heart.tsx diff --git a/src/renderer/elements/Nav/Nav.module.css b/src/elements/Nav/Nav.module.css similarity index 100% rename from src/renderer/elements/Nav/Nav.module.css rename to src/elements/Nav/Nav.module.css diff --git a/src/renderer/elements/Nav/Nav.tsx b/src/elements/Nav/Nav.tsx similarity index 100% rename from src/renderer/elements/Nav/Nav.tsx rename to src/elements/Nav/Nav.tsx diff --git a/src/renderer/elements/Toast/Toast.module.css b/src/elements/Toast/Toast.module.css similarity index 93% rename from src/renderer/elements/Toast/Toast.module.css rename to src/elements/Toast/Toast.module.css index 896491f39..1707a6bdb 100644 --- a/src/renderer/elements/Toast/Toast.module.css +++ b/src/elements/Toast/Toast.module.css @@ -5,6 +5,7 @@ border-top-color: var(--border-color); border-right-color: var(--border-color); border-bottom-color: var(--border-color); + border-radius: var(--border-radius); color: var(--text); box-shadow: 0 5px 3px -5px rgba(0 0 0 0.5); padding: 15px; diff --git a/src/renderer/elements/Toast/Toast.tsx b/src/elements/Toast/Toast.tsx similarity index 100% rename from src/renderer/elements/Toast/Toast.tsx rename to src/elements/Toast/Toast.tsx diff --git a/src/renderer/elements/ViewMessage/ViewMessage.module.css b/src/elements/ViewMessage/ViewMessage.module.css similarity index 100% rename from src/renderer/elements/ViewMessage/ViewMessage.module.css rename to src/elements/ViewMessage/ViewMessage.module.css diff --git a/src/renderer/elements/ViewMessage/ViewMessage.tsx b/src/elements/ViewMessage/ViewMessage.tsx similarity index 100% rename from src/renderer/elements/ViewMessage/ViewMessage.tsx rename to src/elements/ViewMessage/ViewMessage.tsx diff --git a/src/generated/typings/index.ts b/src/generated/typings/index.ts new file mode 100644 index 000000000..a48a09e8f --- /dev/null +++ b/src/generated/typings/index.ts @@ -0,0 +1,22 @@ +/** + * Re-export of types generates by ts-rs + */ +export type { Config } from './Config'; +export type { DefaultView } from './DefaultView'; +export type { Doc } from './Doc'; +export type { NumberOf } from './NumberOf'; +export type { Playlist } from './Playlist'; +export type { Repeat } from './Repeat'; +export type { SortBy } from './SortBy'; +export type { SortOrder } from './SortOrder'; +export type { Track } from './Track'; + +/** + * Custom-aggregated types + */ +import type { Doc } from './Doc'; +import type { Track } from './Track'; +import type { Playlist } from './Playlist'; + +export type TrackDoc = Doc; +export type PlaylistDoc = Doc; diff --git a/src/renderer/hooks/useCurrentViewTracks.ts b/src/hooks/useCurrentViewTracks.ts similarity index 88% rename from src/renderer/hooks/useCurrentViewTracks.ts rename to src/hooks/useCurrentViewTracks.ts index 7828390de..aa97857f5 100644 --- a/src/renderer/hooks/useCurrentViewTracks.ts +++ b/src/hooks/useCurrentViewTracks.ts @@ -1,7 +1,7 @@ import { useRouteLoaderData } from 'react-router-dom'; import { useMemo } from 'react'; -import { TrackModel } from '../../shared/types/museeks'; +import { TrackDoc } from '../generated/typings'; import { RootLoaderData } from '../views/Root'; import { PlaylistLoaderData } from '../views/ViewPlaylistDetails'; @@ -12,7 +12,7 @@ type Maybe = T | undefined; /** * Hook that returns the current view tracks (library or playlist) */ -export default function useCurrentViewTracks(): TrackModel[] { +export default function useCurrentViewTracks(): TrackDoc[] { // TODO: how to support Settings page? Should we? const rootData = useRouteLoaderData('root') as Maybe; const filteredLibraryTracks = useFilteredTracks( diff --git a/src/renderer/hooks/useDebounce.ts b/src/hooks/useDebounce.ts similarity index 100% rename from src/renderer/hooks/useDebounce.ts rename to src/hooks/useDebounce.ts diff --git a/src/hooks/useFilteredTracks.ts b/src/hooks/useFilteredTracks.ts new file mode 100644 index 000000000..061e1ec5d --- /dev/null +++ b/src/hooks/useFilteredTracks.ts @@ -0,0 +1,23 @@ +import { useMemo } from 'react'; + +import { filterTracks, sortTracks } from '../lib/utils-library'; +import SORT_ORDERS from '../constants/sort-orders'; +import useLibraryStore from '../stores/useLibraryStore'; +import { TrackDoc } from '../generated/typings'; +import { stripAccents } from '../lib/utils-id3'; + +export default function useFilteredTracks(tracks: TrackDoc[]): TrackDoc[] { + const search = useLibraryStore((state) => stripAccents(state.search)); + const sortBy = useLibraryStore((state) => state.sortBy); + const sortOrder = useLibraryStore((state) => state.sortOrder); + + // Filter and sort TracksList + // sorting being a costly operation, do it after filtering + const filteredTracks = useMemo( + () => + sortTracks(filterTracks(tracks, search), SORT_ORDERS[sortBy][sortOrder]), + [tracks, search, sortBy, sortOrder], + ); + + return filteredTracks; +} diff --git a/src/renderer/hooks/usePlayingTrack.ts b/src/hooks/usePlayingTrack.ts similarity index 66% rename from src/renderer/hooks/usePlayingTrack.ts rename to src/hooks/usePlayingTrack.ts index 6153b3c01..03fdfd020 100644 --- a/src/renderer/hooks/usePlayingTrack.ts +++ b/src/hooks/usePlayingTrack.ts @@ -1,7 +1,7 @@ -import { TrackModel } from '../../shared/types/museeks'; +import { TrackDoc } from '../generated/typings'; import usePlayerStore from '../stores/usePlayerStore'; -export default function usePlayingTrack(): TrackModel | null { +export default function usePlayingTrack(): TrackDoc | null { return usePlayerStore((state) => { if (state.queue.length > 0 && state.queueCursor !== null) { return state.queue[state.queueCursor]; diff --git a/src/renderer/hooks/usePlayingTrackCurrentTime.ts b/src/hooks/usePlayingTrackCurrentTime.ts similarity index 100% rename from src/renderer/hooks/usePlayingTrackCurrentTime.ts rename to src/hooks/usePlayingTrackCurrentTime.ts diff --git a/src/renderer/hooks/usePlayingTrackID.ts b/src/hooks/usePlayingTrackID.ts similarity index 100% rename from src/renderer/hooks/usePlayingTrackID.ts rename to src/hooks/usePlayingTrackID.ts diff --git a/src/shared/lib/__tests__/ipc-channels.test.ts b/src/lib/__tests__/ipc-channels.test.ts similarity index 100% rename from src/shared/lib/__tests__/ipc-channels.test.ts rename to src/lib/__tests__/ipc-channels.test.ts diff --git a/src/shared/lib/__tests__/themes.test.ts b/src/lib/__tests__/themes.test.ts similarity index 90% rename from src/shared/lib/__tests__/themes.test.ts rename to src/lib/__tests__/themes.test.ts index da0d293b8..653b425e0 100644 --- a/src/shared/lib/__tests__/themes.test.ts +++ b/src/lib/__tests__/themes.test.ts @@ -1,6 +1,8 @@ import { describe, test, expect } from 'vitest'; -import { themes } from '../themes'; +import { themes as themesMap } from '../themes'; + +const themes = Object.values(themesMap); describe('themes', () => { test('themes should have a unique identifier', () => { diff --git a/src/lib/config.ts b/src/lib/config.ts new file mode 100644 index 000000000..57480b33c --- /dev/null +++ b/src/lib/config.ts @@ -0,0 +1,54 @@ +import { invoke } from '@tauri-apps/api/core'; + +import type { Config } from '../generated/typings'; + +import { logCaughtError } from './utils'; + +class ConfigManager { + initialConfig: Config | null = null; + + async init() { + if (this.initialConfig != null) { + throw new Error('Config is already instantiated'); + } + + this.initialConfig = await this.getAll(); + } + + /** + * Get the initial value of the config at the time of instantiation. + * Should only be used when starting the app. + */ + getInitial(key: T): Config[T] { + if (this.initialConfig == null) { + throw new Error('Config has not been instantiated with initial values'); + } + + return this.initialConfig[key]; + } + + async getAll(): Promise { + // TODO: check data shape? + return invoke('plugin:config|get_config'); + } + + async get(key: T): Promise { + const config = await this.getAll(); + return config[key]; + } + + async set(key: T, value: Config[T]): Promise { + const config = await this.getAll(); + config[key] = value; + + try { + invoke('plugin:config|set_config', { config }); + } catch (err) { + logCaughtError(err); + } + + return; + } +} + +export default new ConfigManager(); diff --git a/src/renderer/lib/dnd-types.ts b/src/lib/dnd-types.ts similarity index 100% rename from src/renderer/lib/dnd-types.ts rename to src/lib/dnd-types.ts diff --git a/src/lib/icons.ts b/src/lib/icons.ts new file mode 100644 index 000000000..392129a83 --- /dev/null +++ b/src/lib/icons.ts @@ -0,0 +1,23 @@ +/* eslint-disable import/no-unresolved */ + +import NEXT from '../assets/icons/player-next.svg?raw'; +import PAUSE from '../assets/icons/player-pause.svg?raw'; +import PLAY from '../assets/icons/player-play.svg?raw'; +import PREVIOUS from '../assets/icons/player-previous.svg?raw'; +import QUEUE from '../assets/icons/player-queue.svg?raw'; +import REPEAT_ONE from '../assets/icons/player-repeat-one.svg?raw'; +import REPEAT from '../assets/icons/player-repeat.svg?raw'; +import SHUFFLE from '../assets/icons/player-shuffle.svg?raw'; + +const icons = { + NEXT, + PAUSE, + PLAY, + PREVIOUS, + QUEUE, + REPEAT_ONE, + REPEAT, + SHUFFLE, +}; + +export default icons; diff --git a/src/shared/lib/ipc-channels.ts b/src/lib/ipc-channels.ts similarity index 100% rename from src/shared/lib/ipc-channels.ts rename to src/lib/ipc-channels.ts diff --git a/src/shared/lib/logger.ts b/src/lib/logger.ts similarity index 100% rename from src/shared/lib/logger.ts rename to src/lib/logger.ts diff --git a/src/renderer/lib/player.ts b/src/lib/player.ts similarity index 65% rename from src/renderer/lib/player.ts rename to src/lib/player.ts index 3114973ed..259d9f865 100644 --- a/src/renderer/lib/player.ts +++ b/src/lib/player.ts @@ -1,4 +1,9 @@ -import { TrackModel } from '../../shared/types/museeks'; +import { convertFileSrc } from '@tauri-apps/api/core'; + +import { TrackDoc } from '../generated/typings'; + +import config from './config'; +import { logCaughtError } from './utils'; interface PlayerOptions { playbackRate?: number; @@ -17,9 +22,7 @@ interface PlayerOptions { */ class Player { private audio: HTMLAudioElement; - private durationThresholdReached: boolean; - private track: TrackModel | null; - public threshold: number; + private track: TrackDoc | null; constructor(options?: PlayerOptions) { const mergedOptions = { @@ -36,13 +39,11 @@ class Player { this.audio.defaultPlaybackRate = mergedOptions.playbackRate; // eslint-disable-next-line // @ts-ignore - this.audio.setSinkId(mergedOptions.audioOutputDevice); + // TODO: + // this.audio.setSinkId(mergedOptions.audioOutputDevice); this.audio.playbackRate = mergedOptions.playbackRate; this.audio.volume = mergedOptions.volume; this.audio.muted = mergedOptions.muted; - - this.threshold = 0.75; - this.durationThresholdReached = false; } async play() { @@ -90,21 +91,22 @@ class Player { } async setOutputDevice(deviceID: string) { - // eslint-disable-next-line - // @ts-ignore - await this.audio.setSinkId(deviceID); + try { + // eslint-disable-next-line + // @ts-ignore + await this.audio.setSinkId(deviceID); + } catch (err) { + logCaughtError(err); + } } getTrack() { return this.track; } - setTrack(track: TrackModel) { + setTrack(track: TrackDoc) { this.track = track; - this.audio.src = window.MuseeksAPI.library.parseUri(track.path); - - // When we change song, need to update the thresholdReached indicator. - this.durationThresholdReached = false; + this.audio.src = convertFileSrc(track.doc.path); } setCurrentTime(currentTime: number) { @@ -118,28 +120,16 @@ class Player { isPaused() { return this.audio.paused; } - - isThresholdReached() { - if ( - !this.durationThresholdReached && - this.audio.currentTime >= this.audio.duration * this.threshold - ) { - this.durationThresholdReached = true; - } - - return this.durationThresholdReached; - } } /** * Export a singleton by default, for the sake of simplicity (and we only need * one anyway) */ -const { config } = window.MuseeksAPI; export default new Player({ - volume: config.__initialConfig['audioVolume'], - playbackRate: config.__initialConfig['audioPlaybackRate'], - audioOutputDevice: config.__initialConfig['audioOutputDevice'], - muted: config.__initialConfig['audioMuted'], + volume: config.getInitial('audio_volume'), + playbackRate: config.getInitial('audio_playback_rate'), + audioOutputDevice: config.getInitial('audio_output_device'), + muted: config.getInitial('audio_muted'), }); diff --git a/src/shared/lib/themes.ts b/src/lib/themes.ts similarity index 53% rename from src/shared/lib/themes.ts rename to src/lib/themes.ts index 239bec13a..c2b67e862 100644 --- a/src/shared/lib/themes.ts +++ b/src/lib/themes.ts @@ -1,6 +1,11 @@ -import { Theme } from '../types/museeks'; +import { Theme as ThemeID } from '@tauri-apps/api/window'; + // IMPROVE ME: scan the directory for all json files instead +import { Theme } from '../types/museeks'; import lightTheme from '../themes/light.json'; import darkTheme from '../themes/dark.json'; -export const themes = [lightTheme as Theme, darkTheme as Theme]; +export const themes: Record = { + light: lightTheme as Theme, + dark: darkTheme as Theme, +}; diff --git a/src/renderer/lib/utils-events.ts b/src/lib/utils-events.ts similarity index 81% rename from src/renderer/lib/utils-events.ts rename to src/lib/utils-events.ts index 6bb4a93d6..555152bed 100644 --- a/src/renderer/lib/utils-events.ts +++ b/src/lib/utils-events.ts @@ -23,7 +23,9 @@ export function preventNativeDefault(e: Event) { export function isCtrlKey( e: React.KeyboardEvent | React.MouseEvent | KeyboardEvent, ): boolean { - const isMacOS = window.MuseeksAPI.platform === 'darwin'; + // TODO: + const isMacOS = true; + // const isMacOS = window.MuseeksAPI.platform === 'darwin'; return (isMacOS && e.metaKey) || (!isMacOS && e.ctrlKey); } @@ -31,7 +33,9 @@ export function isCtrlKey( export function isAltKey( e: React.KeyboardEvent | React.MouseEvent | KeyboardEvent, ): boolean { - const isMacOS = window.MuseeksAPI.platform === 'darwin'; + // TODO: + const isMacOS = true; + // const isMacOS = window.MuseeksAPI.platform === 'darwin'; return (isMacOS && e.ctrlKey) || (!isMacOS && e.metaKey); } diff --git a/src/shared/lib/utils-id3.ts b/src/lib/utils-id3.ts similarity index 60% rename from src/shared/lib/utils-id3.ts rename to src/lib/utils-id3.ts index b5dd869ca..d466902d3 100644 --- a/src/shared/lib/utils-id3.ts +++ b/src/lib/utils-id3.ts @@ -1,4 +1,5 @@ -import { Track, TrackEditableFields } from '../types/museeks'; +import { TrackDoc } from '../generated/typings'; +import { TrackSearchableFields } from '../types/museeks'; /** * Strip accent from String. From https://jsperf.com/strip-accents @@ -21,11 +22,9 @@ export const stripAccents = (str: string): string => { /** * Take a track a returns its lowered metadata (used for search) */ -export const getLoweredMeta = ( - metadata: TrackEditableFields, -): Track['loweredMetas'] => ({ - artist: metadata.artist.map((meta) => stripAccents(meta.toLowerCase())), - album: stripAccents(metadata.album.toLowerCase()), - title: stripAccents(metadata.title.toLowerCase()), - genre: metadata.genre.map((meta) => stripAccents(meta.toLowerCase())), +export const getLoweredMeta = (track: TrackDoc): TrackSearchableFields => ({ + artists: track.doc.artists.map((v) => stripAccents(v.toLowerCase())), + album: stripAccents(track.doc.album.toLowerCase()), + title: stripAccents(track.doc.title.toLowerCase()), + genres: track.doc.genres.map((v) => stripAccents(v.toLowerCase())), }); diff --git a/src/lib/utils-library.ts b/src/lib/utils-library.ts new file mode 100644 index 000000000..b68bebacc --- /dev/null +++ b/src/lib/utils-library.ts @@ -0,0 +1,49 @@ +import orderBy from 'lodash/orderBy'; + +import { SortTuple } from '../constants/sort-orders'; +import { TrackDoc } from '../generated/typings'; + +import * as utils from './utils'; +import { stripAccents } from './utils-id3'; + +/** + * Filter an array of tracks by string + */ +export const filterTracks = ( + tracks: TrackDoc[], + search: string, +): TrackDoc[] => { + // Avoid performing useless searches + if (search.length === 0) return tracks; + + // Unoptimized, bad + return tracks.filter( + (track) => + stripAccents(track.doc.artists.toString().toLowerCase()).includes( + search, + ) || + stripAccents(track.doc.album.toLowerCase()).includes(search) || + stripAccents(track.doc.genres.toString().toLowerCase()).includes( + search, + ) || + stripAccents(track.doc.title.toLowerCase()).includes(search), + ); +}; + +/** + * Sort an array of tracks (alias to lodash.orderby) + */ +export const sortTracks = (tracks: TrackDoc[], sort: SortTuple): TrackDoc[] => { + const [columns, order] = sort; + return orderBy(tracks, columns, order); +}; + +/** + * Format a list of tracks to a nice status + */ +export const getStatus = (tracks: TrackDoc[]): string => { + const status = utils.parseDuration( + tracks.map((d) => d.doc.duration).reduce((a, b) => a + b, 0), + ); + return `${tracks.length} tracks, ${status}`; +}; diff --git a/src/renderer/lib/utils-player.ts b/src/lib/utils-player.ts similarity index 85% rename from src/renderer/lib/utils-player.ts rename to src/lib/utils-player.ts index 239438da4..ce36ef421 100644 --- a/src/renderer/lib/utils-player.ts +++ b/src/lib/utils-player.ts @@ -1,13 +1,13 @@ -import { TrackModel } from '../../shared/types/museeks'; +import { TrackDoc } from '../generated/typings'; /** * Shuffle an array with a Player behavior in mind: * the currently-playing track should remain the same, */ export const shuffleTracks = ( - tracks: TrackModel[], + tracks: TrackDoc[], index: number, -): TrackModel[] => { +): TrackDoc[] => { const shuffledTracks = [...tracks]; const currentTrack = shuffledTracks.splice(index, 1)[0]; diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 000000000..d7790cb77 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,35 @@ +import { error } from '@tauri-apps/plugin-log'; + +/** + * Parse an int to a more readable string + */ +export const parseDuration = (duration: number | null): string => { + if (duration !== null) { + const hours = Math.trunc(duration / 3600); + const minutes = Math.trunc(duration / 60) % 60; + const seconds = Math.trunc(duration) % 60; + + const hoursStringified = hours < 10 ? `0${hours}` : hours; + const minutesStringified = minutes < 10 ? `0${minutes}` : minutes; + const secondsStringified = seconds < 10 ? `0${seconds}` : seconds; + + let result = hours > 0 ? `${hoursStringified}:` : ''; + result += `${minutesStringified}:${secondsStringified}`; + + return result; + } + + return '00:00'; +}; + +/** + * Friendly logging for caught errors + * https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript + */ +export const logCaughtError = (err: unknown): void => { + let message; + if (err instanceof Error) message = err.message; + else message = String(err); + // we'll proceed, but let's report it + error(message); +}; diff --git a/src/renderer/entrypoint.tsx b/src/main.tsx similarity index 51% rename from src/renderer/entrypoint.tsx rename to src/main.tsx index e524cbf87..6b52e7788 100644 --- a/src/renderer/entrypoint.tsx +++ b/src/main.tsx @@ -9,8 +9,8 @@ import * as ReactDOM from 'react-dom/client'; import { DndProvider } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { RouterProvider } from 'react-router-dom'; +import { attachConsole } from '@tauri-apps/plugin-log'; -import router from './views/router'; /* |-------------------------------------------------------------------------- | Styles @@ -19,6 +19,7 @@ import router from './views/router'; import 'font-awesome/css/font-awesome.css'; import 'normalize.css/normalize.css'; import './styles/main.module.css'; +import config from './lib/config'; /* |-------------------------------------------------------------------------- @@ -26,15 +27,24 @@ import './styles/main.module.css'; |-------------------------------------------------------------------------- */ -const wrap = document.getElementById('wrap'); +(async function instantiateMuseeks() { + // Async instantiations: we need to execute a couple of async tasks before + // rendering the app + await Promise.allSettled([attachConsole(), config.init()]); -if (wrap) { - const root = ReactDOM.createRoot(wrap); - root.render( - - - - - , - ); -} + // We import the app content asynchronously so synchronous instantiations (like stores) + // can make use of __museeks_initial_config (hacky, I know). + const router = (await import('./views/router')).default; + const wrap = document.getElementById('root'); + + if (wrap) { + const root = ReactDOM.createRoot(wrap); + root.render( + + + + + , + ); + } +})(); diff --git a/src/main/entrypoint.ts b/src/main/entrypoint.ts deleted file mode 100644 index 554d8c998..000000000 --- a/src/main/entrypoint.ts +++ /dev/null @@ -1,129 +0,0 @@ -import path from 'path'; - -import { app, BrowserWindow } from 'electron'; -import remote from '@electron/remote/main'; - -import logger from '../shared/lib/logger'; - -import AppModule from './modules/AppModule'; -import ApplicationMenuModule from './modules/ApplicationMenuModule'; -import ConfigModule from './modules/ConfigModule'; -import PowerModule from './modules/PowerMonitorModule'; -import ThumbarModule from './modules/ThumbarModule'; -import DockMenuModule from './modules/DockMenuDarwinModule'; -import SleepBlockerModule from './modules/SleepBlockerModule'; -import DialogsModule from './modules/DialogsModule'; -import NativeThemeModule from './modules/NativeThemeModule'; -import DevtoolsModule from './modules/DevtoolsModule'; -import WindowPositionModule from './modules/WindowPositionModule'; -import IPCCoverModule from './modules/IPCCoverModule'; -import IPCLibraryModule from './modules/IPCLibraryModule'; -import IPCNotificationsModule from './modules/IPCNotificationsModule'; -import IPCPlaylistsModule from './modules/IPCPlaylistsModule'; -import * as ModulesManager from './lib/modules-manager'; -import { checkBounds } from './lib/utils'; - -const appRoot = path.resolve(__dirname, '..'); // Careful, not future-proof -const rendererDistPath = path.join(appRoot, 'renderer'); -const preloadDistPath = path.join(appRoot, 'preload'); - -// @deprecated Remove all usage of remote in the app -remote.initialize(); - -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the javascript object is GCed. -let mainWindow: Electron.BrowserWindow | null = null; - -// Quit when all windows are closed -app.on('window-all-closed', () => { - app.quit(); -}); - -// This method will be called when Electron has finished its -// initialization and ready to create browser windows. -app.on('ready', async () => { - const configModule = new ConfigModule(); - await ModulesManager.init(configModule); - const config = configModule.getConfig(); - - const bounds = checkBounds(config.get('bounds')); - - // Create the browser window - mainWindow = new BrowserWindow({ - title: 'Museeks', - x: bounds.x, - y: bounds.y, - width: bounds.width, - height: bounds.height, - minWidth: 900, - minHeight: 550, - frame: true, - autoHideMenuBar: true, - titleBarStyle: 'hiddenInset', // MacOS polished window - show: false, - webPreferences: { - // sandbox cannot be removed before we remove all those usage from preload: - // https://www.electronjs.org/docs/latest/tutorial/sandbox#preload-scripts - sandbox: false, - allowRunningInsecureContent: false, - autoplayPolicy: 'no-user-gesture-required', - webSecurity: process.env.ELECTRON_RENDERER_URL == null, // Cannot load local resources without that - preload: path.join(preloadDistPath, 'entrypoint.js'), - }, - }); - - // Open dev tools if museeks runs in debug or development mode - if ( - process.argv.includes('--devtools') || - process.env.NODE_ENV === 'development' || - process.env.VITE_DEV_SERVER_URL - ) { - mainWindow.webContents.openDevTools({ mode: 'detach' }); - } - - mainWindow.on('closed', () => { - // Dereference the window object - mainWindow = null; - }); - - // Prevent webContents from opening new windows (e.g ctrl-click on link) - mainWindow.webContents.setWindowOpenHandler(() => { - return { action: 'deny' }; - }); - - // @deprecated Remove all usage of remote in the app - remote.enable(mainWindow.webContents); - - // ... and load the html page generated by Vite - const viewSuffix = `#/${config.get('defaultView')}`; - - let url: string; - - if (!app.isPackaged && process.env['ELECTRON_RENDERER_URL']) { - url = `${process.env['ELECTRON_RENDERER_URL']}${viewSuffix}`; - } else { - url = `file://${rendererDistPath}/index.html${viewSuffix}`; - } - - logger.info(`Loading main window with "${url}"`); - mainWindow.loadURL(url); - - // Let's list the list of modules we will use for Museeks - ModulesManager.init( - new AppModule(mainWindow, config), - new PowerModule(mainWindow), - new ApplicationMenuModule(mainWindow), - new ThumbarModule(mainWindow), - new DockMenuModule(mainWindow), - new SleepBlockerModule(mainWindow), - new DialogsModule(mainWindow), - new NativeThemeModule(mainWindow, config), - new DevtoolsModule(mainWindow), - new WindowPositionModule(mainWindow, config), - // Modules used to handle IPC APIs - new IPCCoverModule(mainWindow), - new IPCLibraryModule(mainWindow), - new IPCNotificationsModule(mainWindow, config), - new IPCPlaylistsModule(mainWindow), - ).catch(logger.error); -}); diff --git a/src/main/lib/modules-manager.ts b/src/main/lib/modules-manager.ts deleted file mode 100644 index 8d0499e47..000000000 --- a/src/main/lib/modules-manager.ts +++ /dev/null @@ -1,16 +0,0 @@ -import logger from '../../shared/lib/logger'; -import Module from '../modules/BaseModule'; - -export const init = async (...modules: Module[]): Promise => { - await Promise.allSettled( - modules.map((module) => - module.init().catch((err) => { - throw err; - }), - ), - ).catch((err) => { - logger.error( - `An error occured when loading ${module.constructor.name} could not be loaded:\n${err}`, - ); - }); -}; diff --git a/src/main/lib/utils-cover.ts b/src/main/lib/utils-cover.ts deleted file mode 100644 index 6c84a2489..000000000 --- a/src/main/lib/utils-cover.ts +++ /dev/null @@ -1,88 +0,0 @@ -import path from 'path'; -import fs from 'fs'; - -import * as mmd from 'music-metadata'; -import { globby } from 'globby'; - -import logger from '../../shared/lib/logger'; - -const SUPPORTED_COVER_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.bmp', '.gif']; -const SUPPORTED_COVER_NAMES = ['album', 'albumart', 'folder', 'cover', 'front']; - -/** - * Parse data to be used by img/background-image with base64 - */ -export const parseBase64 = (format: string, data: string): string => { - return `data:${format};base64,${data}`; -}; - -/** - * Determine if a file is a valid cover or not - */ -const isValidFilename = (pathname: path.ParsedPath): boolean => { - const isExtensionValid = SUPPORTED_COVER_EXTENSIONS.includes( - pathname.ext.toLowerCase(), - ); - const isNameValid = SUPPORTED_COVER_NAMES.some((name) => { - return pathname.name.toLowerCase().includes(name); - }); - - return isExtensionValid && isNameValid; -}; - -/** - * Smart fetch cover (from id3 or file directory) - */ -export const fetchCover = async ( - trackPath: string, - ignoreID3 = false, - base64 = false, -): Promise => { - if (!trackPath) { - return null; - } - - if (!ignoreID3) { - const data = await mmd.parseFile(trackPath); - const picture = data.common.picture && data.common.picture[0]; - - if (picture) { - // If cover in id3 - return parseBase64(picture.format, picture.data.toString('base64')); - } - } - - // scan folder for any cover image - const folder = path.dirname(trackPath); - const pattern = `${folder.replace(/\\/g, '/')}/*`; - - const matches = await globby(pattern, { followSymbolicLinks: false }); - - const match = matches.find((elem) => { - return isValidFilename(path.parse(elem)); - }); - - if (match) { - if (base64) return getFileAsBase64(match); - - return `file://${match}`; - } - - return null; -}; - -/** - * Returns the given file as a base64 string - */ -export const getFileAsBase64 = async ( - filePath: string, -): Promise => { - try { - const content = fs.readFileSync(filePath, { encoding: 'base64' }); - return parseBase64(path.extname(filePath).substr(1), content); - } catch (err) { - logger.warn('Could not get cover as base64:'); - logger.warn(err); - return null; - } -}; diff --git a/src/main/lib/utils-m3u.ts b/src/main/lib/utils-m3u.ts deleted file mode 100644 index 3681f2a6f..000000000 --- a/src/main/lib/utils-m3u.ts +++ /dev/null @@ -1,55 +0,0 @@ -import path from 'path'; -import fs from 'fs'; - -import chardet from 'chardet'; -import iconv from 'iconv-lite'; - -import logger from '../../shared/lib/logger'; - -const isFile = (path: string) => fs.lstatSync(path).isFile(); - -/** - * Analyze a .m3u file and returns the resolved path of each song from it - */ -export const parse = (filePath: string): string[] => { - try { - const baseDir = path.parse(filePath).dir; - const content = fs.readFileSync(filePath); - const encoding = chardet.detect(content); - - if (typeof encoding !== 'string') { - throw new Error(`could not guess the file encoding (${filePath})`); - } - - const decodedContent = iconv.decode(content, encoding); - - const files = decodedContent.split(/\r?\n/).reduce((acc, line) => { - if (line.length === 0) { - return acc; - } - - // If absolute path - if (fs.existsSync(path.resolve(line)) && isFile(path.resolve(line))) { - acc.push(path.resolve(line)); - return acc; - } - - // If relative Path - if ( - fs.existsSync(path.resolve(baseDir, line)) && - isFile(path.resolve(baseDir, line)) - ) { - acc.push(path.resolve(baseDir, line)); - return acc; - } - - return acc; - }, [] as string[]); - - return files; - } catch (err) { - logger.warn(err); - } - - return []; -}; diff --git a/src/main/lib/utils.ts b/src/main/lib/utils.ts deleted file mode 100644 index 1b1237d31..000000000 --- a/src/main/lib/utils.ts +++ /dev/null @@ -1,42 +0,0 @@ -import electron from 'electron'; - -import { ConfigBounds } from '../../shared/types/museeks'; - -const DEFAULT_WIDTH = 900; -const DEFAULT_HEIGHT = 550; - -export const checkBounds = function ( - bounds: ConfigBounds | undefined, -): ConfigBounds { - if (bounds === undefined) { - bounds = { - x: 0, - y: 0, - height: 0, - width: 0, - }; - } - - // check if the browser window is offscreen - const display = electron.screen.getDisplayNearestPoint({ - x: Math.round(bounds.x), - y: Math.round(bounds.y), - }).workArea; - - const onScreen = - bounds.x >= display.x && - bounds.x + bounds.width <= display.x + display.width && - bounds.y >= display.y && - bounds.y + bounds.height <= display.y + display.height; - - if (!onScreen) { - return { - width: DEFAULT_WIDTH, - height: DEFAULT_HEIGHT, - x: display.width / 2 - DEFAULT_WIDTH / 2, - y: display.height / 2 - DEFAULT_HEIGHT / 2, - }; - } - - return bounds; -}; diff --git a/src/main/modules/AppModule.ts b/src/main/modules/AppModule.ts deleted file mode 100644 index 2b93bfdc3..000000000 --- a/src/main/modules/AppModule.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Module in charge of handling the different window behavior based on platforms - */ - -import os from 'os'; - -import type Store from 'electron-store'; -import { ipcMain, app } from 'electron'; - -import logger from '../../shared/lib/logger'; -import channels from '../../shared/lib/ipc-channels'; -import { Config } from '../../shared/types/museeks'; - -import ModuleWindow from './BaseWindowModule'; - -export default class AppModule extends ModuleWindow { - protected config: Store; - protected forceQuit: boolean; - - constructor(window: Electron.BrowserWindow, config: Store) { - super(window); - - this.config = config; - this.forceQuit = false; - } - - async load(): Promise { - // Make the app a single-instance app - this.ensureSingleInstance(); - - // Shows app only once it is loaded (avoid initial white flash) - ipcMain.once(channels.APP_READY, () => { - if (this.window) { - this.window.show(); - } - }); - - // Restart the app with the same arguments - ipcMain.on(channels.APP_RESTART, () => { - app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) }); - app.exit(0); - }); - - // Prevent the window to be closed, hide it instead (to continue audio playback) - this.window.on('close', (e) => { - this.close(e); - }); - - ipcMain.on(channels.APP_CLOSE, (e) => { - this.close(e); - }); - - // Click on the dock icon to show the app again on macOS - app.on('activate', () => { - if (this.window) { - this.window.show(); - this.window.focus(); - } - }); - - // Small hack to check on MacOS if the dock close action has been clicked - // https://stackoverflow.com/questions/35008347/electron-close-w-x-vs-right-click-dock-and-quit#35782702 - app.on('before-quit', () => { - this.forceQuit = true; - }); - } - - close(e: Electron.Event): void { - if (this.forceQuit || os.platform() !== 'darwin') { - app.quit(); - this.window.destroy(); - } else { - e.preventDefault(); - this.window.hide(); - } - } - - ensureSingleInstance(): void { - const gotTheLock = app.requestSingleInstanceLock(); - - app.on('second-instance', () => { - // Someone tried to run a second instance, we should focus our window. - if (this.window) { - if (this.window.isMinimized()) this.window.restore(); - this.window.focus(); - } - }); - - if (!gotTheLock) { - logger.info( - 'Another instance of Museeks is already running, quitting...', - ); - app.quit(); - } - } -} diff --git a/src/main/modules/ApplicationMenuModule.ts b/src/main/modules/ApplicationMenuModule.ts deleted file mode 100644 index 12a22eb94..000000000 --- a/src/main/modules/ApplicationMenuModule.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Module in charge of the app menu - * Litteraly stolen from: https://electronjs.org/docs/api/menu#examples - */ - -import { Menu, shell } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; - -import ModuleWindow from './BaseWindowModule'; - -export default class ApplicationMenuModule extends ModuleWindow { - async load(): Promise { - const template: Electron.MenuItemConstructorOptions[] = [ - { role: 'appMenu' }, - { role: 'fileMenu' }, - { role: 'editMenu' }, - { - label: 'View', - submenu: [ - { - label: 'Jump to playing track', - accelerator: 'CmdOrCtrl+T', - click: () => { - this.window.webContents.send(channels.MENU_JUMP_TO_PLAYING_TRACK); - }, - }, - { type: 'separator' }, - { - label: 'Go to library', - accelerator: 'CmdOrCtrl+L', - click: () => { - this.window.webContents.send(channels.MENU_GO_TO_LIBRARY); - }, - }, - { - label: 'Go to playlists', - accelerator: 'CmdOrCtrl+P', - click: () => { - this.window.webContents.send(channels.MENU_GO_TO_PLAYLISTS); - }, - }, - { type: 'separator' }, - { role: 'reload' }, - { role: 'forceReload' }, - { role: 'toggleDevTools' }, - { type: 'separator' }, - { role: 'resetZoom' }, - { role: 'zoomIn' }, - { role: 'zoomOut' }, - { type: 'separator' }, - { role: 'togglefullscreen' }, - ], - }, - { role: 'windowMenu' }, - { - role: 'help', - submenu: [ - { - label: 'Learn More', - click: async () => { - await shell.openExternal('https://museeks.io'); - }, - }, - ], - }, - ]; - - const menu = Menu.buildFromTemplate(template); - Menu.setApplicationMenu(menu); - } -} diff --git a/src/main/modules/BaseModule.ts b/src/main/modules/BaseModule.ts deleted file mode 100644 index 20b76a9e5..000000000 --- a/src/main/modules/BaseModule.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Example of Module, other modules should extent this class - */ - -import os from 'os'; - -import logger from '../../shared/lib/logger'; - -export default class Module { - protected loaded: boolean; - protected platforms: NodeJS.Platform[]; - - constructor() { - this.loaded = false; - this.platforms = ['win32', 'linux', 'darwin']; - } - - // To not be overriden - async init(): Promise { - if (this.loaded) - throw new TypeError(`Module ${this.constructor.name} is already loaded`); - - if (this.platforms.includes(os.platform())) { - await this.load().catch((err) => { - throw err; - }); - this.loaded = true; - logger.info(`Loaded ${this.constructor.name}`); - } else { - logger.info( - `Skipping load of ${ - this.constructor.name - } (supported platform: ${this.platforms.join(', ')})`, - ); - } - } - - // Can (now) be an asynchronous method - async load(): Promise { - throw new TypeError( - `Module ${this.constructor.name} should have a load() method`, - ); - // Do whatever you want here :) - } -} diff --git a/src/main/modules/BaseWindowModule.ts b/src/main/modules/BaseWindowModule.ts deleted file mode 100644 index f28c42694..000000000 --- a/src/main/modules/BaseWindowModule.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Example of Module, other modules should extent this class - */ - -import Module from './BaseModule'; - -export default class ModuleWindow extends Module { - protected window: Electron.BrowserWindow; - - constructor(window: Electron.BrowserWindow) { - super(); - this.window = window; - } - - getWindow(): Electron.BrowserWindow { - return this.window; - } -} diff --git a/src/main/modules/ConfigModule.ts b/src/main/modules/ConfigModule.ts deleted file mode 100644 index 7b639a214..000000000 --- a/src/main/modules/ConfigModule.ts +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Essential module for creating/loading the app config - */ - -import electron, { app, ipcMain } from 'electron'; -import Store from 'electron-store'; - -import { Config, Repeat, SortBy, SortOrder } from '../../shared/types/museeks'; -import channels from '../../shared/lib/ipc-channels'; -import logger from '../../shared/lib/logger'; - -import Module from './BaseModule'; - -export default class ConfigModule extends Module { - private workArea: Electron.Rectangle; - private config: Store; - - constructor() { - super(); - - logger.info(`Using "${app.getPath('userData')}" as config path`); - - this.workArea = electron.screen.getPrimaryDisplay().workArea; - this.config = new Store({ - name: 'config', - defaults: this.getDefaultConfig(), - }); - - // A few manual migrations, electron-store migratons don't seem to play well - // new setting - if (this.config.get('tracksDensity') == undefined) { - logger.info('Config: setting "tracksDensity" option'); - this.config.set('tracksDensity', 'normal'); - } - - logger.child; - - // dark-legacy is gone - if (this.config.get('theme') === 'dark-legacy') { - logger.info('Config: disabling dark-legacy theme'); - this.config.set('theme', 'dark'); - } - } - - async load(): Promise { - ipcMain.on(channels.CONFIG_GET_ALL, (event) => { - event.returnValue = this.config.store; - }); - - ipcMain.handle(channels.CONFIG_GET_ALL, (): Config => this.config.store); - - ipcMain.handle( - channels.CONFIG_GET, - (_e: Electron.Event, key: T): Config[T] => { - logger.debug('Config get', key); - return this.config.get(key); - }, - ); - - ipcMain.handle( - channels.CONFIG_SET, - ( - _e: Electron.Event, - key: T, - value: Config[T], - ): void => { - logger.debug('Config set', key, value); - this.config.set(key, value); - }, - ); - } - - getConfig(): Store { - const config = this.config; - - if (config === undefined) { - throw new Error('Config is not defined, has it been loaded?'); - } - - return config; - } - - getDefaultConfig(): Config { - const config: Config = { - theme: '__system', - audioVolume: 1, - audioPlaybackRate: 1, - audioOutputDevice: 'default', - audioMuted: false, - audioShuffle: false, - audioRepeat: Repeat.NONE, - tracksDensity: 'normal', - defaultView: 'library', - librarySort: { - by: SortBy.ARTIST, - order: SortOrder.ASC, - }, - // musicFolders: [], - sleepBlocker: false, - autoUpdateChecker: true, - displayNotifications: true, - bounds: { - width: 1000, - height: 600, - x: Math.round(this.workArea.width / 2), - y: Math.round(this.workArea.height / 2), - }, - }; - - return config; - } -} diff --git a/src/main/modules/DevtoolsModule.ts b/src/main/modules/DevtoolsModule.ts deleted file mode 100644 index 88da558a6..000000000 --- a/src/main/modules/DevtoolsModule.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Module in charge of loading devtools extensions to ensure a good developer - * experience - */ - -import installExtensions, { - REDUX_DEVTOOLS, - REACT_DEVELOPER_TOOLS, -} from 'electron-devtools-assembler'; - -import logger from '../../shared/lib/logger'; - -import ModuleWindow from './BaseWindowModule'; - -export default class DevtoolsModule extends ModuleWindow { - async load(): Promise { - const isProduction = process.env.NODE_ENV === 'production'; - - // Let's install some extensions so it's easier for us to debug things - if (!isProduction) { - try { - await installExtensions([REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS], { - loadExtensionOptions: { - allowFileAccess: true, - }, - }); - logger.info('Added devtools extensions'); - } catch (err) { - logger.warn('An error occurred while trying to add extensions:\n', err); - } - } - } -} diff --git a/src/main/modules/DialogsModule.ts b/src/main/modules/DialogsModule.ts deleted file mode 100644 index 0e654dd3d..000000000 --- a/src/main/modules/DialogsModule.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Module in charge of receiving dialogs requests from the renderer process - * and returning data if needed - */ - -import { dialog, ipcMain } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; - -import ModuleWindow from './BaseWindowModule'; - -export default class DialogsModule extends ModuleWindow { - async load(): Promise { - /** - * showMessageBox - */ - ipcMain.handle( - channels.DIALOG_MESSAGE_BOX, - async (_event, options: Electron.MessageBoxOptions) => { - const result = await dialog.showMessageBox(this.window, options); - - return result; - }, - ); - - /** - * showOpenDialog - */ - ipcMain.handle( - channels.DIALOG_OPEN, - async (_event, options: Electron.OpenDialogOptions) => { - const result = await dialog.showOpenDialog(options); - - return result; - }, - ); - } -} diff --git a/src/main/modules/DockMenuDarwinModule.ts b/src/main/modules/DockMenuDarwinModule.ts deleted file mode 100644 index 61277f5f3..000000000 --- a/src/main/modules/DockMenuDarwinModule.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Module in charge of the dock menu on macOS - */ - -import { IpcMainEvent, Menu, app, ipcMain } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; -import { PlayerStatus, TrackModel } from '../../shared/types/museeks'; - -import ModuleWindow from './BaseWindowModule'; - -export default class DockMenuDarwinModule extends ModuleWindow { - protected menu: Electron.MenuItemConstructorOptions[]; - protected songDetails: Electron.MenuItemConstructorOptions[]; - protected playToggle: Electron.MenuItemConstructorOptions[]; - protected pauseToggle: Electron.MenuItemConstructorOptions[]; - - constructor(window: Electron.BrowserWindow) { - super(window); - this.platforms = ['darwin']; - - this.menu = []; - this.songDetails = []; - this.playToggle = []; - this.pauseToggle = []; - } - - async load(): Promise { - this.songDetails = [ - { - label: 'Not playing', - enabled: false, - }, - { - type: 'separator', - }, - ]; - - this.playToggle = [ - { - label: 'Play', - click: () => { - this.window.webContents.send(channels.PLAYBACK_PLAY); - }, - }, - ]; - - this.pauseToggle = [ - { - label: 'Pause', - click: () => { - this.window.webContents.send(channels.PLAYBACK_PAUSE); - }, - }, - ]; - - this.menu = [ - { - label: 'Previous', - click: () => { - this.window.webContents.send(channels.PLAYBACK_PREVIOUS); - }, - }, - { - label: 'Next', - click: () => { - this.window.webContents.send(channels.PLAYBACK_NEXT); - }, - }, - ]; - - // Load events listener for player actions - ipcMain.on(channels.PLAYBACK_PLAY, () => { - this.setDockMenu(PlayerStatus.PLAY); - }); - - ipcMain.on(channels.PLAYBACK_PAUSE, () => { - this.setDockMenu(PlayerStatus.PAUSE); - }); - - ipcMain.on( - channels.PLAYBACK_TRACK_CHANGE, - (_e: IpcMainEvent, track: TrackModel) => { - this.updateDockMenu(track); - this.setDockMenu(PlayerStatus.PLAY); - }, - ); - - this.setDockMenu(PlayerStatus.PAUSE); - } - - setDockMenu(state: PlayerStatus): void { - const playPauseItem = state === 'play' ? this.pauseToggle : this.playToggle; - const menuTemplate = [...this.songDetails, ...playPauseItem, ...this.menu]; - app.dock.setMenu(Menu.buildFromTemplate(menuTemplate)); - } - - updateDockMenu(metadata: TrackModel): void { - this.songDetails = [ - { - label: `${metadata.title}`, - enabled: false, - }, - { - label: `by ${metadata.artist}`, - enabled: false, - }, - { - label: `on ${metadata.album}`, - enabled: false, - }, - { - type: 'separator', - }, - ]; - } -} diff --git a/src/main/modules/IPCCoverModule.ts b/src/main/modules/IPCCoverModule.ts deleted file mode 100644 index f49bc8507..000000000 --- a/src/main/modules/IPCCoverModule.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ipcMain } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; -import { fetchCover } from '../lib/utils-cover'; - -import ModuleWindow from './BaseWindowModule'; - -/** - * Module in charge of returning the cover data for a given track - */ -export default class IPCCoverModule extends ModuleWindow { - async load(): Promise { - ipcMain.handle( - channels.COVER_GET, - (_e, path: string): Promise => { - return fetchCover(path, false, true); - }, - ); - } -} diff --git a/src/main/modules/IPCLibraryModule.ts b/src/main/modules/IPCLibraryModule.ts deleted file mode 100644 index 301be4329..000000000 --- a/src/main/modules/IPCLibraryModule.ts +++ /dev/null @@ -1,331 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -import { BrowserWindow, ipcMain, IpcMainInvokeEvent } from 'electron'; -import { globby } from 'globby'; -import * as mmd from 'music-metadata'; -import queue from 'queue'; -import pickBy from 'lodash/pickBy'; - -import * as m3u from '../lib/utils-m3u'; -import channels from '../../shared/lib/ipc-channels'; -import { Track } from '../../shared/types/museeks'; -import logger, { loggerExtras } from '../../shared/lib/logger'; -import { getLoweredMeta } from '../../shared/lib/utils-id3'; - -import ModuleWindow from './BaseWindowModule'; - -interface ScanFile { - path: string; - stat: fs.Stats; -} - -/* -|-------------------------------------------------------------------------- -| supported Formats -|-------------------------------------------------------------------------- -*/ - -const SUPPORTED_TRACKS_EXTENSIONS = [ - // MP3 / MP4 - '.mp3', - '.mp4', - '.aac', - '.m4a', - '.3gp', - '.wav', - // Opus - '.ogg', - '.ogv', - '.ogm', - '.opus', - // Flac - '.flac', - // web media - '.webm', -]; - -const SUPPORTED_PLAYLISTS_EXTENSIONS = ['.m3u']; - -/** - * Module in charge of renderer <> main processes communication regarding - * library management, covers, playlists etc. - */ -class IPCLibraryModule extends ModuleWindow { - public import: { - processed: number; - total: number; - }; - - constructor(window: BrowserWindow) { - super(window); - - this.import = { - processed: 0, - total: 0, - }; - } - - async load(): Promise { - ipcMain.handle( - channels.LIBRARY_IMPORT_TRACKS, - this.importTracks.bind(this), - ); - ipcMain.handle(channels.LIBRARY_LOOKUP, this.libraryLookup.bind(this)); - ipcMain.handle(channels.PLAYLISTS_RESOLVE_M3U, this.resolveM3u.bind(this)); - } - - // --------------------------------------------------------------------------- - // IPC Events - // --------------------------------------------------------------------------- - - /** - * Scan the file system and return all music files and playlists that may be - * safely imported to Museeks. - */ - private async libraryLookup( - _e: IpcMainInvokeEvent, - pathsToScan: string[], - ): Promise<[string[], string[]]> { - logger.info('Starting tracks lookup', pathsToScan); - loggerExtras.time('Library lookup'); - - // 1. Get the stats for all the files/paths - const paths = await Promise.all(pathsToScan.map(this.getStats)); - - // 2. Split directories and files - const files: string[] = []; - const folders: string[] = []; - - paths.forEach((elem) => { - if (elem.stat.isFile()) files.push(elem.path); - if (elem.stat.isDirectory() || elem.stat.isSymbolicLink()) - folders.push(elem.path); - }); - - // 3. Scan all the directories with globby - const globbies = folders.map((folder) => { - // Normalize slashes and escape regex special characters - const pattern = `${folder - .replace(/\\/g, '/') - // I'm not sure about this eslint-ignore - // eslint-disable-next-line no-useless-escape - .replace(/([$^*+?()\[\]])/g, '\\$1')}/**/*.*`; - - return globby(pattern, { followSymbolicLinks: true }); - }); - - const subDirectoriesFiles = await Promise.all(globbies); - // Scan folders and add files to library - - // 4. Merge all path arrays together and filter them with the extensions we support - const allFiles = subDirectoriesFiles - .reduce((acc, array) => acc.concat(array), [] as string[]) - .concat(files); // Add the initial files - - const supportedTrackFiles = allFiles.filter((filePath) => { - const extension = path.extname(filePath).toLowerCase(); - return SUPPORTED_TRACKS_EXTENSIONS.includes(extension); - }); - - const supportedPlaylistsFiles = allFiles.filter((filePath) => { - const extension = path.extname(filePath).toLowerCase(); - return SUPPORTED_PLAYLISTS_EXTENSIONS.includes(extension); - }); - - loggerExtras.timeEnd('Library lookup'); - - return [supportedTrackFiles, supportedPlaylistsFiles]; - } - - private async resolveM3u( - _e: IpcMainInvokeEvent, - path: string, - ): Promise { - return m3u.parse(path); - } - - /** - * Now: returns the id3 tags of all the given tracks path - * Tomorrow: do DB insertion here - */ - async importTracks( - _e: IpcMainInvokeEvent, - tracksPath: string[], - ): Promise { - logger.info(`Starting import of ${tracksPath.length} tracks`); - loggerExtras.time('Tracks scan'); - - return new Promise((resolve, reject) => { - if (tracksPath.length === 0) return; - - try { - // Instantiate queue - const scannedFiles: Track[] = []; - - const scanQueue = new queue(); - scanQueue.concurrency = 32; - scanQueue.autostart = false; - - scanQueue.addEventListener('end', () => { - this.import.processed = 0; - this.import.total = 0; - - loggerExtras.timeEnd('Tracks scan'); - resolve(scannedFiles); - }); - // End queue instantiation - - this.import.total += tracksPath.length; - - // Add all the items to the queue - tracksPath.forEach((filePath, index) => { - scanQueue.push(async (callback) => { - try { - // Normalize (back)slashes on Windows - filePath = path.resolve(filePath); - - // Check if there is an existing record in the DB - // const existingDoc = await db.tracks.findOnlyByPath(filePath); - - // If there is existing document - // if (!existingDoc) { - // Get metadata - const track = await this.getMetadata(filePath); - // const insertedDoc = await db.tracks.insert(track); - scannedFiles.push(track); - // } - - this.import.processed++; - } catch (err) { - logger.warn(err); - } finally { - if (index % 50 == 0) { - logger.debug(`Finished scanning ${index} tracks`); - } - } - - if (callback) callback(); - }); - }); - - scanQueue.start(); - } catch (err) { - reject(err); - } - }); - } - - // --------------------------------------------------------------------------- - // Helpers - // --------------------------------------------------------------------------- - - private getDefaultMetadata(): Track { - return { - album: 'Unknown', - artist: ['Unknown artist'], - disk: { - no: 0, - of: 0, - }, - duration: 0, - genre: [], - loweredMetas: { - artist: ['unknown artist'], - album: 'unknown', - title: '', - genre: [], - }, - path: '', - playCount: 0, - title: '', - track: { - no: 0, - of: 0, - }, - year: null, - }; - } - - private parseMusicMetadata( - data: mmd.IAudioMetadata, - trackPath: string, - ): Partial { - const { common, format } = data; - - const metadata = { - album: common.album, - artist: - common.artists || - (common.artist && [common.artist]) || - (common.albumartist && [common.albumartist]), - disk: common.disk, - duration: format.duration, - genre: common.genre, - title: common.title || path.parse(trackPath).base, - track: common.track, - year: common.year, - }; - - return pickBy(metadata); - } - - /** - * Get a file ID3 metadata - */ - private async getMetadata(trackPath: string): Promise { - const defaultMetadata = this.getDefaultMetadata(); - - const basicMetadata: Track = { - ...defaultMetadata, - path: trackPath, - }; - - try { - const data = await mmd.parseFile(trackPath, { - skipCovers: true, - duration: true, - }); - - // Let's try to define something with what we got so far... - const parsedData = this.parseMusicMetadata(data, trackPath); - - const metadata: Track = { - ...defaultMetadata, - ...parsedData, - path: trackPath, - }; - - metadata.loweredMetas = getLoweredMeta(metadata); - - // Let's try another wat to retrieve a track duration - // if (metadata.duration < 0.5) { - // try { - // metadata.duration = await getAudioDuration(trackPath); - // } catch (err) { - // logger.warn(`An error occured while getting ${trackPath} duration: ${err}`); - // } - // } - - return metadata; - } catch (err) { - logger.warn( - `An error occured while reading ${trackPath} id3 tags: ${err}`, - ); - } - - return basicMetadata; - } - - /** - * Get stats of a file while keeping the path itself - */ - private async getStats(folderPath: string): Promise { - return { - path: folderPath, - stat: await fs.promises.stat(folderPath), - }; - } -} - -export default IPCLibraryModule; diff --git a/src/main/modules/IPCNotificationsModule.ts b/src/main/modules/IPCNotificationsModule.ts deleted file mode 100644 index 9e9933bb1..000000000 --- a/src/main/modules/IPCNotificationsModule.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type Store from 'electron-store'; -import { - ipcMain, - IpcMainEvent, - NativeImage, - nativeImage, - Notification, -} from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; -import { Config, TrackModel } from '../../shared/types/museeks'; -import * as utilsCover from '../lib/utils-cover'; - -import ModuleWindow from './BaseWindowModule'; - -/** - * Module in charge of displaying native notifications on certain user actions - */ -export default class IPCNotificationsModule extends ModuleWindow { - protected config: Store; - - constructor(window: Electron.BrowserWindow, config: Store) { - super(window); - this.config = config; - } - - async load(): Promise { - ipcMain.on( - channels.PLAYBACK_PLAY, - (_e: IpcMainEvent, track: TrackModel) => { - this.sendPlaybackNotification(track); - }, - ); - } - - private async sendPlaybackNotification(track: TrackModel): Promise { - if ( - this.window.isFocused() || - this.config.get('displayNotifications') === false - ) { - return; - } - - const cover = await utilsCover.fetchCover(track.path); - - let icon: NativeImage | undefined = undefined; - - if (cover !== null) { - icon = nativeImage.createFromDataURL(cover); - } - - const notification = new Notification({ - title: track.title, - body: `${track.artist}\n${track.album}`, - icon, - silent: true, - }); - - notification.on('click', () => { - this.window.show(); - this.window.focus(); - }); - - notification.show(); - } -} diff --git a/src/main/modules/IPCPlaylistsModule.ts b/src/main/modules/IPCPlaylistsModule.ts deleted file mode 100644 index e40335631..000000000 --- a/src/main/modules/IPCPlaylistsModule.ts +++ /dev/null @@ -1,70 +0,0 @@ -import path from 'path'; - -import { app, dialog, ipcMain } from 'electron'; -import * as m3u from 'm3ujs'; - -import channels from '../../shared/lib/ipc-channels'; -import logger from '../../shared/lib/logger'; - -import ModuleWindow from './BaseWindowModule'; - -/** - * Module in charge of returning the cover data for a given track - */ -class IPCPlaylistsModule extends ModuleWindow { - async load(): Promise { - ipcMain.on( - channels.PLAYLIST_EXPORT, - async (_e, name: string, tracksPath: string[]) => { - const { filePath } = await dialog.showSaveDialog(this.window, { - title: 'Export playlist', - defaultPath: path.resolve(app.getPath('music'), name), - filters: [ - { - extensions: ['m3u'], - name: 'random', - }, - ], - }); - - if (filePath) { - try { - const playlistExport = new m3u.Playlist( - new m3u.TypeEXTM3U((entry) => { - if (entry instanceof m3u.Mp3Entry) { - return `${entry.artist} - ${entry.album} - ${entry.track} - ${entry.title}`; - } - return entry.displayName; - }), - ); - - tracksPath.forEach((trackPath) => { - try { - playlistExport.add(new m3u.Mp3Entry(trackPath)); - } catch (err) { - logger.warn(err); - } - }); - - playlistExport.write(filePath); - } catch (err: unknown) { - logger.warn(err); - if (err instanceof Error) { - dialog.showErrorBox( - `An error occured when exporting the playlist "${name}"`, - err.message, - ); - } else { - dialog.showErrorBox( - `An error occured when exporting the playlist "${name}"`, - 'Unknown error', - ); - } - } - } - }, - ); - } -} - -export default IPCPlaylistsModule; diff --git a/src/main/modules/NativeThemeModule.ts b/src/main/modules/NativeThemeModule.ts deleted file mode 100644 index 98953be42..000000000 --- a/src/main/modules/NativeThemeModule.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Module in charge of telling the renderer process which native theme is used - */ - -import type Store from 'electron-store'; -import { ipcMain, nativeTheme } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; -import { themes } from '../../shared/lib/themes'; -import { Config, Theme } from '../../shared/types/museeks'; - -import ModuleWindow from './BaseWindowModule'; - -export default class NativeThemeModule extends ModuleWindow { - protected config: Store; - - constructor(window: Electron.BrowserWindow, config: Store) { - super(window); - this.config = config; - - const theme = this.getTheme(); - nativeTheme.themeSource = theme.themeSource; - } - - async load(): Promise { - /** - * Update the UI when someone changes the global theme settings - */ - nativeTheme.on('updated', () => { - if (this.getThemeID() === '__system') { - this.applyTheme(this.getSystemThemeID()); - } - - // Otherwise, we don't care - }); - - ipcMain.handle(channels.THEME_GET_ID, () => this.getThemeID()); - - /** - * Handle themeSource update and returns the theme variables for a given - * themeID - */ - ipcMain.handle( - channels.THEME_SET_ID, - (_event, themeID: Config['theme']) => { - this.setThemeID(themeID); - }, - ); - - ipcMain.handle(channels.THEME_GET, () => { - let themeID = this.getThemeID(); - - if (themeID === '__system') themeID = this.getSystemThemeID(); - - const theme = themes.find((theme) => theme._id === themeID); - - if (!theme) throw new RangeError(`No theme found with ID ${themeID}`); - - return theme; - }); - } - - getThemeID(): Config['theme'] { - return this.config.get('theme') ?? '__system'; - } - - getTheme(): Theme { - let themeID = this.getThemeID(); - - if (themeID === '__system') { - themeID = this.getSystemThemeID(); - } - - const theme = themes.find((theme) => theme._id === themeID); - - if (!theme) throw new RangeError(`No theme found with ID ${themeID}`); - - return theme; - } - - setThemeID(themeID: Config['theme']): void { - this.config.set('theme', themeID); - - if (themeID === '__system') { - nativeTheme.themeSource = 'system'; - this.applyTheme(this.getSystemThemeID()); - } else { - const theme = themes.find((theme) => theme._id === themeID); - - if (!theme) throw new RangeError(`No theme found with ID ${themeID}`); - - nativeTheme.themeSource = theme.themeSource; - this.applyTheme(theme._id); - } - } - - applyTheme(themeID: Theme['_id']): void { - const theme = themes.find((theme) => theme._id === themeID); - - if (!theme) throw new RangeError(`No theme found with ID ${themeID}`); - - this.window.webContents.send(channels.THEME_APPLY, theme); - } - - getSystemThemeID(): Theme['_id'] { - return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; - } -} diff --git a/src/main/modules/PowerMonitorModule.ts b/src/main/modules/PowerMonitorModule.ts deleted file mode 100644 index ad9fc5a0f..000000000 --- a/src/main/modules/PowerMonitorModule.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Module in charge of pausing the player when going into sleep - */ - -import electron from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; - -import ModuleWindow from './BaseWindowModule'; - -export default class PowerMonitorModule extends ModuleWindow { - async load(): Promise { - const { powerMonitor } = electron; - const { window } = this; - - powerMonitor.on('suspend', () => { - window.webContents.send(channels.PLAYBACK_PAUSE); - }); - } -} diff --git a/src/main/modules/SleepBlockerModule.ts b/src/main/modules/SleepBlockerModule.ts deleted file mode 100644 index 788820195..000000000 --- a/src/main/modules/SleepBlockerModule.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Module in charge of preventing the computer to go to sleep - */ - -import { powerSaveBlocker, ipcMain, IpcMainEvent } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; - -import ModuleWindow from './BaseWindowModule'; - -export default class SleepBlocker extends ModuleWindow { - protected sleepBlockerID: number; - protected enabled: boolean; - - constructor(window: Electron.BrowserWindow) { - super(window); - - this.enabled = false; - this.sleepBlockerID = 0; - this.platforms = ['win32', 'darwin', 'linux']; - } - - onStartPlayback = (): void => { - if (this.enabled && !powerSaveBlocker.isStarted(this.sleepBlockerID)) { - // or 'prevent-display-sleep' - this.sleepBlockerID = powerSaveBlocker.start('prevent-app-suspension'); - } - }; - - onStopPlayback = (): void => { - if (powerSaveBlocker.isStarted(this.sleepBlockerID)) { - powerSaveBlocker.stop(this.sleepBlockerID); - } - }; - - toggleSleepBlocker = (_event: IpcMainEvent, value: boolean): void => { - this.enabled = value; - }; - - async load(): Promise { - ipcMain.on(channels.SETTINGS_TOGGLE_SLEEP_BLOCKER, this.toggleSleepBlocker); - ipcMain.on(channels.PLAYBACK_PLAY, this.onStartPlayback); - ipcMain.on(channels.PLAYBACK_PAUSE, this.onStopPlayback); - ipcMain.on(channels.PLAYBACK_STOP, this.onStopPlayback); - } -} diff --git a/src/main/modules/ThumbarModule.ts b/src/main/modules/ThumbarModule.ts deleted file mode 100644 index 6ee6182cb..000000000 --- a/src/main/modules/ThumbarModule.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Module in charge of the Windows Thumbar - * (buttons appearing on Museeks preview when hovering the icon) - * - * Windows only - */ - -import path from 'path'; - -import { nativeImage, ipcMain } from 'electron'; - -import channels from '../../shared/lib/ipc-channels'; - -import ModuleWindow from './BaseWindowModule'; - -const { createFromPath } = nativeImage; - -const iconsDirectory = path.resolve( - path.join(__dirname, '../../src/shared/assets/icons/windows'), -); - -export default class ThumbarModule extends ModuleWindow { - constructor(window: Electron.BrowserWindow) { - super(window); - this.platforms = ['win32']; - } - - async load(): Promise { - const { window } = this; - - const icons = { - play: createFromPath(path.join(iconsDirectory, 'play.ico')), - // playDisabled: createFromPath(path.join(iconsDirectory, 'play-disabled.ico')), - pause: createFromPath(path.join(iconsDirectory, 'pause.ico')), - pauseDisabled: createFromPath( - path.join(iconsDirectory, 'pause-disabled.ico'), - ), - prev: createFromPath(path.join(iconsDirectory, 'backward.ico')), - prevDisabled: createFromPath( - path.join(iconsDirectory, 'backward-disabled.ico'), - ), - next: createFromPath(path.join(iconsDirectory, 'forward.ico')), - nextDisabled: createFromPath( - path.join(iconsDirectory, 'forward-disabled.ico'), - ), - }; - - const thumbarButtons = { - play: { - tooltip: 'Play', - icon: icons.play, - click: () => { - window.webContents.send(channels.PLAYBACK_PLAY); - }, - }, - /* playDisabled: { - tooltip: 'Play', - flags: ['disabled'], - icon: icons.playDisabled, - click: () => { return null; } // Electron's TypeScript definition issue - }, */ - pause: { - tooltip: 'Pause', - icon: icons.pause, - click: () => { - window.webContents.send(channels.PLAYBACK_PAUSE); - }, - }, - pauseDisabled: { - tooltip: 'Pause', - flags: ['disabled'], - icon: icons.pauseDisabled, - click: () => { - return null; - }, // Electron's TypeScript definition issue - }, - prev: { - tooltip: 'Prev', - icon: icons.prev, - click: () => { - window.webContents.send(channels.PLAYBACK_PREVIOUS); - }, - }, - prevDisabled: { - tooltip: 'Prev', - flags: ['disabled'], - icon: icons.prevDisabled, - click: () => { - return null; - }, // Electron's TypeScript definition issue - }, - next: { - tooltip: 'Next', - icon: icons.next, - click: () => { - window.webContents.send(channels.PLAYBACK_NEXT); - }, - }, - nextDisabled: { - tooltip: 'Next', - flags: ['disabled'], - icon: icons.nextDisabled, - click: () => { - return null; - }, // Electron's TypeScript definition issue - }, - }; - - ipcMain.once(channels.APP_READY, () => { - window.setThumbarButtons([ - thumbarButtons.prevDisabled, - thumbarButtons.play, - thumbarButtons.nextDisabled, - ]); - }); - - ipcMain.on(channels.PLAYBACK_PLAY, () => { - window.setThumbarButtons([ - thumbarButtons.prev, - thumbarButtons.pause, - thumbarButtons.next, - ]); - }); - - ipcMain.on(channels.PLAYBACK_PAUSE, () => { - window.setThumbarButtons([ - thumbarButtons.prev, - thumbarButtons.play, - thumbarButtons.next, - ]); - }); - - ipcMain.on(channels.PLAYBACK_STOP, () => { - window.setThumbarButtons([ - thumbarButtons.prevDisabled, - thumbarButtons.play, - thumbarButtons.nextDisabled, - ]); - }); - } -} diff --git a/src/main/modules/WindowPositionModule.ts b/src/main/modules/WindowPositionModule.ts deleted file mode 100644 index a7db693cc..000000000 --- a/src/main/modules/WindowPositionModule.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Module in charge of remembering the window position, width and height - */ - -import type Store from 'electron-store'; -import debounce from 'lodash/debounce'; - -import { Config } from '../../shared/types/museeks'; - -import ModuleWindow from './BaseWindowModule'; - -export default class WindowPositionModule extends ModuleWindow { - protected config: Store; - - constructor(window: Electron.BrowserWindow, config: Store) { - super(window); - this.config = config; - } - - async load(): Promise { - this.window.on('resize', debounce(this.saveBounds, 250).bind(this)); - this.window.on('move', debounce(this.saveBounds, 250).bind(this)); - } - - saveBounds() { - const bounds = this.window.getBounds(); - this.config.set('bounds', bounds); - } -} diff --git a/src/main/tsconfig.json b/src/main/tsconfig.json deleted file mode 100644 index 61fe9549c..000000000 --- a/src/main/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": ["ESNext"] - }, - "include": ["../main", "../shared"], - // This imports preloads, which will fail as it requires DOM lib - "exclude": ["../shared/types/declarations/preload.d.ts"] -} diff --git a/src/preload/db.ts b/src/preload/db.ts deleted file mode 100644 index 1bba894a4..000000000 --- a/src/preload/db.ts +++ /dev/null @@ -1,217 +0,0 @@ -import path from 'path'; - -import { app } from '@electron/remote'; -import PouchDB from 'pouchdb'; -import PouchDBFind from 'pouchdb-find'; - -import { - Playlist, - PlaylistModel, - Track, - TrackModel, -} from '../shared/types/museeks'; - -/** - * This will ultimately move to the main process. Here for legacy purpose until - * we find a more suitable database as linvodb is not supported anymore. - */ -const pathUserData = app.getPath('userData'); - -PouchDB.plugin(PouchDBFind); - -const Tracks = new PouchDB(path.join(pathUserData, 'TracksDB'), { - adapter: 'leveldb', - auto_compaction: true, -}); - -Tracks.createIndex({ - index: { - fields: ['path'], - }, -}); - -const Playlists = new PouchDB( - path.join(pathUserData, 'PlaylistsDB'), - { - adapter: 'leveldb', - auto_compaction: true, - }, -); - -Playlists.createIndex({ - index: { - fields: ['importPath'], - }, -}); - -/** ---------------------------------------------------------------------------- - * Shared helpers - * -------------------------------------------------------------------------- */ - -async function reset(): Promise { - // We cannot use destroy() as it literally destroys it, when we just want to - // empty it - const [allTracks, allPlaylists] = await Promise.all([ - tracks.getAll(), - playlists.getAll(), - ]); - - const deletedTracks = allTracks.map((track) => ({ - ...track, - _deleted: true, - })); - - const deletedPlaylists = allPlaylists.map((playlist) => ({ - ...playlist, - _deleted: true, - })); - - await Promise.all([ - Tracks.bulkDocs(deletedTracks), - Playlists.bulkDocs(deletedPlaylists), - ]); -} - -/** ---------------------------------------------------------------------------- - * Tracks - * -------------------------------------------------------------------------- */ - -const tracks = { - async getAll(): Promise { - // Use custom IDs instead? - const [firstResponse, secondResponse] = await Promise.all([ - Tracks.allDocs({ include_docs: true, endkey: '_design' }), - Tracks.allDocs({ include_docs: true, startkey: '_design\uffff' }), - ]); - - const tracks = [...firstResponse.rows, ...secondResponse.rows] - .map((record) => record.doc) - .filter(Boolean); - - return tracks; - }, - - async insertMultiple(tracks: Track[]) { - return Tracks.bulkDocs(tracks); - }, - - async update(track: TrackModel) { - return Tracks.put(track); - }, - - async incrementPlayCount(track: TrackModel) { - const doc = await Tracks.get(track._id); - await Tracks.put({ - ...doc, - playCount: doc.playCount + 1, - }); - }, - - async remove(trackIDs: string[]): Promise { - const response = await Tracks.find({ - selector: { _id: { $in: trackIDs } }, - }); - const tracks = response.docs; - const deletedTracks = tracks.map((track) => ({ - ...track, - _deleted: true, - })); - - await Tracks.bulkDocs(deletedTracks); - }, - - async findByID(trackIDs: string[]): Promise { - const response = await Tracks.find({ - selector: { _id: { $in: trackIDs } }, - }); - return response.docs; - }, - - async findOnlyByID(trackID: string): Promise { - return Tracks.get(trackID); - }, - - async findByPath(paths: string[]): Promise { - const response = await Tracks.find({ selector: { path: { $in: paths } } }); - return response.docs; - }, - - async findOnlyByPath(path: string): Promise { - const response = await Tracks.find({ selector: { path } }); - const [track] = response.docs; - return track; - }, -}; - -/** ---------------------------------------------------------------------------- - * Playlists helpers - * -------------------------------------------------------------------------- */ - -const playlists = { - async getAll(): Promise { - // Use custom IDs instead? - const [firstResponse, secondResponse] = await Promise.all([ - Playlists.allDocs({ include_docs: true, endkey: '_design' }), - Playlists.allDocs({ include_docs: true, startkey: '_design\uffff' }), - ]); - - const playlists = [...firstResponse.rows, ...secondResponse.rows] - .map((record) => record.doc) - .filter(Boolean); - - return playlists; - }, - - async insert(playlist: Playlist) { - return Playlists.post(playlist); - }, - - async rename(playlistID: string, name: string) { - const playlist = await Playlists.get(playlistID); - Playlists.put({ - ...playlist, - name, - }); - }, - - async remove(playlistID: string) { - const playlist = await Playlists.get(playlistID); - - await Playlists.put({ - ...playlist, - _deleted: true, - }); - }, - - async findByID(playlistIDs: string[]): Promise { - const response = await Playlists.find({ - selector: { _id: { $in: playlistIDs } }, - }); - return response.docs; - }, - - async findOnlyByID(playlistID: string): Promise { - return Playlists.get(playlistID); - }, - - async setTracks(playlistID: string, tracksIDs: string[]) { - const playlist = await Playlists.get(playlistID); - - await Playlists.put({ - ...playlist, - tracks: tracksIDs, - }); - }, -}; - -/** ---------------------------------------------------------------------------- - * Export all the way! - * -------------------------------------------------------------------------- */ - -const db = { - reset, - tracks, - playlists, -}; - -export default db; diff --git a/src/preload/entrypoint.ts b/src/preload/entrypoint.ts deleted file mode 100644 index 2cb765b05..000000000 --- a/src/preload/entrypoint.ts +++ /dev/null @@ -1,150 +0,0 @@ -import os from 'os'; -import path from 'path'; - -import '@total-typescript/ts-reset'; -import { Menu, app } from '@electron/remote'; -import { IpcRendererEvent, contextBridge, ipcRenderer, shell } from 'electron'; - -import { Config, Track } from '../shared/types/museeks'; -import channels from '../shared/lib/ipc-channels'; -import { parseUri } from '../shared/lib/utils-uri'; - -import db from './db'; - -/** - * Ok, so what is there exactly? - * - * Preload is still very new to Museeks as I'm trying to upgrade Museeks to - * Electron's best practices, but basically: - * - * - We were using XActions in the past as an "API" - * - I am moving parts of those APIs here - * - Those APIs should at some point get rid of Node.js to enable contextbridge - * - Some of those APIs implementations may need to go to the main process instead - * - There's many things to refactor, some things will look weird as they are - * in an in-between state. - */ - -/* -|-------------------------------------------------------------------------- -| File association - make it work one day -|-------------------------------------------------------------------------- -*/ - -// TODO: only working on macOS, issue to follow: -// https://github.com/electron/electron/issues/27116 -// app.on('open-file', (event, path) => { -// event.preventDefault(); -// logger.info(path); // absolute path to file -// }); - -/* -|-------------------------------------------------------------------------- -| Config API: the config lives in the main process and we communicate with -| it via IPC -|-------------------------------------------------------------------------- -*/ - -const config = { - __initialConfig: ipcRenderer.sendSync(channels.CONFIG_GET_ALL), - getAll(): Promise { - return ipcRenderer.invoke(channels.CONFIG_GET_ALL); - }, - get(key: T): Promise { - return ipcRenderer.invoke(channels.CONFIG_GET, key); - }, - set(key: T, value: Config[T]): Promise { - return ipcRenderer.invoke(channels.CONFIG_SET, key, value); - }, -}; - -/* -|-------------------------------------------------------------------------- -| Window object extension -| TODO: some of these should go to the main process and be converted to use -| contextBridge.exposeToMainWorld + sandboxed renderer -|-------------------------------------------------------------------------- -*/ - -const ElectronAPI = { - ipcRenderer: { - // FIXME unsafe - // All these usage should probably go to the main process, or we should - // expose explicit APIs for what those usages are trying to solve - on: ( - channel: string, - listener: (event: IpcRendererEvent, value: unknown) => void, - ) => { - const listenerCount = ipcRenderer.listenerCount(channel); - if (listenerCount === 0) { - ipcRenderer.on(channel, listener); - } else { - // eslint-disable-next-line no-console - console.warn( - `Event "${channel}" already has ${listenerCount} listeners, aborting.`, - ); - } - }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - off: ( - channel: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _listener: (event: IpcRendererEvent, value: unknown) => void, - ) => { - // Because we function cannot be passed between preload / renderer, - // ipcRenderer.off does not work. Until we fix the FIXME unsafe above - ipcRenderer.removeAllListeners(channel); - // ipcRenderer.off(channel, listener); - }, - send: ipcRenderer.send, - sendSync: ipcRenderer.sendSync, - invoke: ipcRenderer.invoke, - }, - menu: { - showContextMenu: (template: Electron.MenuItemConstructorOptions[]) => { - const context = Menu.buildFromTemplate(template); - context.popup({}); - }, - }, -}; - -// When editing something here, please update museeks.d.ts to extend the -// window.MuseeksAPI global object. -const MuseeksAPI = { - platform: os.platform(), - version: app.getVersion(), - config, - app: { - ready: () => ipcRenderer.send(channels.APP_READY), - restart: () => ipcRenderer.send(channels.APP_RESTART), - clone: () => ipcRenderer.send(channels.APP_CLOSE), - }, - db, - library: { - showTrackInFolder: (track: Track) => shell.showItemInFolder(track.path), - parseUri, - }, - playlists: { - resolveM3u: async (path: string): Promise => - await ipcRenderer.invoke(channels.PLAYLISTS_RESOLVE_M3U, path), - }, - covers: { - getCoverAsBase64: (track: Track) => - ipcRenderer.invoke(channels.COVER_GET, track.path), - }, - // TODO: all of the things below should be removed - path: { - parse: path.parse, - resolve: path.resolve, - }, - shell: { - openExternal: shell.openExternal, - openUserDataDirectory: () => shell.openPath(app.getPath('userData')), - }, -}; - -contextBridge.exposeInMainWorld('ElectronAPI', ElectronAPI); -contextBridge.exposeInMainWorld('MuseeksAPI', MuseeksAPI); - -export type ElectronAPI = typeof ElectronAPI; -export type MuseeksAPI = typeof MuseeksAPI; diff --git a/src/preload/tsconfig.json b/src/preload/tsconfig.json deleted file mode 100644 index 32d3e25d0..000000000 --- a/src/preload/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": ["ESNext", "DOM"] - }, - "include": ["../preload", "../shared"] -} diff --git a/src/renderer/components/DropzoneImport/DropzoneImport.tsx b/src/renderer/components/DropzoneImport/DropzoneImport.tsx deleted file mode 100644 index 9e5ba2292..000000000 --- a/src/renderer/components/DropzoneImport/DropzoneImport.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import styles from './DropzoneImport.module.css'; - -type Props = { - title: string; - subtitle: string; - shown: boolean; -}; - -export default function DropzoneImport(props: Props) { - return ( -
-
{props.title}
-
{props.subtitle}
-
- ); -} diff --git a/src/renderer/constants/sort-orders.ts b/src/renderer/constants/sort-orders.ts deleted file mode 100644 index dab1ee881..000000000 --- a/src/renderer/constants/sort-orders.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Track, SortOrder, SortBy, Path } from '../../shared/types/museeks'; - -// For perforances reasons, otherwise _.orderBy will perform weird check -// the is far more resource/time impactful -const parseArtist = (t: Track): string => t.loweredMetas.artist[0].toString(); -const parseGenre = (t: Track): string => t.loweredMetas.genre.toString(); - -type TrackKeys = Path; -type IterateeFunction = (track: Track) => string; -export type SortTuple = [ - Array, - Array<'asc' | 'desc'>, -]; - -// Declarations -const sortOrders: Record> = { - [SortBy.ARTIST]: { - [SortOrder.ASC]: [ - // Default - [parseArtist, 'year', 'loweredMetas.album', 'disk.no', 'track.no'], - ['asc'], - ], - [SortOrder.DSC]: [ - [parseArtist, 'year', 'loweredMetas.album', 'disk.no', 'track.no'], - ['desc'], - ], - }, - [SortBy.TITLE]: { - [SortOrder.ASC]: [ - [ - 'loweredMetas.title', - parseArtist, - 'year', - 'loweredMetas.album', - 'disk.no', - 'track.no', - ], - ['asc'], - ], - [SortOrder.DSC]: [ - [ - 'loweredMetas.title', - parseArtist, - 'year', - 'loweredMetas.album', - 'disk.no', - 'track.no', - ], - ['desc'], - ], - }, - [SortBy.DURATION]: { - [SortOrder.ASC]: [ - [ - 'duration', - parseArtist, - 'year', - 'loweredMetas.album', - 'disk.no', - 'track.no', - ], - ['asc'], - ], - [SortOrder.DSC]: [ - [ - 'duration', - parseArtist, - 'year', - 'loweredMetas.album', - 'disk.no', - 'track.no', - ], - ['desc'], - ], - }, - [SortBy.ALBUM]: { - [SortOrder.ASC]: [ - ['loweredMetas.album', parseArtist, 'year', 'disk.no', 'track.no'], - ['asc'], - ], - [SortOrder.DSC]: [ - ['loweredMetas.album', parseArtist, 'year', 'disk.no', 'track.no'], - ['desc'], - ], - }, - [SortBy.GENRE]: { - [SortOrder.ASC]: [ - [ - parseGenre, - parseArtist, - 'year', - 'loweredMetas.album', - 'disk.no', - 'track.no', - ], - ['asc'], - ], - [SortOrder.DSC]: [ - [ - parseGenre, - parseArtist, - 'year', - 'loweredMetas.album', - 'disk.no', - 'track.no', - ], - ['desc'], - ], - }, -}; - -export default sortOrders; diff --git a/src/renderer/hooks/useFilteredTracks.ts b/src/renderer/hooks/useFilteredTracks.ts deleted file mode 100644 index 2d62360c5..000000000 --- a/src/renderer/hooks/useFilteredTracks.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useMemo } from 'react'; - -import { TrackModel } from '../../shared/types/museeks'; -import { filterTracks, sortTracks } from '../lib/utils-library'; -import SORT_ORDERS from '../constants/sort-orders'; -import useLibraryStore from '../stores/useLibraryStore'; - -export default function useFilteredTracks(tracks: TrackModel[]): TrackModel[] { - const search = useLibraryStore((state) => state.search); - const sort = useLibraryStore((state) => state.sort); - - // Filter and sort TracksList - // sorting being a costly operation, do it after filtering - const filteredTracks = useMemo( - () => - sortTracks( - filterTracks(tracks, search), - SORT_ORDERS[sort.by][sort.order], - ), - [tracks, search, sort], - ); - - return filteredTracks; -} diff --git a/src/renderer/index.html b/src/renderer/index.html deleted file mode 100644 index 988682f2b..000000000 --- a/src/renderer/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - Museeks - - - - -
- -
- - diff --git a/src/renderer/lib/icons.ts b/src/renderer/lib/icons.ts deleted file mode 100644 index 718b1c3c5..000000000 --- a/src/renderer/lib/icons.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable import/no-unresolved */ - -import NEXT from '../../shared/assets/icons/player-next.svg?raw'; -import PAUSE from '../../shared/assets/icons/player-pause.svg?raw'; -import PLAY from '../../shared/assets/icons/player-play.svg?raw'; -import PREVIOUS from '../../shared/assets/icons/player-previous.svg?raw'; -import QUEUE from '../../shared/assets/icons/player-queue.svg?raw'; -import REPEAT_ONE from '../../shared/assets/icons/player-repeat-one.svg?raw'; -import REPEAT from '../../shared/assets/icons/player-repeat.svg?raw'; -import SHUFFLE from '../../shared/assets/icons/player-shuffle.svg?raw'; - -const icons = { - NEXT, - PAUSE, - PLAY, - PREVIOUS, - QUEUE, - REPEAT_ONE, - REPEAT, - SHUFFLE, -}; - -export default icons; diff --git a/src/renderer/lib/utils-library.ts b/src/renderer/lib/utils-library.ts deleted file mode 100644 index 01922587a..000000000 --- a/src/renderer/lib/utils-library.ts +++ /dev/null @@ -1,46 +0,0 @@ -import orderBy from 'lodash/orderBy'; - -import { TrackModel } from '../../shared/types/museeks'; -import { SortTuple } from '../constants/sort-orders'; - -import * as utils from './utils'; - -/** - * Filter an array of tracks by string - */ -export const filterTracks = ( - tracks: TrackModel[], - search: string, -): TrackModel[] => { - // Avoid performing useless searches - if (search.length === 0) return tracks; - - return tracks.filter( - (track) => - track.loweredMetas.artist.toString().includes(search) || - track.loweredMetas.album.includes(search) || - track.loweredMetas.genre.toString().includes(search) || - track.loweredMetas.title.includes(search), - ); -}; - -/** - * Sort an array of tracks (alias to lodash.orderby) - */ -export const sortTracks = ( - tracks: TrackModel[], - sort: SortTuple, -): TrackModel[] => { - const [columns, order] = sort; - return orderBy(tracks, columns, order); -}; - -/** - * Format a list of tracks to a nice status - */ -export const getStatus = (tracks: TrackModel[]): string => { - const status = utils.parseDuration( - tracks.map((d) => d.duration).reduce((a, b) => a + b, 0), - ); - return `${tracks.length} tracks, ${status}`; -}; diff --git a/src/renderer/lib/utils.ts b/src/renderer/lib/utils.ts deleted file mode 100644 index 9d506caef..000000000 --- a/src/renderer/lib/utils.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Parse an int to a more readable string - */ -export const parseDuration = (duration: number | null): string => { - if (duration !== null) { - const hours = Math.trunc(duration / 3600); - const minutes = Math.trunc(duration / 60) % 60; - const seconds = Math.trunc(duration) % 60; - - const hoursStringified = hours < 10 ? `0${hours}` : hours; - const minutesStringified = minutes < 10 ? `0${minutes}` : minutes; - const secondsStringified = seconds < 10 ? `0${seconds}` : seconds; - - let result = hours > 0 ? `${hoursStringified}:` : ''; - result += `${minutesStringified}:${secondsStringified}`; - - return result; - } - - return '00:00'; -}; - -/** - * Remove duplicates (realpath) and useless children folders - */ -export const removeUselessFolders = (folders: string[]): string[] => { - // Remove duplicates - let filteredFolders = folders.filter( - (elem, index) => folders.indexOf(elem) === index, - ); - - const foldersToBeRemoved: string[] = []; - - filteredFolders.forEach((folder, i) => { - filteredFolders.forEach((subfolder, j) => { - if ( - subfolder.includes(folder) && - i !== j && - !foldersToBeRemoved.includes(folder) - ) { - foldersToBeRemoved.push(subfolder); - } - }); - }); - - filteredFolders = filteredFolders.filter( - (elem) => !foldersToBeRemoved.includes(elem), - ); - - return filteredFolders; -}; - -// export const getAudioDuration = (trackPath: string): Promise => { -// const audio = new Audio(); - -// return new Promise((resolve, reject) => { -// audio.addEventListener('loadedmetadata', () => { -// resolve(audio.duration); -// }); - -// audio.addEventListener('error', (e) => { -// // eslint-disable-next-line -// // @ts-ignore error event typing is wrong -// const message = `Error getting audio duration: (${e.currentTarget.error.code}) ${trackPath}`; -// reject(new Error(message)); -// }); - -// audio.preload = 'metadata'; -// // HACK no idea what other caracters could fuck things up -// audio.src = encodeURI(trackPath).replace('#', '%23'); -// }); -// }; diff --git a/src/renderer/stores/AppAPI.ts b/src/renderer/stores/AppAPI.ts deleted file mode 100644 index f18f89858..000000000 --- a/src/renderer/stores/AppAPI.ts +++ /dev/null @@ -1,15 +0,0 @@ -import SettingsAPI from './SettingsAPI'; - -const init = async (): Promise => { - await SettingsAPI.check(); - - // Tell the main process to show the window - window.MuseeksAPI.app.ready(); -}; - -// Should we use something else to harmonize between zustand and non-store APIs? -const AppAPI = { - init, -}; - -export default AppAPI; diff --git a/src/renderer/stores/useLibraryStore.ts b/src/renderer/stores/useLibraryStore.ts deleted file mode 100644 index 76675f0bd..000000000 --- a/src/renderer/stores/useLibraryStore.ts +++ /dev/null @@ -1,345 +0,0 @@ -import chunk from 'lodash/chunk'; -import type { MessageBoxReturnValue } from 'electron'; - -import { - LibrarySort, - SortBy, - SortOrder, - Track, - TrackEditableFields, - TrackModel, -} from '../../shared/types/museeks'; -import logger from '../../shared/lib/logger'; -import router from '../views/router'; -import channels from '../../shared/lib/ipc-channels'; -import { getLoweredMeta, stripAccents } from '../../shared/lib/utils-id3'; - -import PlaylistsAPI from './PlaylistsAPI'; -import { createStore } from './store-helpers'; -import useToastsStore from './useToastsStore'; -import usePlayerStore from './usePlayerStore'; - -const { path, db, config } = window.MuseeksAPI; -const { ipcRenderer } = window.ElectronAPI; - -type LibraryState = { - search: string; - sort: LibrarySort; - refreshing: boolean; - refresh: { - processed: number; - total: number; - }; - highlightPlayingTrack: boolean; - api: { - search: (value: string) => void; - sort: (sortBy: SortBy) => void; - scanPlaylists: (paths: string[]) => Promise; - add: (pathsToScan: string[]) => Promise; - remove: (tracksIDs: string[]) => Promise; - reset: () => Promise; - incrementPlayCount: (track: TrackModel) => Promise; - updateTrackMetadata: ( - trackID: string, - newFields: TrackEditableFields, - ) => Promise; - highlightPlayingTrack: (highlight: boolean) => void; - }; -}; - -const useLibraryStore = createStore((set, get) => ({ - search: '', - sort: config.__initialConfig['librarySort'], - refreshing: false, - refresh: { - processed: 0, - total: 0, - }, - highlightPlayingTrack: false, // hacky, fixme - - api: { - /** - * Filter tracks by search - */ - search: (search): void => { - set({ search: stripAccents(search) }); - }, - - /** - * Filter tracks by sort query - */ - sort: async (sortBy): Promise => { - const prevSort = get().sort; - - let sort: LibrarySort | null = null; - - // If same sort by, just reverse the order - if (sortBy === prevSort.by) { - sort = { - ...prevSort, - order: - prevSort.order === SortOrder.ASC ? SortOrder.DSC : SortOrder.ASC, - }; - } - // If it's different, then we assume the user needs ASC order by default - else { - sort = { - by: sortBy, - order: SortOrder.ASC, - }; - } - - await config.set('librarySort', sort); - - set({ sort }); - }, - - /** - * Scan a directory for playlists and import them - * TODO: probaly move to "main"? - */ - scanPlaylists: async (paths) => { - await Promise.all( - paths.map(async (filePath) => { - try { - const playlistFiles = - await window.MuseeksAPI.playlists.resolveM3u(filePath); - const playlistName = path.parse(filePath).name; - - const existingTracks: TrackModel[] = - await db.tracks.findByPath(playlistFiles); - - await PlaylistsAPI.create( - playlistName, - existingTracks.map((track) => track._id), - filePath, - ); - } catch (err) { - logger.warn(err); - } - }), - ); - }, - - /** - * Add tracks to Library - */ - add: async (pathsToScan): Promise => { - set({ refreshing: true }); - - try { - // Get all valid track paths - // TODO move this whole function to main process - const [supportedTrackFiles, supportedPlaylistsFiles] = - await ipcRenderer.invoke(channels.LIBRARY_LOOKUP, pathsToScan); - - if ( - supportedTrackFiles.length === 0 && - supportedPlaylistsFiles.length === 0 - ) { - set({ - refreshing: false, - refresh: { processed: 0, total: 0 }, - }); - return; - } - - // 5. Import the music tracks found the directories - const tracks: Track[] = await ipcRenderer.invoke( - channels.LIBRARY_IMPORT_TRACKS, - supportedTrackFiles, - ); - - const batchSize = 100; - const chunkedTracks = chunk(tracks, batchSize); - let processed = 0; - - await Promise.allSettled( - chunkedTracks.map(async (chunk) => { - // First, let's see if some of those files are already inserted - const insertedChunk = await db.tracks.insertMultiple(chunk); - - processed += batchSize; - - // Progress bar update - set({ - refresh: { - processed, - total: tracks.length, - }, - }); - - return insertedChunk; - }), - ); - - // TODO: do not re-import existing tracks - - // Import playlists found in the directories - await get().api.scanPlaylists(supportedPlaylistsFiles); - - router.revalidate(); - return; - } catch (err) { - useToastsStore - .getState() - .api.add('danger', 'An error occured when scanning the library'); - logger.warn(err); - return; - } finally { - set({ - refreshing: false, - refresh: { processed: 0, total: 0 }, - }); - } - }, - - /** - * remove tracks from library - */ - remove: async (tracksIDs) => { - // not calling await on it as it calls the synchonous message box - const options: Electron.MessageBoxOptions = { - buttons: ['Cancel', 'Remove'], - title: 'Remove tracks from library?', - message: `Are you sure you want to remove ${tracksIDs.length} element(s) from your library?`, - type: 'warning', - }; - - const result: MessageBoxReturnValue = await ipcRenderer.invoke( - channels.DIALOG_MESSAGE_BOX, - options, - ); - - if (result.response === 1) { - // button possition, here 'remove' - // Remove tracks from the Track collection - await db.tracks.remove(tracksIDs); - - router.revalidate(); - // That would be great to remove those ids from all the playlists, but it's not easy - // and should not cause strange behaviors, all PR for that would be really appreciated - // TODO: see if it's possible to remove the IDs from the selected state of TracksList as it "could" lead to strange behaviors - } - }, - - /** - * Reset the library - */ - reset: async (): Promise => { - usePlayerStore.getState().api.stop(); - try { - const options: Electron.MessageBoxOptions = { - buttons: ['Cancel', 'Reset'], - title: 'Reset library?', - message: - 'Are you sure you want to reset your library? All your tracks and playlists will be cleared.', - type: 'warning', - }; - - const result = await ipcRenderer.invoke( - channels.DIALOG_MESSAGE_BOX, - options, - ); - - if (result.response === 1) { - set({ refreshing: true }); - await db.reset(); - set({ refreshing: false }); - - router.revalidate(); - } - } catch (err) { - logger.error(err); - } - }, - - /** - * Update the play count attribute. - */ - incrementPlayCount: async (track: TrackModel): Promise => { - try { - await db.tracks.incrementPlayCount(track); - } catch (err) { - logger.warn(err); - } - }, - - /** - * Update the id3 attributes. - * IMPROVE ME: add support for writing metadata (hint: node-id3 does not work - * well). - * - * @param trackID The ID of the track to update - * @param newFields The fields to be updated and their new value - */ - updateTrackMetadata: async ( - trackID: string, - newFields: TrackEditableFields, - ): Promise => { - let track = await db.tracks.findOnlyByID(trackID); - - track = { - ...track, - ...newFields, - loweredMetas: getLoweredMeta(newFields), - }; - - if (!track) { - throw new Error('No track found while trying to update track metadata'); - } - - await db.tracks.update(track); - - router.revalidate(); - }, - - /** - * Set highlight trigger for a track - * FIXME: very hacky, and not great, should be done another way - */ - highlightPlayingTrack: (highlight: boolean): void => { - set({ highlightPlayingTrack: highlight }); - }, - }, - - // Old code used to manage folders to be scanned, to be re-enabled one day - // case (types.LIBRARY_ADD_FOLDERS): { // TODO Redux -> move to a thunk - // const { folders } = action.payload; - // let musicFolders = window.MuseeksAPI.config.get('musicFolders'); - - // // Check if we received folders - // if (folders !== undefined) { - // musicFolders = musicFolders.concat(folders); - - // // Remove duplicates, useless children, ect... - // musicFolders = utils.removeUselessFolders(musicFolders); - - // musicFolders.sort(); - - // config.set('musicFolders', musicFolders); - // } - - // return { ...state }; - // } - - // case (types.LIBRARY_REMOVE_FOLDER): { // TODO Redux -> move to a thunk - // if (!state.library.refreshing) { - // const musicFolders = window.MuseeksAPI.config.get('musicFolders'); - - // musicFolders.splice(action.index, 1); - - // config.set('musicFolders', musicFolders); - - // return { ...state }; - // } - - // return state; - // } -})); - -export default useLibraryStore; - -export function useLibraryAPI() { - return useLibraryStore((state) => state.api); -} diff --git a/src/renderer/tsconfig.json b/src/renderer/tsconfig.json deleted file mode 100644 index b81ef858e..000000000 --- a/src/renderer/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "lib": ["ESNext", "DOM", "DOM.Iterable"], - "jsx": "react-jsx", - "plugins": [{ "name": "typescript-plugin-css-modules" }] - }, - "include": ["../renderer", "../shared"] -} diff --git a/src/renderer/views/Root.tsx b/src/renderer/views/Root.tsx deleted file mode 100644 index 61332171d..000000000 --- a/src/renderer/views/Root.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { useEffect } from 'react'; -import { Outlet } from 'react-router-dom'; -import { useDrop } from 'react-dnd'; -import { NativeTypes } from 'react-dnd-html5-backend'; - -import logger from '../../shared/lib/logger'; -import Header from '../components/Header/Header'; -import Footer from '../components/Footer/Footer'; -import Toasts from '../components/Toasts/Toasts'; -import AppActions from '../stores/AppAPI'; -import DropzoneImport from '../components/DropzoneImport/DropzoneImport'; -import MediaSessionEvents from '../components/Events/MediaSessionEvents'; -import AppEvents from '../components/Events/AppEvents'; -import PlayerEvents from '../components/Events/PlayerEvents'; -import IPCPlayerEvents from '../components/Events/IPCPlayerEvents'; -import IPCNavigationEvents from '../components/Events/IPCNavigationEvents'; -import GlobalKeyBindings from '../components/Events/GlobalKeyBindings'; -import { useLibraryAPI } from '../stores/useLibraryStore'; - -import styles from './Root.module.css'; -import { LoaderData } from './router'; - -const { db } = window.MuseeksAPI; - -export default function ViewRoot() { - useEffect(() => { - AppActions.init(); - }, []); - - const libraryAPI = useLibraryAPI(); - - // Drop behavior to add tracks to the library from any string - const [{ isOver }, drop] = useDrop(() => { - return { - accept: [NativeTypes.FILE], - drop(item: { files: Array }) { - const files = item.files.map((file) => file.path); - - libraryAPI - .add(files) - .then((/* _importedTracks */) => { - // TODO: Import to playlist here - }) - .catch((err) => { - logger.warn(err); - }); - }, - collect: (monitor) => ({ - isOver: monitor.isOver(), - }), - }; - }); - - return ( -
- {/** Bunch of global event handlers */} - - - - - - - {/** The actual app */} -
-
- -
-
- - -
- ); -} - -export type RootLoaderData = LoaderData; - -ViewRoot.loader = async () => { - // this can be slow, think about caching it or something, especially when - // we revalidate routing - const tracks = await db.tracks.getAll(); - return { tracks }; -}; diff --git a/src/shared/lib/utils-uri.ts b/src/shared/lib/utils-uri.ts deleted file mode 100644 index d9c226768..000000000 --- a/src/shared/lib/utils-uri.ts +++ /dev/null @@ -1,18 +0,0 @@ -import os from 'os'; -import path from 'path'; - -/** - * Parse an URI, encoding some characters - */ -export const parseUri = (uri: string): string => { - // path and os should not be used - const root = os.platform() === 'win32' ? '' : path.parse(uri).root; - - const location = path - .resolve(uri) - .split(path.sep) - .map((d, i) => (i === 0 ? d : encodeURIComponent(d))) - .reduce((a, b) => path.join(a, b)); - - return `file://${root}${location}`; -}; diff --git a/src/shared/types/declarations/preload.d.ts b/src/shared/types/declarations/preload.d.ts deleted file mode 100644 index 6ccf3b62a..000000000 --- a/src/shared/types/declarations/preload.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -// eslint-disable-next-line spaced-comment -/// - -import type { ElectronAPI, MuseeksAPI } from '../../../preload/entrypoint'; - -declare global { - interface Window { - MuseeksAPI: MuseeksAPI; - ElectronAPI: ElectronAPI; - } - - // Once context bridge is enabled: - // const MuseeksAPI: MuseeksAPI; -} - -export {}; diff --git a/src/shared/types/museeks.ts b/src/shared/types/museeks.ts deleted file mode 100644 index d790c842a..000000000 --- a/src/shared/types/museeks.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Player related stuff - */ -export enum PlayerStatus { - PLAY = 'play', - PAUSE = 'pause', - STOP = 'stop', -} - -export enum Repeat { - ALL = 'all', - ONE = 'one', - NONE = 'none', -} - -export enum SortBy { - ARTIST = 'artist', - ALBUM = 'album', - TITLE = 'title', - DURATION = 'duration', - GENRE = 'genre', -} - -export enum SortOrder { - ASC = 'asc', - DSC = 'dsc', -} - -/** - * App models - */ -export interface Track { - album: string; - artist: string[]; - disk: { - no: number; - of: number; - }; - duration: number; - genre: string[]; - loweredMetas: { - artist: string[]; - album: string; - title: string; - genre: string[]; - }; - path: string; - playCount: number; - title: string; - track: { - no: number; - of: number; - }; - year: number | null; -} - -export interface Playlist { - name: string; - tracks: string[]; - importPath?: string; // associated m3u file -} - -/** - * Database schemes - */ -export type TrackModel = PouchDB.Core.ExistingDocument< - Track & PouchDB.Core.AllDocsMeta ->; - -export type PlaylistModel = PouchDB.Core.ExistingDocument< - Playlist & PouchDB.Core.AllDocsMeta ->; - -/** - * Editable track fields (via right-click -> edit track) - */ -export type TrackEditableFields = Pick< - TrackModel, - 'title' | 'artist' | 'album' | 'genre' ->; - -/** - * Various - */ -export interface Toast { - id: string; - content: string; - type: ToastType; -} - -export type ToastType = 'success' | 'danger' | 'warning'; - -export interface LibrarySort { - by: SortBy; - order: SortOrder; -} - -/** - * Config - */ - -export interface ConfigBounds { - width: number; - height: number; - x: number; - y: number; -} - -// https://github.com/microsoft/TypeScript/issues/29729#issuecomment-460346421 -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type AnyThemeID = string & { whatever?: any }; -// TODO: how to automate this? Maybe losen types to "string" -type ThemeIDs = 'dark' | 'light' | AnyThemeID; - -export interface Config { - theme: ThemeIDs | '__system'; - audioVolume: number; - audioPlaybackRate: number; - audioOutputDevice: string; - audioMuted: boolean; - audioShuffle: boolean; - audioRepeat: Repeat; - tracksDensity: 'normal' | 'compact'; - defaultView: string; - librarySort: { - by: SortBy; - order: SortOrder; - }; - // musicFolders: string[], - sleepBlocker: boolean; - autoUpdateChecker: boolean; - displayNotifications: boolean; - bounds: ConfigBounds; -} - -/** - * Themes - */ - -export interface Theme { - _id: ThemeIDs; - name: string; - themeSource: Electron.NativeTheme['themeSource']; - variables: Record; -} - -/** - * Helpers - */ - -type StringableKey = T extends readonly unknown[] - ? number extends T['length'] - ? number - : `${number}` - : string | number; - -export type Path = T extends object - ? { - [P in keyof T & StringableKey]: `${P}` | `${P}.${Path}`; - }[keyof T & StringableKey] - : never; diff --git a/src/renderer/stores/PlaylistsAPI.ts b/src/stores/PlaylistsAPI.ts similarity index 84% rename from src/renderer/stores/PlaylistsAPI.ts rename to src/stores/PlaylistsAPI.ts index 74c7fbb5f..d2cb34201 100644 --- a/src/renderer/stores/PlaylistsAPI.ts +++ b/src/stores/PlaylistsAPI.ts @@ -1,25 +1,18 @@ -import { - Playlist, - TrackModel, - PlaylistModel, -} from '../../shared/types/museeks'; -import logger from '../../shared/lib/logger'; -import channels from '../../shared/lib/ipc-channels'; +import { PlaylistDoc, TrackDoc } from '../generated/typings'; +import logger from '../lib/logger'; +import channels from '../lib/ipc-channels'; import router from '../views/router'; import useToastsStore from './useToastsStore'; import usePlayerStore from './usePlayerStore'; -const { db } = window.MuseeksAPI; -const { ipcRenderer } = window.ElectronAPI; - /** * Start playing playlist (on double click) */ const play = async (playlistID: string): Promise => { try { - const playlist: PlaylistModel = await db.playlists.findOnlyByID(playlistID); - const tracks: TrackModel[] = await db.tracks.findByID(playlist.tracks); + const playlist: PlaylistDoc = await db.playlists.findOnlyByID(playlistID); + const tracks: TrackDoc[] = await db.tracks.findByID(playlist.tracks); usePlayerStore.getState().api.start(tracks).catch(logger.warn); } catch (err) { logger.warn(err); @@ -36,12 +29,12 @@ const create = async ( redirect = false, ): Promise => { try { - const playlist: Playlist = { + const playlist: PlaylistDoc = { name, tracks, }; - if (importPath) playlist.importPath = importPath; + if (importPath) playlist.doc.importPath = importPath; const doc = await db.playlists.insert(playlist); router.revalidate(); @@ -176,14 +169,16 @@ const reorderTracks = async ( if (tracksIDs.includes(targetTrackID)) return; try { - const playlist: Playlist = await db.playlists.findOnlyByID(playlistID); + const playlist: PlaylistDoc = await db.playlists.findOnlyByID(playlistID); - const newTracks = playlist.tracks.filter((id) => !tracksIDs.includes(id)); + const newTracks = playlist.doc.tracks.filter( + (id) => !tracksIDs.includes(id), + ); let targetIndex = newTracks.indexOf(targetTrackID); if (targetIndex === -1) { throw new Error( - `Could not find targetTrackID in the playlist "${playlist.name}"`, + `Could not find targetTrackID in the playlist "${playlist.doc.name}"`, ); } @@ -205,13 +200,13 @@ const reorderTracks = async ( * TODO: investigate why the playlist path are relative, and not absolute */ const exportToM3u = async (playlistID: string): Promise => { - const playlist: PlaylistModel = await db.playlists.findOnlyByID(playlistID); - const tracks: TrackModel[] = await db.tracks.findByID(playlist.tracks); + const playlist: PlaylistDoc = await db.playlists.findOnlyByID(playlistID); + const tracks: TrackDoc[] = await db.tracks.findByID(playlist.doc.tracks); ipcRenderer.send( channels.PLAYLIST_EXPORT, - playlist.name, - tracks.map((track) => track.path), + playlist.doc.name, + tracks.map((track) => track.doc.path), ); }; diff --git a/src/renderer/stores/SettingsAPI.ts b/src/stores/SettingsAPI.ts similarity index 62% rename from src/renderer/stores/SettingsAPI.ts rename to src/stores/SettingsAPI.ts index 6614fd2ae..02b2a8616 100644 --- a/src/renderer/stores/SettingsAPI.ts +++ b/src/stores/SettingsAPI.ts @@ -1,26 +1,21 @@ import * as semver from 'semver'; +import { getVersion } from '@tauri-apps/api/app'; +import { getCurrent } from '@tauri-apps/api/window'; +import { invoke } from '@tauri-apps/api/core'; -import channels from '../../shared/lib/ipc-channels'; -import { Config, Theme } from '../../shared/types/museeks'; -import logger from '../../shared/lib/logger'; +import { Config, DefaultView } from '../generated/typings'; +import { Theme } from '../types/museeks'; +import { themes } from '../lib/themes'; +import config from '../lib/config'; import useToastsStore from './useToastsStore'; -const { ipcRenderer } = window.ElectronAPI; -const { config } = window.MuseeksAPI; - interface UpdateCheckOptions { silentFail?: boolean; } -const getTheme = async (): Promise => { - const themeID = await ipcRenderer.invoke(channels.THEME_GET_ID); - - return themeID; -}; - const setTheme = async (themeID: string): Promise => { - await ipcRenderer.invoke(channels.THEME_SET_ID, themeID); + await config.set('theme', themeID); // TODO: own plugin? await checkTheme(); }; @@ -29,30 +24,41 @@ const setTheme = async (themeID: string): Promise => { */ const applyThemeToUI = async (theme: Theme): Promise => { // TODO think about variables validity? - const root = document.documentElement; + // TODO: update the window theme dynamically + const root = document.documentElement; Object.entries(theme.variables).forEach(([property, value]) => { root.style.setProperty(property, value); }); }; const checkTheme = async (): Promise => { - const theme: Theme = await ipcRenderer.invoke(channels.THEME_GET); + // TODO: Tauri offers no API to query the system system preference,getCurrent().theme() + // that is used when a window is created with no assigned theme. + // So we are bypassing the user choice for now. + // const themeID: string = await config.get("theme"); + const themeID = (await getCurrent().theme()) ?? 'light'; + const theme = themes[themeID]; + + if (theme == null) { + throw new Error(`Theme ${themeID} not found`); + } + applyThemeToUI(theme); }; const setTracksDensity = async ( - density: Config['tracksDensity'], + density: Config['track_view_density'], ): Promise => { - await window.MuseeksAPI.config.set('tracksDensity', density); + await config.set('track_view_density', density); }; /** * Check and enable sleep blocker if needed */ const checkSleepBlocker = async (): Promise => { - if (await config.get('sleepBlocker')) { - ipcRenderer.send('settings:toggleSleepBlocker', true); + if (await config.get('sleepblocker')) { + invoke('plugin:sleepblocker|enable'); } }; @@ -62,7 +68,13 @@ const checkSleepBlocker = async (): Promise => { const checkForUpdate = async ( options: UpdateCheckOptions = {}, ): Promise => { - const currentVersion = window.MuseeksAPI.version; + const shouldCheck = await config.get('auto_update_checker'); + + if (!shouldCheck) { + return; + } + + const currentVersion = await getVersion(); try { const response = await fetch( @@ -101,48 +113,49 @@ const checkForUpdate = async ( * Init all settings */ const check = async (): Promise => { - await checkTheme(); - checkSleepBlocker(); - if (await config.get('autoUpdateChecker')) { - checkForUpdate({ silentFail: true }).catch((err) => { - logger.error(err); - }); - } + await Promise.allSettled([ + checkTheme(), + checkSleepBlocker(), + checkForUpdate({ silentFail: true }), + ]); }; /** * Toggle sleep blocker */ const toggleSleepBlocker = async (value: boolean): Promise => { - await config.set('sleepBlocker', value); - - ipcRenderer.send('settings:toggleSleepBlocker', value); + if (value == true) { + invoke('plugin:sleepblocker|enable'); + } else { + invoke('plugin:sleepblocker|disable'); + } }; /** * Set the default view of the app */ -const setDefaultView = async (value: string): Promise => { - await config.set('defaultView', value); +const setDefaultView = async (defaultView: DefaultView): Promise => { + await invoke('plugin:default-view|set', { + defaultView, + }); }; /** * Toggle update check on startup */ const toggleAutoUpdateChecker = async (value: boolean): Promise => { - await config.set('autoUpdateChecker', value); + await config.set('auto_update_checker', value); }; /** * Toggle native notifications display */ const toggleDisplayNotifications = async (value: boolean): Promise => { - await config.set('displayNotifications', value); + await config.set('notifications', value); }; // Should we use something else to harmonize between zustand and non-store APIs? const SettingsAPI = { - getTheme, setTheme, applyThemeToUI, setTracksDensity, diff --git a/src/renderer/stores/store-helpers.ts b/src/stores/store-helpers.ts similarity index 100% rename from src/renderer/stores/store-helpers.ts rename to src/stores/store-helpers.ts diff --git a/src/stores/useLibraryStore.ts b/src/stores/useLibraryStore.ts new file mode 100644 index 000000000..5922101d8 --- /dev/null +++ b/src/stores/useLibraryStore.ts @@ -0,0 +1,246 @@ +import { ask, open } from '@tauri-apps/plugin-dialog'; +import { invoke } from '@tauri-apps/api/core'; + +import logger from '../lib/logger'; +import router from '../views/router'; +import channels from '../lib/ipc-channels'; +import { SortBy, SortOrder } from '../generated/typings'; +import config from '../lib/config'; + +import { createStore } from './store-helpers'; +import usePlayerStore from './usePlayerStore'; +import useToastsStore from './useToastsStore'; + +type LibraryState = { + search: string; + sortBy: SortBy; + sortOrder: SortOrder; + refreshing: boolean; + refresh: { + processed: number; + total: number; + }; + highlightPlayingTrack: boolean; + api: { + search: (value: string) => void; + sort: (sortBy: SortBy) => void; + add: () => Promise; + remove: (tracksIDs: string[]) => Promise; + reset: () => Promise; + // updateTrackMetadata: ( + // trackID: string, + // newFields: TrackEditableFields + // ) => Promise; + highlightPlayingTrack: (highlight: boolean) => void; + }; +}; + +const useLibraryStore = createStore((set, get) => ({ + search: '', + // TODO: get from config + sortBy: config.getInitial('library_sort_by'), + sortOrder: config.getInitial('library_sort_order'), + refreshing: false, + refresh: { + processed: 0, + total: 0, + }, + highlightPlayingTrack: false, // hacky, fixme + + api: { + /** + * Filter tracks by search + */ + search: (search): void => { + set({ search }); + }, + + /** + * Filter tracks by sort query + */ + sort: async (sortBy): Promise => { + const prevSortBy = get().sortBy; + const prevSortOrder = get().sortOrder; + + let sortOrder: SortOrder; + + // If same sort by, just reverse the order + if (sortBy === prevSortBy) { + sortOrder = prevSortOrder === 'Asc' ? 'Dsc' : 'Asc'; + } + + // If it's different, then we assume the user needs ASC order by default + else { + sortOrder = 'Asc'; + } + + await config.set('library_sort_by', sortBy); + await config.set('library_sort_order', sortOrder); + + set({ sortBy, sortOrder }); + }, + + /** + * Add tracks to Library + */ + add: async (): Promise => { + try { + const result = await open({ + multiple: true, + directory: true, + }); + + if (result == null) { + return; + } + + set({ refreshing: true }); + await invoke('plugin:database|import_tracks_to_library', { + importPaths: result, + }); + // TODO: re-implement progress + // TODO: scan and import playlists + + router.revalidate(); + return; + } catch (err) { + useToastsStore + .getState() + .api.add('danger', 'An error occured when scanning the library'); + logger.warn(err); + return; + } finally { + set({ + refreshing: false, + refresh: { processed: 0, total: 0 }, + }); + } + }, + + /** + * remove tracks from library + */ + remove: async (tracksIDs) => { + // not calling await on it as it calls the synchonous message box + const options: Electron.MessageBoxOptions = { + buttons: ['Cancel', 'Remove'], + title: 'Remove tracks from library?', + message: `Are you sure you want to remove ${tracksIDs.length} element(s) from your library?`, + type: 'warning', + }; + + const result: MessageBoxReturnValue = await ipcRenderer.invoke( + channels.DIALOG_MESSAGE_BOX, + options, + ); + + if (result.response === 1) { + // button possition, here 'remove' + // Remove tracks from the Track collection + await db.tracks.remove(tracksIDs); + + router.revalidate(); + // That would be great to remove those ids from all the playlists, but it's not easy + // and should not cause strange behaviors, all PR for that would be really appreciated + // TODO: see if it's possible to remove the IDs from the selected state of TracksList as it "could" lead to strange behaviors + } + }, + + /** + * Reset the library + */ + reset: async (): Promise => { + usePlayerStore.getState().api.stop(); + try { + const confirmed = await ask( + 'All your tracks and playlists will be deleted from Museeks.', + 'Are you sure you want to reset your library?', + ); + + if (confirmed) { + await invoke('reset_database'); + router.revalidate(); + } + } catch (err) { + logger.error(err); + } + }, + + // /** + // * Update the id3 attributes. + // * IMPROVE ME: add support for writing metadata (hint: node-id3 does not work + // * well). + // * + // * @param trackID The ID of the track to update + // * @param newFields The fields to be updated and their new value + // */ + // updateTrackMetadata: async ( + // trackID: string, + // newFields: TrackEditableFields + // ): Promise => { + // let track = await db.tracks.findOnlyByID(trackID); + + // track = { + // ...track, + // ...newFields, + // loweredMetas: getLoweredMeta(newFields), + // }; + + // if (!track) { + // throw new Error("No track found while trying to update track metadata"); + // } + + // await db.tracks.update(track); + + // router.revalidate(); + // }, + + /** + * Set highlight trigger for a track + * FIXME: very hacky, and not great, should be done another way + */ + highlightPlayingTrack: (highlight: boolean): void => { + set({ highlightPlayingTrack: highlight }); + }, + }, + + // Old code used to manage folders to be scanned, to be re-enabled one day + // case (types.LIBRARY_ADD_FOLDERS): { // TODO Redux -> move to a thunk + // const { folders } = action.payload; + // let musicFolders = window.MuseeksAPI.config.get('musicFolders'); + + // // Check if we received folders + // if (folders !== undefined) { + // musicFolders = musicFolders.concat(folders); + + // // Remove duplicates, useless children, ect... + // musicFolders = utils.removeUselessFolders(musicFolders); + + // musicFolders.sort(); + + // config.set('musicFolders', musicFolders); + // } + + // return { ...state }; + // } + + // case (types.LIBRARY_REMOVE_FOLDER): { // TODO Redux -> move to a thunk + // if (!state.library.refreshing) { + // const musicFolders = window.MuseeksAPI.config.get('musicFolders'); + + // musicFolders.splice(action.index, 1); + + // config.set('musicFolders', musicFolders); + + // return { ...state }; + // } + + // return state; + // } +})); + +export default useLibraryStore; + +export function useLibraryAPI() { + return useLibraryStore((state) => state.api); +} diff --git a/src/renderer/stores/usePlayerStore.ts b/src/stores/usePlayerStore.ts similarity index 89% rename from src/renderer/stores/usePlayerStore.ts rename to src/stores/usePlayerStore.ts index b965515b8..2c66eaec5 100644 --- a/src/renderer/stores/usePlayerStore.ts +++ b/src/stores/usePlayerStore.ts @@ -2,26 +2,28 @@ import debounce from 'lodash/debounce'; import { StateCreator } from 'zustand'; import { persist } from 'zustand/middleware'; -import { PlayerStatus, Repeat, TrackModel } from '../../shared/types/museeks'; +import { PlayerStatus } from '../types/museeks'; import { shuffleTracks } from '../lib/utils-player'; -import logger from '../../shared/lib/logger'; +import logger from '../lib/logger'; import router from '../views/router'; import player from '../lib/player'; +import { TrackDoc, Repeat } from '../generated/typings'; +import config from '../lib/config'; import { createStore } from './store-helpers'; import useToastsStore from './useToastsStore'; import useLibraryStore from './useLibraryStore'; type PlayerState = { - queue: TrackModel[]; - oldQueue: TrackModel[]; + queue: TrackDoc[]; + oldQueue: TrackDoc[]; queueCursor: number | null; queueOrigin: null | string; repeat: Repeat; shuffle: boolean; playerStatus: PlayerStatus; api: { - start: (queue: TrackModel[], _id?: string) => Promise; + start: (queue: TrackDoc[], id?: string) => Promise; play: () => Promise; pause: () => void; playPause: () => Promise; @@ -41,19 +43,17 @@ type PlayerState = { removeFromQueue: (index: number) => void; addInQueue: (tracksIDs: string[]) => Promise; addNextInQueue: (tracksIDs: string[]) => Promise; - setQueue: (tracks: TrackModel[]) => void; + setQueue: (tracks: TrackDoc[]) => void; }; }; -const { config } = window.MuseeksAPI; - const usePlayerStore = createPlayerStore((set, get) => ({ queue: [], // Tracks to be played oldQueue: [], // Queue backup (in case of shuffle) queueCursor: null, // The cursor of the queue queueOrigin: null, // URL of the queue when it was started - repeat: config.__initialConfig['audioRepeat'], // the current repeat state (one, all, none) - shuffle: config.__initialConfig['audioShuffle'], // If shuffle mode is enabled + repeat: config.getInitial('audio_repeat'), // the current repeat state (one, all, none) + shuffle: config.getInitial('audio_shuffle'), // If shuffle mode is enabled playerStatus: PlayerStatus.STOP, // Player status api: { @@ -76,14 +76,12 @@ const usePlayerStore = createPlayerStore((set, get) => ({ const shuffle = state.shuffle; const oldQueue = [...newQueue]; - const trackID = _id || newQueue[0]._id; + const trackID = _id || newQueue[0].id; // Typically, if we are in the playlists generic view without any view selected if (newQueue.length === 0) return; - const queuePosition = newQueue.findIndex( - (track) => track._id === trackID, - ); + const queuePosition = newQueue.findIndex((track) => track.id === trackID); // If a track exists if (queuePosition > -1) { @@ -175,9 +173,9 @@ const usePlayerStore = createPlayerStore((set, get) => ({ let newQueueCursor; if (queueCursor !== null) { - if (repeat === Repeat.ONE) { + if (repeat === 'One') { newQueueCursor = queueCursor; - } else if (repeat === Repeat.ALL && queueCursor === queue.length - 1) { + } else if (repeat === 'All' && queueCursor === queue.length - 1) { // is last track newQueueCursor = 0; // start with new track } else { @@ -239,12 +237,13 @@ const usePlayerStore = createPlayerStore((set, get) => ({ toggleShuffle: async (shuffle) => { shuffle = shuffle ?? !get().shuffle; - await config.set('audioShuffle', shuffle); + // TODO: + // await config.set("audioShuffle", shuffle); const { queue, queueCursor, oldQueue } = get(); if (queueCursor !== null) { - const trackPlayingID = queue[queueCursor]._id; + const trackPlayingID = queue[queueCursor].id; // If we need to shuffle everything if (shuffle) { @@ -260,7 +259,7 @@ const usePlayerStore = createPlayerStore((set, get) => ({ } else { // Unshuffle the queue by restoring the initial queue const currentTrackIndex = oldQueue.findIndex( - (track) => trackPlayingID === track._id, + (track) => trackPlayingID === track.id, ); // Roll back to the old but update queueCursor @@ -280,19 +279,20 @@ const usePlayerStore = createPlayerStore((set, get) => ({ // Get to the next repeat type if none is specified if (repeat == undefined) { switch (get().repeat) { - case Repeat.NONE: - repeat = Repeat.ALL; + case 'None': + repeat = 'All'; break; - case Repeat.ALL: - repeat = Repeat.ONE; + case 'All': + repeat = 'One'; break; - case Repeat.ONE: - repeat = Repeat.NONE; + case 'One': + repeat = 'None'; break; } } - await config.set('audioRepeat', repeat); + // TODO: + // await config.set("audioRepeat", repeat); set({ repeat }); }, @@ -311,7 +311,8 @@ const usePlayerStore = createPlayerStore((set, get) => ({ if (muted) player.mute(); else player.unmute(); - await config.set('audioMuted', muted); + // TODO: + // await config.set("audioMuted", muted); }, /** @@ -322,7 +323,8 @@ const usePlayerStore = createPlayerStore((set, get) => ({ // if in allowed range player.setPlaybackRate(value); - await config.set('audioPlaybackRate', value); + // TODO: + // await config.set("audioPlaybackRate", value); } }, @@ -333,7 +335,8 @@ const usePlayerStore = createPlayerStore((set, get) => ({ if (deviceID) { try { await player.setOutputDevice(deviceID); - await config.set('audioOutputDevice', deviceID); + // TODO: + // await config.set("audioOutputDevice", deviceID); } catch (err) { logger.warn(err); useToastsStore @@ -454,7 +457,7 @@ const usePlayerStore = createPlayerStore((set, get) => ({ /** * Set the queue */ - setQueue: (tracks: TrackModel[]) => { + setQueue: (tracks: TrackDoc[]) => { set({ queue: tracks, }); @@ -526,5 +529,5 @@ function createPlayerStore(store: StateCreator) { * Make sure we don't save audio volume to the file system too often */ const saveVolume = debounce(async (volume: number) => { - await config.set('audioVolume', volume); + await config.set('audio_volume', volume); }, 500); diff --git a/src/renderer/stores/useToastsStore.ts b/src/stores/useToastsStore.ts similarity index 93% rename from src/renderer/stores/useToastsStore.ts rename to src/stores/useToastsStore.ts index 42ff9d4a3..e82cda3b7 100644 --- a/src/renderer/stores/useToastsStore.ts +++ b/src/stores/useToastsStore.ts @@ -1,6 +1,6 @@ import { nanoid } from 'nanoid'; -import { Toast, ToastType } from '../../shared/types/museeks'; +import { Toast, ToastType } from '../types/museeks'; import { createStore } from './store-helpers'; diff --git a/src/renderer/styles/general.module.css b/src/styles/general.module.css similarity index 91% rename from src/renderer/styles/general.module.css rename to src/styles/general.module.css index 5745f882e..e8d5832b2 100644 --- a/src/renderer/styles/general.module.css +++ b/src/styles/general.module.css @@ -1,6 +1,8 @@ /* Colors (default theme) */ :root { --main-color: #459ce7; + --main-color-darker: #3a73a4; + --main-color-lighter: #63aff0; --link-color: #459ce7; --link-color-hover: #52afff; --bold: 600; @@ -60,6 +62,6 @@ strong { } /* App wrapper */ -:global #wrap { +:global #root { height: 100vh; } diff --git a/src/renderer/styles/main.module.css b/src/styles/main.module.css similarity index 100% rename from src/renderer/styles/main.module.css rename to src/styles/main.module.css diff --git a/src/renderer/styles/utils.module.css b/src/styles/utils.module.css similarity index 100% rename from src/renderer/styles/utils.module.css rename to src/styles/utils.module.css diff --git a/src/shared/themes/dark.json b/src/themes/dark.json similarity index 100% rename from src/shared/themes/dark.json rename to src/themes/dark.json diff --git a/src/shared/themes/light.json b/src/themes/light.json similarity index 100% rename from src/shared/themes/light.json rename to src/themes/light.json diff --git a/src/shared/types/declarations/images.d.ts b/src/types/declarations/images.d.ts similarity index 100% rename from src/shared/types/declarations/images.d.ts rename to src/types/declarations/images.d.ts diff --git a/src/shared/types/declarations/level-js.d.ts b/src/types/declarations/level-js.d.ts similarity index 100% rename from src/shared/types/declarations/level-js.d.ts rename to src/types/declarations/level-js.d.ts diff --git a/src/shared/types/declarations/linvodb3.d.ts b/src/types/declarations/linvodb3.d.ts similarity index 100% rename from src/shared/types/declarations/linvodb3.d.ts rename to src/types/declarations/linvodb3.d.ts diff --git a/src/shared/types/declarations/react-fontawesome.d.ts b/src/types/declarations/react-fontawesome.d.ts similarity index 100% rename from src/shared/types/declarations/react-fontawesome.d.ts rename to src/types/declarations/react-fontawesome.d.ts diff --git a/src/shared/types/declarations/react-onclickout.d.ts b/src/types/declarations/react-onclickout.d.ts similarity index 100% rename from src/shared/types/declarations/react-onclickout.d.ts rename to src/types/declarations/react-onclickout.d.ts diff --git a/src/shared/types/declarations/react-simple-input.d.ts b/src/types/declarations/react-simple-input.d.ts similarity index 100% rename from src/shared/types/declarations/react-simple-input.d.ts rename to src/types/declarations/react-simple-input.d.ts diff --git a/src/shared/types/declarations/svg-inline-react.d.ts b/src/types/declarations/svg-inline-react.d.ts similarity index 100% rename from src/shared/types/declarations/svg-inline-react.d.ts rename to src/types/declarations/svg-inline-react.d.ts diff --git a/src/types/museeks.ts b/src/types/museeks.ts new file mode 100644 index 000000000..6de47e900 --- /dev/null +++ b/src/types/museeks.ts @@ -0,0 +1,61 @@ +import { Track } from '../generated/typings'; + +/** + * Player related stuff + */ +export enum PlayerStatus { + PLAY = 'play', + PAUSE = 'pause', + STOP = 'stop', +} + +/** + * Editable track fields (via right-click -> edit track) + */ +export type TrackEditableFields = Pick< + Track, + 'title' | 'artists' | 'album' | 'genres' +>; + +export type TrackSearchableFields = Pick< + Track, + 'title' | 'artists' | 'album' | 'genres' +>; + +/** + * Various + */ +export interface Toast { + id: string; + content: string; + type: ToastType; +} + +export type ToastType = 'success' | 'danger' | 'warning'; + +/** + * Themes + */ + +export interface Theme { + _id: string; + name: string; + themeSource: 'light' | 'dark'; + variables: Record; +} + +/** + * Helpers + */ + +type StringableKey = T extends readonly unknown[] + ? number extends T['length'] + ? number + : `${number}` + : string | number; + +export type Path = T extends object + ? { + [P in keyof T & StringableKey]: `${P}` | `${P}.${Path}`; + }[keyof T & StringableKey] + : never; diff --git a/src/renderer/views/Root.module.css b/src/views/Root.module.css similarity index 100% rename from src/renderer/views/Root.module.css rename to src/views/Root.module.css diff --git a/src/views/Root.tsx b/src/views/Root.tsx new file mode 100644 index 000000000..200677f91 --- /dev/null +++ b/src/views/Root.tsx @@ -0,0 +1,74 @@ +// import { useEffect } from "react"; +import { Outlet, useLoaderData } from 'react-router-dom'; +import { type } from '@tauri-apps/plugin-os'; +// import logger from "../lib/logger"; +import { invoke } from '@tauri-apps/api/core'; +import { getCurrent } from '@tauri-apps/api/window'; +import { Suspense, useEffect } from 'react'; + +import Header from '../components/Header/Header'; +import Footer from '../components/Footer/Footer'; +import Toasts from '../components/Toasts/Toasts'; +// import AppActions from "../stores/AppAPI"; +import DropzoneImport from '../components/DropzoneImport/DropzoneImport'; +import MediaSessionEvents from '../components/Events/MediaSessionEvents'; +// import AppEvents from "../components/Events/AppEvents"; +import PlayerEvents from '../components/Events/PlayerEvents'; +// import IPCPlayerEvents from "../components/Events/IPCPlayerEvents"; +// import IPCNavigationEvents from "../components/Events/IPCNavigationEvents"; +import GlobalKeyBindings from '../components/Events/GlobalKeyBindings'; +// import { useLibraryAPI } from "../stores/useLibraryStore"; +import SettingsAPI from '../stores/SettingsAPI'; +import { TrackDoc } from '../generated/typings'; + +import styles from './Root.module.css'; +import { LoaderData } from './router'; + +export default function ViewRoot() { + useEffect(() => { + SettingsAPI.check() + // Show the app once everything is loaded + .then(() => getCurrent()) + .then((window) => { + window.show(); + }); + }, []); + + const { platform } = useLoaderData() as RootLoaderData; + + return ( +
+ {/** Bunch of global event handlers */} + {/** TODO: */} + {/* + + */} + + + + {/** The actual app */} +
+
+ +
+
+ + + + +
+ ); +} + +export type RootLoaderData = LoaderData; + +ViewRoot.loader = async () => { + const osType = await type(); + + // this can be slow, think about caching it or something, especially when + // we revalidate routing + const tracks = (await invoke( + 'plugin:database|get_all_tracks', + )) as Array; + return { tracks, platform: osType }; +}; diff --git a/src/renderer/views/ViewLibrary.module.css b/src/views/ViewLibrary.module.css similarity index 100% rename from src/renderer/views/ViewLibrary.module.css rename to src/views/ViewLibrary.module.css diff --git a/src/renderer/views/ViewLibrary.tsx b/src/views/ViewLibrary.tsx similarity index 93% rename from src/renderer/views/ViewLibrary.tsx rename to src/views/ViewLibrary.tsx index c117a90f7..60099bc4d 100644 --- a/src/renderer/views/ViewLibrary.tsx +++ b/src/views/ViewLibrary.tsx @@ -6,14 +6,13 @@ import TracksList from '../components/TracksList/TracksList'; import useLibraryStore from '../stores/useLibraryStore'; import usePlayingTrackID from '../hooks/usePlayingTrackID'; import useFilteredTracks from '../hooks/useFilteredTracks'; +import config from '../lib/config'; import { RootLoaderData } from './Root'; import { LoaderData } from './router'; import appStyles from './Root.module.css'; import styles from './ViewLibrary.module.css'; -const { db, config } = window.MuseeksAPI; - export default function ViewLibrary() { const trackPlayingID = usePlayingTrackID(); const refreshing = useLibraryStore((state) => state.refreshing); @@ -87,7 +86,10 @@ export type LibraryLoaderData = LoaderData; ViewLibrary.loader = async () => { return { - playlists: await db.playlists.getAll(), - tracksDensity: await config.get('tracksDensity'), + // TODO: + playlists: [], + tracksDensity: (await config.get('track_view_density')) as + | 'compact' + | 'normal', }; }; diff --git a/src/renderer/views/ViewPlaylistDetails.tsx b/src/views/ViewPlaylistDetails.tsx similarity index 96% rename from src/renderer/views/ViewPlaylistDetails.tsx rename to src/views/ViewPlaylistDetails.tsx index e12b15252..6c7adab2f 100644 --- a/src/renderer/views/ViewPlaylistDetails.tsx +++ b/src/views/ViewPlaylistDetails.tsx @@ -12,11 +12,10 @@ import PlaylistsAPI from '../stores/PlaylistsAPI'; import { filterTracks } from '../lib/utils-library'; import useLibraryStore from '../stores/useLibraryStore'; import usePlayingTrackID from '../hooks/usePlayingTrackID'; +import config from '../lib/config'; import { LoaderData } from './router'; -const { db, config } = window.MuseeksAPI; - export default function ViewPlaylistDetails() { const { playlists, playlistTracks, tracksDensity } = useLoaderData() as PlaylistLoaderData; @@ -110,6 +109,6 @@ ViewPlaylistDetails.loader = async ({ params }: LoaderFunctionArgs) => { // TODO: can we re-use parent's data? playlists: await db.playlists.getAll(), playlistTracks: await db.tracks.findByID(playlist.tracks), - tracksDensity: await config.get('tracksDensity'), + tracksDensity: await config.get('track_view_density'), }; }; diff --git a/src/renderer/views/ViewPlaylists.module.css b/src/views/ViewPlaylists.module.css similarity index 100% rename from src/renderer/views/ViewPlaylists.module.css rename to src/views/ViewPlaylists.module.css diff --git a/src/renderer/views/ViewPlaylists.tsx b/src/views/ViewPlaylists.tsx similarity index 84% rename from src/renderer/views/ViewPlaylists.tsx rename to src/views/ViewPlaylists.tsx index c728f04d5..acf02b8fd 100644 --- a/src/renderer/views/ViewPlaylists.tsx +++ b/src/views/ViewPlaylists.tsx @@ -5,17 +5,17 @@ import { redirect, useLoaderData, } from 'react-router-dom'; +import { invoke } from '@tauri-apps/api/core'; import PlaylistsNav from '../components/PlaylistsNav/PlaylistsNav'; import * as ViewMessage from '../elements/ViewMessage/ViewMessage'; import PlaylistsAPI from '../stores/PlaylistsAPI'; +import { PlaylistDoc } from '../generated/typings'; import { LoaderData } from './router'; import appStyles from './Root.module.css'; import styles from './ViewPlaylists.module.css'; -const { db } = window.MuseeksAPI; - export default function ViewPlaylists() { const { playlists } = useLoaderData() as PlaylistsLoaderData; @@ -51,7 +51,9 @@ export default function ViewPlaylists() { export type PlaylistsLoaderData = LoaderData; ViewPlaylists.loader = async ({ params }: LoaderFunctionArgs) => { - const playlists = await db.playlists.getAll(); + const playlists = (await invoke( + 'plugin:database|get_all_playlists', + )) as Array; const [firstPlaylist] = playlists; const { playlistID } = params; @@ -60,10 +62,10 @@ ViewPlaylists.loader = async ({ params }: LoaderFunctionArgs) => { playlistID === undefined || // If playlist ID does not exist, redirect to the first playlist (playlistID !== undefined && - !playlists.map((playlist) => playlist._id).includes(playlistID)) + !playlists.map((playlist) => playlist.id).includes(playlistID)) ) { if (firstPlaylist !== undefined) { - return redirect(`/playlists/${firstPlaylist._id}`); + return redirect(`/playlists/${firstPlaylist.id}`); } } diff --git a/src/renderer/views/ViewSettings.module.css b/src/views/ViewSettings.module.css similarity index 100% rename from src/renderer/views/ViewSettings.module.css rename to src/views/ViewSettings.module.css diff --git a/src/renderer/views/ViewSettings.tsx b/src/views/ViewSettings.tsx similarity index 82% rename from src/renderer/views/ViewSettings.tsx rename to src/views/ViewSettings.tsx index 06e6a326c..d444b9c30 100644 --- a/src/renderer/views/ViewSettings.tsx +++ b/src/views/ViewSettings.tsx @@ -1,6 +1,8 @@ import { Outlet, useMatch, Navigate } from 'react-router-dom'; +import { getTauriVersion, getVersion } from '@tauri-apps/api/app'; import * as Nav from '../elements/Nav/Nav'; +import config from '../lib/config'; import { LoaderData } from './router'; import appStyles from './Root.module.css'; @@ -32,9 +34,9 @@ export default function ViewSettingsView() { export type SettingsLoaderData = LoaderData; ViewSettingsView.loader = async () => { - const config = await window.MuseeksAPI.config.getAll(); - return { - config, + config: await config.getAll(), + version: await getVersion(), + tauriVersion: await getTauriVersion(), }; }; diff --git a/src/renderer/views/ViewSettingsAbout.tsx b/src/views/ViewSettingsAbout.tsx similarity index 69% rename from src/renderer/views/ViewSettingsAbout.tsx rename to src/views/ViewSettingsAbout.tsx index 6a6771baa..c66feab59 100644 --- a/src/renderer/views/ViewSettingsAbout.tsx +++ b/src/views/ViewSettingsAbout.tsx @@ -1,11 +1,15 @@ +import { useLoaderData } from 'react-router-dom'; + import ExternalLink from '../elements/ExternalLink/ExternalLink'; import Heart from '../elements/Heart/Heart'; import * as Setting from '../components/Setting/Setting'; import SettingsAPI from '../stores/SettingsAPI'; import Button from '../elements/Button/Button'; +import { SettingsLoaderData } from './ViewSettings'; + export default function ViewSettingsAbout() { - const version = window.MuseeksAPI.version; + const { version, tauriVersion } = useLoaderData() as SettingsLoaderData; return (
@@ -20,7 +24,7 @@ export default function ViewSettingsAbout() { href={`https://github.com/martpie/museeks/releases/tag/${version}`} > release notes - + {' '}

+

Tauri {tauriVersion}

Contributors

@@ -54,26 +59,6 @@ export default function ViewSettingsAbout() { .

- -

Support me

-

- Maintaining Museeks includes some costs. All the work is done on - contributors' free time, but I still have recurring costs like - domain names and developer certificates. -

-

- If you appreciate my work, and if you can afford it, you can for - example show support by{' '} - - sponsoring me - {' '} - (or just{' '} - - buying me a beer - - ) on GitHub (🙌). -

-
); } diff --git a/src/renderer/views/ViewSettingsAudio.tsx b/src/views/ViewSettingsAudio.tsx similarity index 93% rename from src/renderer/views/ViewSettingsAudio.tsx rename to src/views/ViewSettingsAudio.tsx index e9da1c536..2ee877c2f 100644 --- a/src/renderer/views/ViewSettingsAudio.tsx +++ b/src/views/ViewSettingsAudio.tsx @@ -26,7 +26,7 @@ export default function ViewSettingsAudio() { diff --git a/src/renderer/views/ViewSettingsLibrary.tsx b/src/views/ViewSettingsLibrary.tsx similarity index 60% rename from src/renderer/views/ViewSettingsLibrary.tsx rename to src/views/ViewSettingsLibrary.tsx index 6bf247e18..7ed423aae 100644 --- a/src/renderer/views/ViewSettingsLibrary.tsx +++ b/src/views/ViewSettingsLibrary.tsx @@ -1,31 +1,11 @@ -import { useCallback } from 'react'; - import * as Setting from '../components/Setting/Setting'; import Button from '../elements/Button/Button'; -import channels from '../../shared/lib/ipc-channels'; -import logger from '../../shared/lib/logger'; import useLibraryStore, { useLibraryAPI } from '../stores/useLibraryStore'; -const { ipcRenderer } = window.ElectronAPI; - export default function ViewSettingsLibrary() { const libraryAPI = useLibraryAPI(); const isLibraryRefreshing = useLibraryStore((state) => state.refreshing); - const openFolderSelector = useCallback(async () => { - const options: Electron.OpenDialogOptions = { - properties: ['multiSelections', 'openDirectory', 'openFile'], - }; - - const result = await ipcRenderer.invoke(channels.DIALOG_OPEN, options); - - if (result.filePaths) { - libraryAPI.add(result.filePaths).catch((err) => { - logger.warn(err); - }); - } - }, [libraryAPI]); - return (
@@ -34,7 +14,7 @@ export default function ViewSettingsLibrary() { This will also scan for .m3u files and create corresponding playlists. - diff --git a/src/renderer/views/ViewSettingsUI.tsx b/src/views/ViewSettingsUI.tsx similarity index 80% rename from src/renderer/views/ViewSettingsUI.tsx rename to src/views/ViewSettingsUI.tsx index f9b3ec923..3d669b7cd 100644 --- a/src/renderer/views/ViewSettingsUI.tsx +++ b/src/views/ViewSettingsUI.tsx @@ -4,8 +4,8 @@ import { useLoaderData } from 'react-router-dom'; import SettingsAPI from '../stores/SettingsAPI'; import * as Setting from '../components/Setting/Setting'; import CheckboxSetting from '../components/SettingCheckbox/SettingCheckbox'; -import { themes } from '../../shared/lib/themes'; -import { Config } from '../../shared/types/museeks'; +import { themes } from '../lib/themes'; +import { Config, DefaultView } from '../generated/typings'; import { SettingsLoaderData } from './ViewSettings'; @@ -23,14 +23,14 @@ export default function ViewSettingsUI() { ChangeEventHandler >((e) => { SettingsAPI.setTracksDensity( - e.currentTarget.value as Config['tracksDensity'], + e.currentTarget.value as Config['track_view_density'], ); }, []); const onDefaultViewChange = useCallback< ChangeEventHandler >((e) => { - SettingsAPI.setDefaultView(e.currentTarget.value); + SettingsAPI.setDefaultView(e.currentTarget.value as DefaultView); }, []); return ( @@ -41,9 +41,10 @@ export default function ViewSettingsUI() { defaultValue={config.theme} onChange={onThemeChange} id="setting-theme" + disabled // Issue in Tauri where we cannot easily detect system-wide preferences > - {themes.map((theme) => { + {Object.values(themes).map((theme) => { return (