From ac8f27b8f0725c1951e7883610303ff88f121d27 Mon Sep 17 00:00:00 2001 From: Ryan Draga Date: Mon, 12 Oct 2026 10:28:48 -0400 Subject: [PATCH] MINOR: Replace Tauri w/GPUi --- .env.example | 2 +- .github/scripts/bump-version.sh | 26 +- .github/scripts/inject-macos-dmg-installer.sh | 73 - .github/scripts/package-macos.sh | 147 + .github/workflows/ci-release.yml | 134 +- .gitignore | 3 +- .vscode/extensions.json | 2 - .vscode/settings.json | 3 - Makefile | 216 +- README.md | 54 +- app/.gitignore | 3 + {src-tauri => app}/Cargo.lock | 6266 ++++++++++++----- {src-tauri => app}/Cargo.toml | 14 +- {src-tauri => app}/app-icon.png | Bin app/build.rs | 10 + {src-tauri => app}/icons/128x128.png | Bin {src-tauri => app}/icons/128x128@2x.png | Bin {src-tauri => app}/icons/32x32.png | Bin {src-tauri => app}/icons/64x64.png | Bin .../icons/Square107x107Logo.png | Bin .../icons/Square142x142Logo.png | Bin .../icons/Square150x150Logo.png | Bin .../icons/Square284x284Logo.png | Bin {src-tauri => app}/icons/Square30x30Logo.png | Bin .../icons/Square310x310Logo.png | Bin {src-tauri => app}/icons/Square44x44Logo.png | Bin {src-tauri => app}/icons/Square71x71Logo.png | Bin {src-tauri => app}/icons/Square89x89Logo.png | Bin {src-tauri => app}/icons/StoreLogo.png | Bin {src-tauri => app}/icons/icon.icns | Bin {src-tauri => app}/icons/icon.ico | Bin {src-tauri => app}/icons/icon.png | Bin app/icons/icon.rc | 1 + .../icons/ios/AppIcon-20x20@1x.png | Bin .../icons/ios/AppIcon-20x20@2x-1.png | Bin .../icons/ios/AppIcon-20x20@2x.png | Bin .../icons/ios/AppIcon-20x20@3x.png | Bin .../icons/ios/AppIcon-29x29@1x.png | Bin .../icons/ios/AppIcon-29x29@2x-1.png | Bin .../icons/ios/AppIcon-29x29@2x.png | Bin .../icons/ios/AppIcon-29x29@3x.png | Bin .../icons/ios/AppIcon-40x40@1x.png | Bin .../icons/ios/AppIcon-40x40@2x-1.png | Bin .../icons/ios/AppIcon-40x40@2x.png | Bin .../icons/ios/AppIcon-40x40@3x.png | Bin .../icons/ios/AppIcon-512@2x.png | Bin .../icons/ios/AppIcon-60x60@2x.png | Bin .../icons/ios/AppIcon-60x60@3x.png | Bin .../icons/ios/AppIcon-76x76@1x.png | Bin .../icons/ios/AppIcon-76x76@2x.png | Bin .../icons/ios/AppIcon-83.5x83.5@2x.png | Bin {src-tauri => app}/macos/How to install.txt | 0 .../macos/Install Grimoire.command | 0 {src-tauri => app}/migrations/001_init.sql | 0 .../migrations/002_reading_progress.sql | 0 {src-tauri => app}/src/comic.rs | 18 - {src-tauri => app}/src/config.rs | 50 +- {src-tauri => app}/src/convert.rs | 0 {src-tauri => app}/src/db.rs | 0 {src-tauri => app}/src/error.rs | 0 {src-tauri => app}/src/kf8_extract.rs | 0 {src-tauri => app}/src/kindle.rs | 32 - app/src/lib.rs | 17 + {src-tauri => app}/src/library.rs | 20 +- {src-tauri => app}/src/main.rs | 0 {src-tauri => app}/src/metadata.rs | 0 {src-tauri => app}/src/openlibrary.rs | 0 {src-tauri => app}/src/reader.rs | 0 {src-tauri => app}/src/state.rs | 0 app/src/ui/backend.rs | 292 + app/src/ui/book.rs | 531 ++ app/src/ui/devices.rs | 404 ++ app/src/ui/document.rs | 208 + app/src/ui/icon.rs | 42 + app/src/ui/library.rs | 1244 ++++ app/src/ui/mod.rs | 71 + app/src/ui/reader.rs | 522 ++ app/src/ui/settings.rs | 221 + app/src/ui/shell.rs | 648 ++ app/src/ui/theme.rs | 214 + package.json | 40 - pnpm-lock.yaml | 1462 ---- pnpm-workspace.yaml | 13 - src-tauri/.gitignore | 7 - src-tauri/build.rs | 3 - src-tauri/capabilities/default.json | 22 - .../android/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../icons/android/mipmap-hdpi/ic_launcher.png | Bin 3092 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 25593 -> 0 bytes .../android/mipmap-hdpi/ic_launcher_round.png | Bin 3550 -> 0 bytes .../icons/android/mipmap-mdpi/ic_launcher.png | Bin 2924 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 13591 -> 0 bytes .../android/mipmap-mdpi/ic_launcher_round.png | Bin 3439 -> 0 bytes .../android/mipmap-xhdpi/ic_launcher.png | Bin 9103 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 39530 -> 0 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 10801 -> 0 bytes .../android/mipmap-xxhdpi/ic_launcher.png | Bin 17419 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 69206 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 20470 -> 0 bytes .../android/mipmap-xxxhdpi/ic_launcher.png | Bin 27091 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 100555 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 31486 -> 0 bytes .../android/values/ic_launcher_background.xml | 4 - src-tauri/package.json | 15 - src-tauri/pnpm-lock.yaml | 29 - src-tauri/src/device_watch.rs | 181 - src-tauri/src/lib.rs | 440 -- src-tauri/tauri.conf.json | 63 - src-tauri/windows/fragments/library-path.wxs | 28 - src-tauri/windows/hooks.nsh | 31 - src-tauri/windows/set-library-path.ps1 | 41 - src/app.css | 458 -- src/app.html | 31 - src/epubjs.d.ts | 25 - src/lib/CoverPickerModal.svelte | 248 - src/lib/LibraryFilters.svelte | 332 - src/lib/ThemeToggle.svelte | 71 - src/lib/WindowControls.svelte | 313 - src/lib/api.ts | 348 - src/lib/device-presence.svelte.ts | 89 - src/lib/library-filters.ts | 38 - src/lib/reader-prefs.test.ts | 43 - src/lib/reader-prefs.ts | 94 - src/lib/theme.test.ts | 63 - src/lib/theme.ts | 45 - src/lib/ui/ActionButton.svelte | 70 - src/lib/ui/Button.svelte | 39 - src/lib/ui/Checkbox.svelte | 72 - src/lib/ui/Dialog.svelte | 144 - src/lib/ui/SearchField.svelte | 42 - src/lib/ui/TextField.svelte | 100 - src/lib/view-mode.test.ts | 22 - src/lib/view-mode.ts | 17 - src/lib/window-chrome.ts | 17 - src/lib/windows.test.ts | 17 - src/lib/windows.ts | 37 - src/routes/+layout.svelte | 230 - src/routes/+layout.ts | 5 - src/routes/+page.svelte | 1432 ---- src/routes/book/[id]/+page.svelte | 588 -- src/routes/devices/+page.svelte | 306 - src/routes/devices/library/+page.svelte | 750 -- src/routes/read/[id]/+page.svelte | 744 -- src/routes/read/[id]/+page.ts | 1 - src/routes/settings/+page.svelte | 338 - static/favicon.svg | 1061 --- static/svelte.svg | 1 - static/tauri.svg | 6 - static/vite.svg | 1 - svelte.config.js | 18 - tsconfig.json | 19 - vite.config.js | 37 - 152 files changed, 9329 insertions(+), 12855 deletions(-) delete mode 100755 .github/scripts/inject-macos-dmg-installer.sh create mode 100755 .github/scripts/package-macos.sh delete mode 100644 .vscode/settings.json create mode 100644 app/.gitignore rename {src-tauri => app}/Cargo.lock (61%) rename {src-tauri => app}/Cargo.toml (73%) rename {src-tauri => app}/app-icon.png (100%) create mode 100644 app/build.rs rename {src-tauri => app}/icons/128x128.png (100%) rename {src-tauri => app}/icons/128x128@2x.png (100%) rename {src-tauri => app}/icons/32x32.png (100%) rename {src-tauri => app}/icons/64x64.png (100%) rename {src-tauri => app}/icons/Square107x107Logo.png (100%) rename {src-tauri => app}/icons/Square142x142Logo.png (100%) rename {src-tauri => app}/icons/Square150x150Logo.png (100%) rename {src-tauri => app}/icons/Square284x284Logo.png (100%) rename {src-tauri => app}/icons/Square30x30Logo.png (100%) rename {src-tauri => app}/icons/Square310x310Logo.png (100%) rename {src-tauri => app}/icons/Square44x44Logo.png (100%) rename {src-tauri => app}/icons/Square71x71Logo.png (100%) rename {src-tauri => app}/icons/Square89x89Logo.png (100%) rename {src-tauri => app}/icons/StoreLogo.png (100%) rename {src-tauri => app}/icons/icon.icns (100%) rename {src-tauri => app}/icons/icon.ico (100%) rename {src-tauri => app}/icons/icon.png (100%) create mode 100644 app/icons/icon.rc rename {src-tauri => app}/icons/ios/AppIcon-20x20@1x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-20x20@2x-1.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-20x20@2x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-20x20@3x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-29x29@1x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-29x29@2x-1.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-29x29@2x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-29x29@3x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-40x40@1x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-40x40@2x-1.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-40x40@2x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-40x40@3x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-512@2x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-60x60@2x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-60x60@3x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-76x76@1x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-76x76@2x.png (100%) rename {src-tauri => app}/icons/ios/AppIcon-83.5x83.5@2x.png (100%) rename {src-tauri => app}/macos/How to install.txt (100%) rename {src-tauri => app}/macos/Install Grimoire.command (100%) rename {src-tauri => app}/migrations/001_init.sql (100%) rename {src-tauri => app}/migrations/002_reading_progress.sql (100%) rename {src-tauri => app}/src/comic.rs (96%) rename {src-tauri => app}/src/config.rs (62%) rename {src-tauri => app}/src/convert.rs (100%) rename {src-tauri => app}/src/db.rs (100%) rename {src-tauri => app}/src/error.rs (100%) rename {src-tauri => app}/src/kf8_extract.rs (100%) rename {src-tauri => app}/src/kindle.rs (97%) create mode 100644 app/src/lib.rs rename {src-tauri => app}/src/library.rs (98%) rename {src-tauri => app}/src/main.rs (100%) rename {src-tauri => app}/src/metadata.rs (100%) rename {src-tauri => app}/src/openlibrary.rs (100%) rename {src-tauri => app}/src/reader.rs (100%) rename {src-tauri => app}/src/state.rs (100%) create mode 100644 app/src/ui/backend.rs create mode 100644 app/src/ui/book.rs create mode 100644 app/src/ui/devices.rs create mode 100644 app/src/ui/document.rs create mode 100644 app/src/ui/icon.rs create mode 100644 app/src/ui/library.rs create mode 100644 app/src/ui/mod.rs create mode 100644 app/src/ui/reader.rs create mode 100644 app/src/ui/settings.rs create mode 100644 app/src/ui/shell.rs create mode 100644 app/src/ui/theme.rs delete mode 100644 package.json delete mode 100644 pnpm-lock.yaml delete mode 100644 pnpm-workspace.yaml delete mode 100644 src-tauri/.gitignore delete mode 100644 src-tauri/build.rs delete mode 100644 src-tauri/capabilities/default.json delete mode 100644 src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 src-tauri/icons/android/mipmap-hdpi/ic_launcher.png delete mode 100644 src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png delete mode 100644 src-tauri/icons/android/mipmap-mdpi/ic_launcher.png delete mode 100644 src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png delete mode 100644 src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png delete mode 100644 src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png delete mode 100644 src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 src-tauri/icons/android/values/ic_launcher_background.xml delete mode 100644 src-tauri/package.json delete mode 100644 src-tauri/pnpm-lock.yaml delete mode 100644 src-tauri/src/device_watch.rs delete mode 100644 src-tauri/src/lib.rs delete mode 100644 src-tauri/tauri.conf.json delete mode 100644 src-tauri/windows/fragments/library-path.wxs delete mode 100644 src-tauri/windows/hooks.nsh delete mode 100644 src-tauri/windows/set-library-path.ps1 delete mode 100644 src/app.css delete mode 100644 src/app.html delete mode 100644 src/epubjs.d.ts delete mode 100644 src/lib/CoverPickerModal.svelte delete mode 100644 src/lib/LibraryFilters.svelte delete mode 100644 src/lib/ThemeToggle.svelte delete mode 100644 src/lib/WindowControls.svelte delete mode 100644 src/lib/api.ts delete mode 100644 src/lib/device-presence.svelte.ts delete mode 100644 src/lib/library-filters.ts delete mode 100644 src/lib/reader-prefs.test.ts delete mode 100644 src/lib/reader-prefs.ts delete mode 100644 src/lib/theme.test.ts delete mode 100644 src/lib/theme.ts delete mode 100644 src/lib/ui/ActionButton.svelte delete mode 100644 src/lib/ui/Button.svelte delete mode 100644 src/lib/ui/Checkbox.svelte delete mode 100644 src/lib/ui/Dialog.svelte delete mode 100644 src/lib/ui/SearchField.svelte delete mode 100644 src/lib/ui/TextField.svelte delete mode 100644 src/lib/view-mode.test.ts delete mode 100644 src/lib/view-mode.ts delete mode 100644 src/lib/window-chrome.ts delete mode 100644 src/lib/windows.test.ts delete mode 100644 src/lib/windows.ts delete mode 100644 src/routes/+layout.svelte delete mode 100644 src/routes/+layout.ts delete mode 100644 src/routes/+page.svelte delete mode 100644 src/routes/book/[id]/+page.svelte delete mode 100644 src/routes/devices/+page.svelte delete mode 100644 src/routes/devices/library/+page.svelte delete mode 100644 src/routes/read/[id]/+page.svelte delete mode 100644 src/routes/read/[id]/+page.ts delete mode 100644 src/routes/settings/+page.svelte delete mode 100644 static/favicon.svg delete mode 100644 static/svelte.svg delete mode 100644 static/tauri.svg delete mode 100644 static/vite.svg delete mode 100644 svelte.config.js delete mode 100644 tsconfig.json delete mode 100644 vite.config.js diff --git a/.env.example b/.env.example index 51169ee..cb6ad7a 100644 --- a/.env.example +++ b/.env.example @@ -11,7 +11,7 @@ # security find-identity -v -p codesigning APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)" -# Optional CI-style path: base64 .p12 + password (Tauri imports into a temp keychain). +# Optional CI-style path: base64 .p12 + password (the package script imports it into a temp keychain). # Leave blank when the cert is already in Keychain Access → My Certificates. # APPLE_CERTIFICATE= # APPLE_CERTIFICATE_PASSWORD= diff --git a/.github/scripts/bump-version.sh b/.github/scripts/bump-version.sh index 6bfb986..792d447 100755 --- a/.github/scripts/bump-version.sh +++ b/.github/scripts/bump-version.sh @@ -33,8 +33,8 @@ CURRENT="" if LATEST_TAG="$(git describe --tags --match 'v*' --abbrev=0 2>/dev/null || true)" \ && [[ -n "${LATEST_TAG}" ]]; then CURRENT="${LATEST_TAG#v}" -elif [[ -f package.json ]]; then - CURRENT="$(node -pe 'require("./package.json").version')" +elif [[ -f app/Cargo.toml ]]; then + CURRENT="$(sed -n 's/^version = "\(.*\)"/\1/p' app/Cargo.toml | head -1)" else CURRENT="0.0.0" fi @@ -66,28 +66,12 @@ esac VERSION="${MAJOR}.${MINOR}.${PATCH}" TAG="v${VERSION}" -node -e " -const fs = require('fs'); -const path = 'package.json'; -const pkg = JSON.parse(fs.readFileSync(path, 'utf8')); -pkg.version = process.argv[1]; -fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n'); -" "${VERSION}" - -node -e " -const fs = require('fs'); -const path = 'src-tauri/tauri.conf.json'; -const conf = JSON.parse(fs.readFileSync(path, 'utf8')); -conf.version = process.argv[1]; -fs.writeFileSync(path, JSON.stringify(conf, null, 2) + '\n'); -" "${VERSION}" - # Replace only the package version line in Cargo.toml. -perl -i -0pe 's/(?m)^(\[package\]\s*(?:(?!\[).)*?^version\s*=\s*)"[^"]*"/${1}"'"${VERSION}"'"/s' src-tauri/Cargo.toml +perl -i -0pe 's/(?m)^(\[package\]\s*(?:(?!\[).)*?^version\s*=\s*)"[^"]*"/${1}"'"${VERSION}"'"/s' app/Cargo.toml # Keep Cargo.lock package stanza in sync when present. -if [[ -f src-tauri/Cargo.lock ]]; then - perl -i -0pe 's/(name = "grimoire"\nversion = )"[^"]*"/${1}"'"${VERSION}"'"/' src-tauri/Cargo.lock +if [[ -f app/Cargo.lock ]]; then + perl -i -0pe 's/(name = "grimoire"\nversion = )"[^"]*"/${1}"'"${VERSION}"'"/' app/Cargo.lock fi echo "VERSION=${VERSION}" diff --git a/.github/scripts/inject-macos-dmg-installer.sh b/.github/scripts/inject-macos-dmg-installer.sh deleted file mode 100755 index 97d2fda..0000000 --- a/.github/scripts/inject-macos-dmg-installer.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -# Inject Gatekeeper installer files into built macOS DMGs. -# Usage: inject-macos-dmg-installer.sh [dmg-path...] -# With no args, finds DMGs under src-tauri/target/**/bundle/dmg/ -set -euo pipefail - -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" -INSTALLER_SRC="$ROOT/src-tauri/macos/Install Grimoire.command" -HOWTO_SRC="$ROOT/src-tauri/macos/How to install.txt" - -if [[ ! -f "$INSTALLER_SRC" ]]; then - echo "missing installer: $INSTALLER_SRC" >&2 - exit 1 -fi -if [[ ! -f "$HOWTO_SRC" ]]; then - echo "missing howto: $HOWTO_SRC" >&2 - exit 1 -fi - -if [[ $# -gt 0 ]]; then - DMGS=("$@") -else - # shellcheck disable=SC2207 - DMGS=($(find "$ROOT/src-tauri/target" -type f -path '*/bundle/dmg/*.dmg' ! -name 'rw.*.dmg' 2>/dev/null | sort -u)) -fi - -if [[ ${#DMGS[@]} -eq 0 ]]; then - echo "No DMGs found to patch." >&2 - exit 1 -fi - -inject_one() { - local dmg="$1" - local work rw mount staging out - - echo "Injecting installer into $(basename "$dmg")…" - - work="$(mktemp -d "${TMPDIR:-/tmp}/grimoire-dmg.XXXXXX")" - rw="$work/rw.dmg" - staging="$work/mount" - out="$work/out.dmg" - mkdir -p "$staging" - - # Writable copy with headroom for installer files. - hdiutil convert "$dmg" -format UDRW -o "$rw" >/dev/null - # Resize: current size + 2MB (hdiutil wants sector counts; -size accepts m). - local size_mb - size_mb="$(du -sm "$rw" | awk '{print $1 + 2}')" - hdiutil resize -size "${size_mb}m" "$rw" >/dev/null - - hdiutil attach "$rw" -readwrite -nobrowse -mountpoint "$staging" >/dev/null - - cp "$INSTALLER_SRC" "$staging/Install Grimoire.command" - chmod +x "$staging/Install Grimoire.command" - cp "$HOWTO_SRC" "$staging/How to install.txt" - - # Flush before detach. - sync - hdiutil detach "$staging" >/dev/null - - hdiutil convert "$rw" -format UDZO -imagekey zlib-level=9 -o "$out" >/dev/null - mv -f "$out" "$dmg" - rm -rf "$work" - echo " ok: $dmg" -} - -for dmg in "${DMGS[@]}"; do - if [[ ! -f "$dmg" ]]; then - echo "skip missing: $dmg" >&2 - continue - fi - inject_one "$dmg" -done diff --git a/.github/scripts/package-macos.sh b/.github/scripts/package-macos.sh new file mode 100755 index 0000000..928645c --- /dev/null +++ b/.github/scripts/package-macos.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# Build a Grimoire.app and a DMG from a release binary. +# Usage: package-macos.sh +# +# Signing: when APPLE_CERTIFICATE is set, import that .p12 and sign with the +# Developer ID Application identity inside it. Otherwise use +# APPLE_SIGNING_IDENTITY from the login keychain. Notarize when the App Store +# Connect API key variables are set. +set -euo pipefail + +BIN="${1:?path to the Grimoire binary}" +OUT="${2:?output directory}" +ARCH="${3:?arch label, such as macos_arm64}" + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' "$ROOT/app/Cargo.toml" | head -1)" +if [[ -z "${VERSION}" ]]; then + echo "Could not read version from app/Cargo.toml" >&2 + exit 1 +fi +if [[ ! -f "${BIN}" ]]; then + echo "Missing binary: ${BIN}" >&2 + exit 1 +fi + +STAGE="$(mktemp -d "${TMPDIR:-/tmp}/grimoire-app.XXXXXX")" +KEYCHAIN_PATH="" +PREVIOUS_KEYCHAINS="" + +cleanup() { + if [[ -n "${KEYCHAIN_PATH}" ]]; then + security delete-keychain "${KEYCHAIN_PATH}" >/dev/null 2>&1 || true + fi + if [[ -n "${PREVIOUS_KEYCHAINS}" ]]; then + # shellcheck disable=SC2086 + security list-keychains -d user -s ${PREVIOUS_KEYCHAINS} >/dev/null 2>&1 || true + fi + rm -rf "${STAGE}" +} +trap cleanup EXIT + +APP="${STAGE}/Grimoire.app" +mkdir -p "${APP}/Contents/MacOS" "${APP}/Contents/Resources" +cp "${BIN}" "${APP}/Contents/MacOS/Grimoire" +chmod +x "${APP}/Contents/MacOS/Grimoire" +cp "${ROOT}/app/icons/icon.icns" "${APP}/Contents/Resources/icon.icns" + +cat > "${APP}/Contents/Info.plist" < + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Grimoire + CFBundleExecutable + Grimoire + CFBundleIconFile + icon + CFBundleIdentifier + com.boxingoctopus.grimoire + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Grimoire + CFBundlePackageType + APPL + CFBundleShortVersionString + ${VERSION} + CFBundleVersion + ${VERSION} + LSMinimumSystemVersion + 12.0 + NSHighResolutionCapable + + + +EOF + +IDENTITY="" +if [[ -n "${APPLE_CERTIFICATE:-}" && -n "${APPLE_CERTIFICATE_PASSWORD:-}" ]]; then + KEYCHAIN_PATH="${STAGE}/build.keychain-db" + KEYCHAIN_PASSWORD="$(openssl rand -base64 32)" + CERT_PATH="${STAGE}/certificate.p12" + printf '%s' "${APPLE_CERTIFICATE}" | base64 -D > "${CERT_PATH}" + PREVIOUS_KEYCHAINS="$(security list-keychains -d user | tr -d '"' | tr '\n' ' ')" + security create-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" + security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}" + security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" + security import "${CERT_PATH}" \ + -k "${KEYCHAIN_PATH}" \ + -P "${APPLE_CERTIFICATE_PASSWORD}" \ + -A -t cert -f pkcs12 \ + -T /usr/bin/codesign \ + -T /usr/bin/security + security set-key-partition-list \ + -S apple-tool:,apple:,codesign: \ + -s -k "${KEYCHAIN_PASSWORD}" \ + "${KEYCHAIN_PATH}" + security list-keychains -d user -s "${KEYCHAIN_PATH}" ${PREVIOUS_KEYCHAINS} + IDENTITY="$(security find-identity -v -p codesigning "${KEYCHAIN_PATH}" | awk -F'"' '/Developer ID Application/{print $2; exit}')" +elif [[ -n "${APPLE_SIGNING_IDENTITY:-}" ]]; then + IDENTITY="${APPLE_SIGNING_IDENTITY}" +fi + +if [[ "${CI:-}" == "true" && -z "${IDENTITY}" ]]; then + echo "Could not find a Developer ID Application identity in APPLE_CERTIFICATE." >&2 + exit 1 +fi + +if [[ -n "${IDENTITY}" ]]; then + echo "Signing with ${IDENTITY}" + codesign --force --options runtime --timestamp --sign "${IDENTITY}" "${APP}/Contents/MacOS/Grimoire" + codesign --force --options runtime --timestamp --sign "${IDENTITY}" "${APP}" +fi + +DMG_ROOT="${STAGE}/dmg" +mkdir -p "${DMG_ROOT}" +cp -R "${APP}" "${DMG_ROOT}/" +ln -s /Applications "${DMG_ROOT}/Applications" +if [[ -z "${IDENTITY}" ]]; then + cp "${ROOT}/app/macos/Install Grimoire.command" "${DMG_ROOT}/Install Grimoire.command" + chmod +x "${DMG_ROOT}/Install Grimoire.command" + cp "${ROOT}/app/macos/How to install.txt" "${DMG_ROOT}/How to install.txt" +fi + +mkdir -p "${OUT}" +DMG="${OUT}/Grimoire_${VERSION}_${ARCH}.dmg" +rm -f "${DMG}" +hdiutil create -volname Grimoire -srcfolder "${DMG_ROOT}" -ov -format UDZO "${DMG}" >/dev/null + +if [[ -n "${IDENTITY}" ]]; then + codesign --force --timestamp --sign "${IDENTITY}" "${DMG}" +fi + +if [[ -n "${IDENTITY}" && -n "${APPLE_API_KEY:-}" && -n "${APPLE_API_ISSUER:-}" && -n "${APPLE_API_KEY_PATH:-}" ]]; then + echo "Notarizing ${DMG}" + xcrun notarytool submit "${DMG}" \ + --key "${APPLE_API_KEY_PATH}" \ + --key-id "${APPLE_API_KEY}" \ + --issuer "${APPLE_API_ISSUER}" \ + --wait + xcrun stapler staple "${DMG}" +fi + +echo "Wrote ${DMG}" diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 1eca700..010e79d 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -42,26 +42,16 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # Wayland, xkbcommon, and fontconfig are what the GPUI window links. - name: Install Linux build deps run: | sudo apt-get update sudo apt-get install -y \ - libwebkit2gtk-4.1-dev \ - libappindicator3-dev \ - librsvg2-dev \ - patchelf \ - xdg-utils - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: pnpm + pkg-config \ + libwayland-dev \ + libxkbcommon-dev \ + libxkbcommon-x11-dev \ + libfontconfig1-dev - name: Setup Rust uses: dtolnay/rust-toolchain@stable @@ -69,16 +59,10 @@ jobs: - name: Cache Rust uses: swatinem/rust-cache@v2 with: - workspaces: './src-tauri -> target' - - - name: Install frontend dependencies - run: pnpm install --frozen-lockfile - - - name: Frontend tests - run: pnpm test + workspaces: './app -> target' - name: Rust tests - run: cargo test --manifest-path src-tauri/Cargo.toml + run: cargo test --manifest-path app/Cargo.toml version-and-release: name: Tag and release @@ -156,7 +140,7 @@ jobs: echo "Missing VERSION or TAG from bump step" >&2 exit 1 fi - git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml src-tauri/Cargo.lock + git add app/Cargo.toml app/Cargo.lock git commit -m "chore: release ${TAG}" git tag -a "${TAG}" -m "Release ${TAG}" git push origin "HEAD:${BASE_REF}" @@ -216,20 +200,24 @@ jobs: include: - platform: macos-latest label: macOS arm64 - args: --target aarch64-apple-darwin + cargo_args: --target aarch64-apple-darwin rust_targets: aarch64-apple-darwin + archive_suffix: macos_arm64 - platform: macos-latest label: macOS x64 - args: --target x86_64-apple-darwin + cargo_args: --target x86_64-apple-darwin rust_targets: x86_64-apple-darwin + archive_suffix: macos_x64 - platform: ubuntu-22.04 label: Linux x64 - args: '' + cargo_args: '' rust_targets: '' + archive_suffix: linux_x64 - platform: windows-latest label: Windows x64 - args: --bundles nsis,msi + cargo_args: '' rust_targets: '' + archive_suffix: windows_x64 runs-on: ${{ matrix.platform }} steps: - name: Checkout release tag @@ -237,27 +225,17 @@ jobs: with: ref: ${{ needs.version-and-release.outputs.tag }} + # Wayland, xkbcommon, and fontconfig are what the GPUI window links. - name: Install Linux build deps if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update sudo apt-get install -y \ - libwebkit2gtk-4.1-dev \ - libappindicator3-dev \ - librsvg2-dev \ - patchelf \ - xdg-utils - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: pnpm + pkg-config \ + libwayland-dev \ + libxkbcommon-dev \ + libxkbcommon-x11-dev \ + libfontconfig1-dev - name: Setup Rust uses: dtolnay/rust-toolchain@stable @@ -267,19 +245,15 @@ jobs: - name: Cache Rust uses: swatinem/rust-cache@v2 with: - workspaces: './src-tauri -> target' - - - name: Install frontend dependencies - run: pnpm install --frozen-lockfile + workspaces: './app -> target' # Developer ID cert + App Store Connect API key for signing and notarization. - # Do not inject files into the DMG after this; that would break the notarization ticket. + # APPLE_SIGNING_IDENTITY is optional: when omitted, the package script uses the identity inside APPLE_CERTIFICATE. - name: Prepare Apple signing and notarization if: matrix.platform == 'macos-latest' env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} APPLE_API_KEY_CONTENTS: ${{ secrets.APPLE_API_KEY_CONTENTS }} @@ -289,7 +263,6 @@ jobs: for name in \ APPLE_CERTIFICATE \ APPLE_CERTIFICATE_PASSWORD \ - APPLE_SIGNING_IDENTITY \ APPLE_API_KEY \ APPLE_API_ISSUER \ APPLE_API_KEY_CONTENTS @@ -308,51 +281,56 @@ jobs: echo "APPLE_API_KEY_PATH=${KEY_PATH}" >> "$GITHUB_ENV" echo "Wrote App Store Connect API key to ${KEY_PATH}" - - name: Build and upload to draft release - if: matrix.platform != 'macos-latest' - uses: tauri-apps/tauri-action@v1 - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} - with: - tagName: ${{ needs.version-and-release.outputs.tag }} - releaseId: ${{ needs.version-and-release.outputs.release_id }} - releaseDraft: true - prerelease: false - args: ${{ matrix.args }} + - name: Build release binary + run: cargo build --manifest-path app/Cargo.toml --release --bin Grimoire ${{ matrix.cargo_args }} - - name: Build macOS bundles - id: build_mac + - name: Package macOS disk image if: matrix.platform == 'macos-latest' - uses: tauri-apps/tauri-action@v1 env: - GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} APPLE_API_KEY_PATH: ${{ env.APPLE_API_KEY_PATH }} - with: - args: ${{ matrix.args }} + run: | + set -euo pipefail + chmod +x .github/scripts/package-macos.sh + .github/scripts/package-macos.sh \ + "app/target/${{ matrix.rust_targets }}/release/Grimoire" \ + dist \ + "${{ matrix.archive_suffix }}" - - name: Upload macOS release assets - if: matrix.platform == 'macos-latest' + - name: Package Linux binary + if: matrix.platform == 'ubuntu-22.04' + run: | + set -euo pipefail + VERSION="${{ needs.version-and-release.outputs.version }}" + mkdir -p dist + tar -C app/target/release -czf "dist/Grimoire_${VERSION}_${{ matrix.archive_suffix }}.tar.gz" Grimoire + + - name: Package Windows binary + if: matrix.platform == 'windows-latest' + shell: bash + run: | + set -euo pipefail + VERSION="${{ needs.version-and-release.outputs.version }}" + mkdir -p dist + tar -a -C app/target/release -cf "dist/Grimoire_${VERSION}_${{ matrix.archive_suffix }}.zip" Grimoire.exe + + - name: Upload release asset + shell: bash env: GH_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }} GH_REPO: ${{ github.repository }} TAG: ${{ needs.version-and-release.outputs.tag }} run: | set -euo pipefail - # bash 3.2 on macOS has no mapfile; use a while-read loop instead. ASSETS=() while IFS= read -r asset; do ASSETS+=("$asset") - done < <(find src-tauri/target -type f \( \ - -path '*/bundle/dmg/*.dmg' -o \ - -path '*/bundle/macos/*.tar.gz' \ - \) ! -name 'rw.*.dmg' | sort -u) + done < <(find dist -type f | sort -u) if [[ ${#ASSETS[@]} -eq 0 ]]; then - echo "No macOS bundle artifacts found to upload" >&2 + echo "No release artifacts found to upload" >&2 exit 1 fi printf 'Uploading:\n' diff --git a/.gitignore b/.gitignore index 502f110..b689662 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* -src-tauri/target +app/target +/dist *.log diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 61343e9..b3c9d51 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,5 @@ { "recommendations": [ - "svelte.svelte-vscode", - "tauri-apps.tauri-vscode", "rust-lang.rust-analyzer" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 2f86c50..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "svelte.enable-ts-plugin": true -} diff --git a/Makefile b/Makefile index 0c20b46..80df8f9 100644 --- a/Makefile +++ b/Makefile @@ -1,212 +1,120 @@ -# Grimoire production builds (Tauri 2 release bundles) +# Grimoire release builds. # # Usage: # make help -# make build # current host -# make build-mac # macOS (universal by default) -# make build-windows # Windows x64 (NSIS when cross-compiling) -# make build-linux # Linux x64 -# -# Bundles land under src-tauri/target//release/bundle/ -# (or src-tauri/target/release/bundle/ for a native host build). +# make build # release binary for this machine +# make build-mac # macOS .app and DMG (loads .env for signing) +# make build-windows # Windows x64, on Windows +# make build-linux # Linux x64, on Linux -PNPM ?= pnpm -TAURI ?= $(PNPM) exec tauri -CARGO_XWIN ?= cargo-xwin -RUSTUP ?= rustup +CARGO ?= cargo HOST_OS := $(shell uname -s 2>/dev/null || echo unknown) HOST_ARCH := $(shell uname -m 2>/dev/null || echo unknown) -# True on native Windows (cmd/PowerShell make) or MSYS/MinGW shells. IS_WINDOWS := $(if $(filter Windows_NT,$(OS)),1,$(if $(findstring MINGW,$(HOST_OS)),1,$(if $(findstring MSYS,$(HOST_OS)),1,$(if $(findstring CYGWIN,$(HOST_OS)),1,)))) -# Production flags: release mode (tauri build default) and non-interactive CI mode. -TAURI_BUILD_FLAGS ?= --ci - # Optional env file for local Apple signing / notarization (see .env.example). -# Usage: make build-mac or make build-mac ENV_FILE=.env.apple ENV_FILE ?= .env -# Platform triples -MAC_TARGET ?= universal-apple-darwin MAC_ARM_TARGET ?= aarch64-apple-darwin MAC_INTEL_TARGET ?= x86_64-apple-darwin WIN_TARGET ?= x86_64-pc-windows-msvc LINUX_TARGET ?= x86_64-unknown-linux-gnu -LINUX_ARM_TARGET ?= aarch64-unknown-linux-gnu -.PHONY: help install build test test-frontend test-rust \ +ifeq ($(HOST_ARCH),x86_64) +MAC_ARCH_LABEL := macos_x64 +else +MAC_ARCH_LABEL := macos_arm64 +endif + +.PHONY: help build test test-rust \ build-mac build-mac-arm build-mac-intel \ - build-windows build-linux build-linux-arm \ - ensure-frontend ensure-mac-targets ensure-windows-target ensure-linux-target \ - ensure-cargo-xwin clean-dmg-mounts clean + build-windows build-linux \ + clean .DEFAULT_GOAL := help help: - @echo "Grimoire production builds" + @echo "Grimoire release builds" @echo "" - @echo " make build Build for the current host ($(HOST_OS)/$(HOST_ARCH))" - @echo " make build-mac macOS universal (arm64 + x86_64)" - @echo " make build-mac-arm macOS Apple Silicon only" - @echo " make build-mac-intel macOS Intel only" - @echo " make build-windows Windows x64" - @echo " make build-linux Linux x64" - @echo " make build-linux-arm Linux arm64" - @echo " make install Install frontend deps and common Rust targets" - @echo " make test Run frontend + Rust automated tests" - @echo " make test-frontend Run Vitest suite" - @echo " make test-rust Run cargo test in src-tauri" - @echo " make clean Remove frontend and Rust build outputs" - @echo " make clean-dmg-mounts Unmount leftover Tauri DMG staging volumes (macOS)" + @echo " make build Release binary for this machine ($(HOST_OS)/$(HOST_ARCH))" + @echo " make build-mac macOS disk image for this machine" + @echo " make build-mac-arm macOS Apple Silicon disk image" + @echo " make build-mac-intel macOS Intel disk image" + @echo " make build-windows Windows x64 executable" + @echo " make build-linux Linux x64 executable" + @echo " make test Run cargo test" + @echo " make clean Remove Rust build outputs and dist/" @echo "" @echo "Apple signing (macOS): copy .env.example to .env and fill values, then" @echo " make build-mac" @echo "Or: make build-mac ENV_FILE=/path/to/your.env" - @echo "When APPLE_SIGNING_IDENTITY or APPLE_CERTIFICATE is set, the Gatekeeper" - @echo "DMG inject is skipped so notarization stays valid." + @echo "CI reads the signing identity from APPLE_CERTIFICATE. For local Keychain" + @echo "signing, set APPLE_SIGNING_IDENTITY in .env." @echo "" - @echo "Run each platform target on that OS when possible. Windows can be" - @echo "cross-built from macOS/Linux with cargo-xwin + NSIS + LLVM." - @echo "Linux GUI deps (WebKitGTK, etc.) require a Linux host or matching sysroot." - -install: ensure-frontend - @$(RUSTUP) target add \ - $(MAC_ARM_TARGET) \ - $(MAC_INTEL_TARGET) \ - $(WIN_TARGET) \ - $(LINUX_TARGET) \ - $(LINUX_ARM_TARGET) || true + @echo "Linux builds need Wayland, xkbcommon, and fontconfig." -ensure-frontend: - $(PNPM) install - -# --- tests ------------------------------------------------------------------- - -test: test-frontend test-rust - -test-frontend: - $(PNPM) test +test: test-rust test-rust: - cargo test --manifest-path src-tauri/Cargo.toml - -ensure-mac-targets: - @$(RUSTUP) target add $(MAC_ARM_TARGET) $(MAC_INTEL_TARGET) - -ensure-windows-target: - @$(RUSTUP) target add $(WIN_TARGET) - -ensure-linux-target: - @$(RUSTUP) target add $(LINUX_TARGET) + $(CARGO) test --manifest-path app/Cargo.toml -ensure-cargo-xwin: - @command -v $(CARGO_XWIN) >/dev/null 2>&1 || cargo install cargo-xwin --locked +build: + $(CARGO) build --manifest-path app/Cargo.toml --release --bin Grimoire -# Stale create-dmg mounts (/Volumes/dmg.*) break subsequent `tauri build` DMG steps. -clean-dmg-mounts: +build-mac: ifeq ($(HOST_OS),Darwin) - @for vol in /Volumes/dmg.* /Volumes/Grimoire*; do \ - if [ -e "$$vol" ]; then \ - echo "Detaching $$vol"; \ - hdiutil detach "$$vol" -force >/dev/null 2>&1 || true; \ - fi; \ - done - @find src-tauri/target -maxdepth 5 -name 'rw.*.dmg' -delete 2>/dev/null || true -else - @echo "clean-dmg-mounts is only needed on macOS" -endif - -# Load ENV_FILE (if present), run tauri build, then inject Gatekeeper helpers only -# when Apple signing env is absent (injecting after notarize breaks the ticket). -define tauri-build-macos - set -euo pipefail; \ - if [ -f "$(ENV_FILE)" ]; then \ - echo "Loading $(ENV_FILE)"; \ - set -a; \ - . "./$(ENV_FILE)"; \ - set +a; \ - fi; \ - $(TAURI) build $(TAURI_BUILD_FLAGS) $(1); \ - if [ -n "$${APPLE_SIGNING_IDENTITY:-}" ] || [ -n "$${APPLE_CERTIFICATE:-}" ]; then \ - echo "Skipping Gatekeeper DMG inject (Apple signing env is set)"; \ - else \ - .github/scripts/inject-macos-dmg-installer.sh; \ - fi -endef - -# Native production build for the machine you are on. -build: ensure-frontend clean-dmg-mounts -ifeq ($(HOST_OS),Darwin) - $(call tauri-build-macos,) -else - $(TAURI) build $(TAURI_BUILD_FLAGS) -endif - -# --- macOS ------------------------------------------------------------------- - -build-mac: ensure-frontend ensure-mac-targets clean-dmg-mounts -ifeq ($(HOST_OS),Darwin) - $(call tauri-build-macos,--target $(MAC_TARGET)) + $(CARGO) build --manifest-path app/Cargo.toml --release --bin Grimoire + $(call sign-and-package,app/target/release/Grimoire,$(MAC_ARCH_LABEL)) else $(error macOS builds require a Darwin host (got $(HOST_OS))) endif -build-mac-arm: ensure-frontend clean-dmg-mounts +build-mac-arm: ifeq ($(HOST_OS),Darwin) - @$(RUSTUP) target add $(MAC_ARM_TARGET) - $(call tauri-build-macos,--target $(MAC_ARM_TARGET)) + rustup target add $(MAC_ARM_TARGET) + $(CARGO) build --manifest-path app/Cargo.toml --release --bin Grimoire --target $(MAC_ARM_TARGET) + $(call sign-and-package,app/target/$(MAC_ARM_TARGET)/release/Grimoire,macos_arm64) else $(error macOS builds require a Darwin host (got $(HOST_OS))) endif -build-mac-intel: ensure-frontend clean-dmg-mounts +build-mac-intel: ifeq ($(HOST_OS),Darwin) - @$(RUSTUP) target add $(MAC_INTEL_TARGET) - $(call tauri-build-macos,--target $(MAC_INTEL_TARGET)) + rustup target add $(MAC_INTEL_TARGET) + $(CARGO) build --manifest-path app/Cargo.toml --release --bin Grimoire --target $(MAC_INTEL_TARGET) + $(call sign-and-package,app/target/$(MAC_INTEL_TARGET)/release/Grimoire,macos_x64) else $(error macOS builds require a Darwin host (got $(HOST_OS))) endif -# --- Windows ----------------------------------------------------------------- - -# Native Windows builds produce both the NSIS setup.exe and the WiX MSI. -# Cross-builds from macOS/Linux can only produce NSIS (MSI needs WiX on Windows). -WIN_BUNDLES ?= nsis,msi - -build-windows: ensure-frontend ensure-windows-target +build-windows: ifeq ($(IS_WINDOWS),1) - $(TAURI) build $(TAURI_BUILD_FLAGS) --target $(WIN_TARGET) --bundles $(WIN_BUNDLES) -else ifeq ($(HOST_OS),Darwin) - @$(MAKE) ensure-cargo-xwin - $(TAURI) build $(TAURI_BUILD_FLAGS) --runner $(CARGO_XWIN) --target $(WIN_TARGET) --bundles nsis -else ifeq ($(HOST_OS),Linux) - @$(MAKE) ensure-cargo-xwin - $(TAURI) build $(TAURI_BUILD_FLAGS) --runner $(CARGO_XWIN) --target $(WIN_TARGET) --bundles nsis + $(CARGO) build --manifest-path app/Cargo.toml --release --bin Grimoire --target $(WIN_TARGET) else - $(error Unsupported host for Windows builds: $(HOST_OS)) + $(error Windows builds require a Windows host (got $(HOST_OS))) endif -# --- Linux ------------------------------------------------------------------- - -build-linux: ensure-frontend ensure-linux-target +build-linux: ifeq ($(HOST_OS),Linux) - $(TAURI) build $(TAURI_BUILD_FLAGS) --target $(LINUX_TARGET) + $(CARGO) build --manifest-path app/Cargo.toml --release --bin Grimoire --target $(LINUX_TARGET) else - $(error Linux builds require a Linux host with WebKitGTK and related deps (got $(HOST_OS))) + $(error Linux builds require a Linux host (got $(HOST_OS))) endif -build-linux-arm: ensure-frontend -ifeq ($(HOST_OS),Linux) - @$(RUSTUP) target add $(LINUX_ARM_TARGET) - $(TAURI) build $(TAURI_BUILD_FLAGS) --target $(LINUX_ARM_TARGET) -else - $(error Linux builds require a Linux host with WebKitGTK and related deps (got $(HOST_OS))) -endif - -# --- cleanup ----------------------------------------------------------------- - clean: - rm -rf build .svelte-kit - cd src-tauri && cargo clean + cd app && $(CARGO) clean + rm -rf dist + +define sign-and-package + set -euo pipefail; \ + if [ -f "$(ENV_FILE)" ]; then \ + echo "Loading $(ENV_FILE)"; \ + set -a; \ + . "./$(ENV_FILE)"; \ + set +a; \ + fi; \ + chmod +x .github/scripts/package-macos.sh; \ + .github/scripts/package-macos.sh "$(1)" dist "$(2)" +endef diff --git a/README.md b/README.md index a716d1d..28a81d3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Grimoire -Cross-platform desktop eBook library manager built with [Tauri 2](https://v2.tauri.app/) and [SvelteKit](https://svelte.dev/). Local SQLite catalog, Calibre-style on-disk library layout, and USB/MTP Kindle sync (including Kindle Colorsoft). +Cross-platform desktop eBook library manager. The window is a native [GPUI](https://gpui-kit.com/) app. The catalog is a local SQLite database with a Calibre-style layout on disk, plus USB/MTP sync for Kindles and other eReaders. ## Features (v0.1) @@ -18,46 +18,41 @@ Cross-platform desktop eBook library manager built with [Tauri 2](https://v2.tau ## Requirements -- Node.js 20+ -- pnpm -- Rust toolchain (for Tauri) -- Platform Tauri dependencies ([guide](https://v2.tauri.app/start/prerequisites/)) +- Rust toolchain ## Develop ```bash -pnpm install -pnpm tauri dev +cargo run --manifest-path app/Cargo.toml --bin Grimoire ``` ## Build ```bash -pnpm tauri build +cargo build --manifest-path app/Cargo.toml --release --bin Grimoire ``` -Or use the Makefile for production platform builds: +Or use the Makefile: ```bash -make build # current host -make build-mac # macOS universal (loads .env for Apple signing if present) -make build-windows # Windows x64 +make build # release binary for this machine +make build-mac # macOS disk image (loads .env for Apple signing if present) +make build-windows # Windows x64 (Windows host) make build-linux # Linux x64 (Linux host) ``` For a signed and notarized local macOS build, copy `.env.example` to `.env`, fill in the Apple values, then run `make build-mac`. See that file for variable notes. -Run `make help` for the full target list. Artifacts are under `src-tauri/target/.../release/bundle/`. +Run `make help` for the full target list. Disk images land in `dist/`. The release binary is under `app/target/release/`. ## Test ```bash -make test # Vitest + cargo test -make test-frontend # frontend unit tests -make test-rust # Rust unit/integration tests +make test +cargo test --manifest-path app/Cargo.toml ``` -Or: `pnpm test`, `pnpm test:rust`, `pnpm test:all`. +The tests cover the catalog, conversion, Kindle detection, and the GPUI theme and reader-font choices. ## Release pipeline (GitHub Actions) @@ -69,7 +64,7 @@ Every pull request runs the test suite. When a PR is **merged**, or when you run - Title starts with `MINOR` → minor bump (`1.2.3` → `1.3.0`) - Title starts with `PATCH`, or anything else → patch bump (`1.2.3` → `1.2.4`) 3. Creates a draft `vX.Y.Z` GitHub Release (notes from commit subjects since the previous tag) -4. Builds production bundles for macOS (arm64 + x64, signed and notarized), Linux x64, and Windows x64 (NSIS setup.exe **and** MSI), then attaches them to that draft +4. Builds release binaries for macOS (arm64 + x64 disk images, signed and notarized), Linux x64, and Windows x64, then attaches them to that draft 5. Publishes the release (immutable releases lock assets only after publish) Example titles: `MINOR add dark mode default`, `MAJOR redesign library schema`, `add filter sidebar` (patch). @@ -94,21 +89,17 @@ macOS release jobs sign with a **Developer ID Application** certificate and nota | --- | --- | | `APPLE_CERTIFICATE` | Base64-encoded `.p12` (`openssl base64 -A -in cert.p12 -out cert-base64.txt`) | | `APPLE_CERTIFICATE_PASSWORD` | Password used when exporting the `.p12` | -| `APPLE_SIGNING_IDENTITY` | Full identity string from `security find-identity -v -p codesigning` | | `APPLE_API_KEY` | App Store Connect API Key ID | | `APPLE_API_ISSUER` | App Store Connect Issuer ID | | `APPLE_API_KEY_CONTENTS` | Full contents of the downloaded `.p8` private key | -See [Tauri macOS code signing](https://v2.tauri.app/distribute/sign/macos/) for creating the certificate and API key. Signed and notarized DMGs open normally; drag Grimoire into Applications. +CI reads the signing identity from `APPLE_CERTIFICATE`, so do not set `APPLE_SIGNING_IDENTITY` in Actions (a mismatch fails the build). For local Keychain signing, set `APPLE_SIGNING_IDENTITY` in `.env` instead. -### Windows installers and library path +Create the Developer ID certificate and the App Store Connect API key in [Apple Developer](https://developer.apple.com/account) and [App Store Connect](https://appstoreconnect.apple.com/access/integrations/api). Signed and notarized DMGs open normally; drag Grimoire into Applications. -Windows releases include both: - -- **NSIS** (`*-setup.exe`): interactive installs ask for the ebook library folder (default `Documents\Grimoire`). Silent/passive installs use that default, or `/LIBRARYPATH=D:\Books`. -- **MSI** (WiX): seeds the same default library path. Override with `msiexec /i Grimoire_*.msi LIBRARYPATH="D:\Books"`. +### Windows -Both write `%APPDATA%\grimoire\config.json` only when that file does not already exist, so upgrades keep an existing library path. +Windows releases are the `Grimoire.exe` binary. Choose the library folder in Settings. Grimoire writes `%APPDATA%\grimoire\config.json` and keeps that library path on later launches. ## Library layout @@ -158,10 +149,7 @@ Other USB devices that identify as an eReader or ebook device may appear as a ge ### Device detection -The Devices page keeps watching while the app is open: - -- **MTP hotplug:** connect/disconnect shows a toast; Devices and Send update quietly (no forced navigation) -- **Mounted volumes:** polled every few seconds for Kobo (`.kobo`), Kindle (`documents` + `system`), reMarkable markers, and volume names that match known brands +The Devices page checks for connected readers every few seconds. A notice appears when a reader connects or disconnects. Detection covers USB MTP devices and mounted volumes (Kobo, Kindle, reMarkable, and volume names that match known brands). ### Device library @@ -193,9 +181,9 @@ Kindles usually appear through the Windows Portable Devices stack with no extra ## Project layout -- `src/` - SvelteKit SPA UI -- `src-tauri/` - Rust backend (SQLite, import, conversion, MTP) -- `src-tauri/migrations/` - SQL schema migrations +- `app/` - Rust app (GPUI window, SQLite catalog, import, conversion, MTP) +- `app/migrations/` - SQL schema migrations +- `static/` - in-app icon artwork ## License diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..aba21e2 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ diff --git a/src-tauri/Cargo.lock b/app/Cargo.lock similarity index 61% rename from src-tauri/Cargo.lock rename to app/Cargo.lock index 5eb14c0..dac0a50 100644 --- a/src-tauri/Cargo.lock +++ b/app/Cargo.lock @@ -2,19 +2,120 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "accesskit" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b7f7f85a7e5f68090000ed7622545829afd484d210358702ae4cb97dd0c320" +dependencies = [ + "enumn", + "uuid", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "023da0e5097f46df7092d5280b02efb9bbf8d93298daeced42652463e357d636" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "phf 0.13.1", + "serde", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d10a236f96f87d70732e44520046785431ef01d5bcd6b041317bfadd2f88245" +dependencies = [ + "accesskit", + "hashbrown 0.16.1", +] + +[[package]] +name = "accesskit_macos" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce02dc63b43f0c9296af9ac946312a2dc8814427d7a64d2d600971dac55b6076" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "objc2 0.5.3", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "accesskit_unix" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e156ed3802e35eefe894ef2671bc6c889303d8a7e110b5e1b48f504b91362f" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "async-channel", + "async-executor", + "async-task", + "atspi", + "futures-lite", + "futures-util", + "serde", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106c2b961215864d1c2e703ee63269c25c4e80a577ffb2c1017b9c17dcdf83a1" +dependencies = [ + "accesskit", + "accesskit_consumer", + "hashbrown 0.16.1", + "static_assertions", + "windows 0.62.2", + "windows-core 0.62.2", +] + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures 0.2.17", + "zeroize", +] + [[package]] name = "aes" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35f0f96ce78e38c3dc6d8948aa8163d06385be74000f3c7a95bf1eef35d3ea32" dependencies = [ - "cipher", + "cipher 0.5.2", "cpubits", "cpufeatures 0.3.1", "zeroize", @@ -38,6 +139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "const-random", "getrandom 0.3.4", "once_cell", "version_check", @@ -72,27 +174,29 @@ dependencies = [ ] [[package]] -name = "alloc-no-stdlib" -version = "2.0.4" +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] -name = "alloc-stdlib" -version = "0.2.4" +name = "android_system_properties" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ - "alloc-no-stdlib", + "libc", ] [[package]] -name = "android_system_properties" -version = "0.1.6" +name = "annotate-snippets" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +checksum = "f211a51805bc641f3ad5b7664c77d2547af685cc33b4cd8d31964027a46f13f1" dependencies = [ - "libc", + "anstyle", + "memchr", + "unicode-width", ] [[package]] @@ -160,6 +264,15 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arc-swap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b" +dependencies = [ + "rustversion", +] + [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -171,12 +284,30 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + [[package]] name = "as-slice" version = "0.2.1" @@ -186,6 +317,55 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading 0.8.9", +] + +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.5", + "raw-window-handle", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + +[[package]] +name = "ashpd" +version = "0.13.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8421aaa9644a5faf26735f258b669b15f063313ef8f8e2bdb28912a1a6f111" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "getrandom 0.4.3", + "serde", + "serde_repr", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -210,6 +390,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee19bd99b43e3691acbad4e840420a4881cea6c0b66a208125a824f8fd53f5a1" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-io", + "pin-project-lite", +] + [[package]] name = "async-executor" version = "1.14.0" @@ -224,6 +416,17 @@ dependencies = [ "slab", ] +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + [[package]] name = "async-io" version = "2.6.0" @@ -253,6 +456,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + [[package]] name = "async-process" version = "2.5.0" @@ -318,33 +532,53 @@ dependencies = [ ] [[package]] -name = "atk" -version = "0.18.2" +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "atspi" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +checksum = "c77886257be21c9cd89a4ae7e64860c6f0eefca799bb79127913052bd0eefb3d" dependencies = [ - "atk-sys", - "glib", - "libc", + "atspi-common", + "atspi-proxies", ] [[package]] -name = "atk-sys" -version = "0.18.2" +name = "atspi-common" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +checksum = "20c5617155740c98003016429ad13fe43ce7a77b007479350a9f8bf95a29f63d" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", ] [[package]] -name = "atomic-waker" -version = "1.1.2" +name = "atspi-proxies" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +checksum = "2230e48787ed3eb4088996eab66a32ca20c0b67bbd4fd6cdfe79f04f1f04c9fc" +dependencies = [ + "atspi-common", + "serde", + "zbus", +] [[package]] name = "autocfg" @@ -381,7 +615,7 @@ dependencies = [ "anyhow", "arrayvec", "log", - "nom", + "nom 8.0.0", "num-rational", "v_frame", ] @@ -396,10 +630,25 @@ dependencies = [ ] [[package]] -name = "base64" -version = "0.21.7" +name = "backtrace" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.8.9", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + +[[package]] +name = "base62" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a647dce9631f152a99ec16cfc09267ae55fe280d5dc288964027d458076323" [[package]] name = "base64" @@ -422,20 +671,40 @@ dependencies = [ "simd-abstraction", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.13.2", + "cexpr", + "clang-sys", + "itertools 0.10.5", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.3", + "shlex 1.3.0", + "syn 2.0.119", +] + [[package]] name = "bit-set" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" [[package]] name = "bit_field" @@ -454,9 +723,6 @@ name = "bitflags" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" -dependencies = [ - "serde_core", -] [[package]] name = "bitstream-io" @@ -479,6 +745,12 @@ dependencies = [ "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" @@ -497,13 +769,31 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.3", +] + [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "objc2", + "objc2 0.6.4", ] [[package]] @@ -520,33 +810,13 @@ dependencies = [ ] [[package]] -name = "brotli" -version = "8.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "5.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bs58" -version = "0.5.1" +name = "bstr" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ - "tinyvec", + "memchr", + "serde_core", ] [[package]] @@ -588,6 +858,20 @@ name = "bytemuck" version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.5", +] [[package]] name = "byteorder" @@ -606,75 +890,65 @@ name = "bytes" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" -dependencies = [ - "serde", -] [[package]] -name = "cairo-rs" -version = "0.18.5" +name = "bzip2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" dependencies = [ - "bitflags 2.13.2", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror 1.0.69", + "libbz2-rs-sys", ] [[package]] -name = "cairo-sys-rs" -version = "0.18.2" +name = "calloop" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "bitflags 2.13.2", + "polling", + "rustix", + "tracing", ] [[package]] -name = "camino" -version = "1.2.5" +name = "calloop-wayland-source" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" +checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" dependencies = [ - "serde_core", + "calloop", + "rustix", + "wayland-backend", + "wayland-client", ] [[package]] -name = "cargo-platform" -version = "0.1.9" +name = "cbc" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" dependencies = [ - "serde", + "cipher 0.4.4", ] [[package]] -name = "cargo_metadata" -version = "0.19.2" +name = "cbindgen" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" dependencies = [ - "camino", - "cargo-platform", - "semver", + "heck 0.4.1", + "indexmap 2.14.2", + "log", + "proc-macro2", + "quote", "serde", "serde_json", - "thiserror 2.0.20", -] - -[[package]] -name = "cargo_toml" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" -dependencies = [ - "serde", - "toml 0.9.12+spec-1.1.0", + "syn 2.0.119", + "tempfile", + "toml 0.8.23", ] [[package]] @@ -686,34 +960,16 @@ dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", + "shlex 2.0.1", ] [[package]] -name = "cfg-expr" -version = "0.15.8" +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "smallvec", - "target-lexicon", + "nom 7.1.3", ] [[package]] @@ -728,6 +984,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "chacha20" version = "0.10.2" @@ -753,6 +1018,17 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.7", + "inout 0.1.4", + "zeroize", +] + [[package]] name = "cipher" version = "0.5.2" @@ -760,7 +1036,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "crypto-common 0.2.2", - "inout", + "inout 0.2.2", +] + +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", ] [[package]] @@ -809,6 +1096,76 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +[[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 0.1.2", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad36507aeb7e16159dfe68db81ccc27571c3ccd4b76fb2fb72fc59e7a4b1b64c" +dependencies = [ + "bitflags 2.13.2", + "block", + "cocoa-foundation 0.2.1", + "core-foundation 0.10.1", + "core-graphics 0.24.0", + "foreign-types", + "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 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" +dependencies = [ + "bitflags 2.13.2", + "block", + "core-foundation 0.10.1", + "core-graphics-types 0.2.0", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -822,15 +1179,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] -name = "combine" -version = "4.6.8" +name = "compression-codecs" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +checksum = "98fc98460ba0ad5317075d3632b8dfc45d0be8c4a49347c2a38272019717614a" dependencies = [ - "bytes", + "bzip2", + "compression-core", + "flate2", "memchr", ] +[[package]] +name = "compression-core" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8ccc4ea9f6acc32d102c0f6d471d11d913ad15f20c04de743374861fa1d414" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -840,12 +1205,42 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-oid" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + [[package]] name = "const-str" version = "1.1.0" @@ -862,13 +1257,22 @@ dependencies = [ ] [[package]] -name = "cookie" -version = "0.18.2" +name = "convert_case" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ - "time", - "version_check", + "unicode-segmentation", +] + +[[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]] @@ -889,17 +1293,54 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" -version = "0.25.0" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.10.1", + "core-graphics-types 0.2.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-helmer-fork" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" dependencies = [ "bitflags 2.13.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types", "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 0.9.4", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.2.0" @@ -907,8 +1348,86 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.13.2", - "core-foundation", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "core-graphics2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4416167a69126e617f8d0a214af0e3c1dbdeffcb100ddf72dcd1a1ac9893c146" +dependencies = [ + "bitflags 2.13.2", + "block", + "cfg-if", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "core-text" +version = "21.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" +dependencies = [ + "core-foundation 0.10.1", + "core-graphics 0.24.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-video" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139679cc63eb9504bdbe37e37874b0247136177655f0008588781e90863afa62" +dependencies = [ + "block", + "core-foundation 0.10.1", + "core-graphics2", + "io-surface", "libc", + "metal", +] + +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "cosmic-text" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" +dependencies = [ + "bitflags 2.13.2", + "fontdb", + "harfrust", + "linebender_resource_handle", + "log", + "rangemap", + "rustc-hash 2.1.3", + "self_cell", + "skrifa 0.40.0", + "smol_str", + "swash", + "sys-locale", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", ] [[package]] @@ -944,15 +1463,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-deque" version = "0.8.8" @@ -972,6 +1482,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.23" @@ -1060,20 +1579,14 @@ dependencies = [ [[package]] name = "ctor" -version = "0.8.0" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +checksum = "914a755b7c2d4af2bdcff7ce1739e2db9a1b81a9b07123d8015786ae03c0980d" dependencies = [ - "ctor-proc-macro", - "dtor", + "link-section", + "linktime-proc-macro", ] -[[package]] -name = "ctor-proc-macro" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" - [[package]] name = "ctutils" version = "0.4.2" @@ -1083,40 +1596,6 @@ dependencies = [ "cmov", ] -[[package]] -name = "darling" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 3.0.5", -] - -[[package]] -name = "darling_macro" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" -dependencies = [ - "darling_core", - "quote", - "syn 3.0.5", -] - [[package]] name = "dashmap" version = "5.5.3" @@ -1146,55 +1625,16 @@ dependencies = [ ] [[package]] -name = "dbus" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" -dependencies = [ - "libc", - "libdbus-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags 1.3.2", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" +name = "data-url" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.20", -] +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "deranged" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "serde_core", -] [[package]] name = "derive_arbitrary" @@ -1222,10 +1662,12 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", "syn 2.0.119", + "unicode-xid", ] [[package]] @@ -1236,6 +1678,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common 0.1.7", + "subtle", ] [[package]] @@ -1250,13 +1693,34 @@ dependencies = [ "ctutils", ] +[[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" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys", + "dirs-sys 0.5.0", +] + +[[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 0.4.6", + "windows-sys 0.48.0", ] [[package]] @@ -1267,10 +1731,16 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.5.2", "windows-sys 0.61.2", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + [[package]] name = "dispatch2" version = "0.3.1" @@ -1278,9 +1748,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ "bitflags 2.13.2", - "block2", + "block2 0.6.2", "libc", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -1295,51 +1765,28 @@ dependencies = [ ] [[package]] -name = "dlopen2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" -dependencies = [ - "dlopen2_derive", - "libc", - "once_cell", - "winapi", -] - -[[package]] -name = "dlopen2_derive" -version = "0.4.3" +name = "dlib" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", + "libloading 0.7.4", ] [[package]] -name = "dom_query" -version = "0.27.0" +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ - "bit-set", - "cssparser 0.36.0", - "foldhash", - "html5ever 0.38.0", - "precomputed-hash", - "selectors", - "tendril", + "litrs", ] [[package]] -name = "dpi" -version = "0.1.2" +name = "downcast-rs" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" -dependencies = [ - "serde", -] +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dtoa" @@ -1356,27 +1803,24 @@ dependencies = [ "dtoa", ] -[[package]] -name = "dtor" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dwrote" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b35532432acc8b19ceed096e35dfa088d3ea037fe4f3c085f1f97f33b4d02" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "dyn-clone" version = "1.0.20" @@ -1409,12 +1853,6 @@ dependencies = [ "winreg", ] -[[package]] -name = "embed_plist" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" - [[package]] name = "encoding" version = "0.2.33" @@ -1479,12 +1917,55 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "encoding_rs" +version = "0.8.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "encoding_rs_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" +dependencies = [ + "encoding_rs", +] + [[package]] name = "endi" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-iterator" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4549325971814bda7a44061bf3fe7e487d447cba01e4220a4b454d630d7a016" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -1506,6 +1987,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "equator" version = "0.4.2" @@ -1553,6 +2045,25 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "euclid" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" +dependencies = [ + "num-traits", +] + [[package]] name = "event-listener" version = "5.4.2" @@ -1607,6 +2118,9 @@ name = "fastrand" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +dependencies = [ + "getrandom 0.4.3", +] [[package]] name = "fax" @@ -1624,13 +2138,14 @@ dependencies = [ ] [[package]] -name = "field-offset" -version = "0.3.6" +name = "filedescriptor" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" dependencies = [ - "memoffset", - "rustc_version", + "libc", + "thiserror 1.0.69", + "winapi", ] [[package]] @@ -1639,6 +2154,12 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.10" @@ -1650,18 +2171,104 @@ dependencies = [ "zlib-rs", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "fastrand", + "futures-core", + "futures-sink", + "spin 0.9.9", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "font-types" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "font-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c92d9081c7ab3bd83ef1eecdfad090f3d966e3ce74106779664114cfe1a6274" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree 0.20.0", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1698,12 +2305,42 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[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.34" @@ -1729,6 +2366,19 @@ dependencies = [ "futures-sink", ] +[[package]] +name = "futures-concurrency" +version = "7.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" +dependencies = [ + "fixedbitset", + "futures-core", + "futures-lite", + "pin-project", + "smallvec", +] + [[package]] name = "futures-core" version = "0.3.34" @@ -1812,266 +2462,844 @@ dependencies = [ ] [[package]] -name = "gdk" -version = "0.18.2" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", + "typenum", + "version_check", ] [[package]] -name = "gdk-pixbuf" -version = "0.18.5" +name = "gethostname" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", + "rustix", + "windows-link 0.2.1", ] [[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" +name = "getopts" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", "libc", - "system-deps", + "wasi", + "wasm-bindgen", ] [[package]] -name = "gdk-sys" -version = "0.18.2" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", + "cfg-if", + "js-sys", "libc", - "pango-sys", - "pkg-config", - "system-deps", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", ] [[package]] -name = "gdkwayland-sys" -version = "0.18.2" +name = "getrandom" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", + "cfg-if", + "js-sys", "libc", - "pkg-config", - "system-deps", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "globset" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + +[[package]] +name = "glow" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror 2.0.20", + "windows 0.62.2", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.13.2", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "gpui-base" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf698677b253118459f552fe15513858c669d79a25837a94e480db2ae330050" +dependencies = [ + "aho-corasick", + "anyhow", + "ashpd 0.13.13", + "async-channel", + "chrono", + "data-url 0.3.2", + "futures", + "gpui-pre", + "gpui-pre-macros", + "gpui-pre-sum-tree", + "html5ever 0.27.0", + "instant", + "itertools 0.13.0", + "lsp-types", + "markdown", + "markup5ever_rcdom", + "num-traits", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", + "raw-window-handle", + "regex", + "ropey", + "schemars", + "serde", + "serde_json", + "smallvec", + "smol", + "tracing", + "unicode-linebreak", + "unicode-segmentation", + "web-time", + "windows 0.58.0", +] + +[[package]] +name = "gpui-component" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc8b7def8e74b0d822d3e96f6b28fb5a2c20eb7a83d30d22fa59ca8088d1159b" +dependencies = [ + "anyhow", + "chrono", + "core-text", + "enum-iterator", + "gpui-base", + "gpui-component-macros", + "gpui-kit-assets", + "gpui-pre", + "gpui-pre-macros", + "gpui-pre-sum-tree", + "instant", + "itertools 0.13.0", + "log", + "lsp-types", + "markdown", + "notify", + "num-traits", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", + "once_cell", + "paste", + "raw-window-handle", + "regex", + "resvg 0.45.1", + "ropey", + "rust-i18n", + "schemars", + "serde", + "serde_json", + "serde_repr", + "smallvec", + "smol", + "tracing", + "uuid", + "windows 0.58.0", +] + +[[package]] +name = "gpui-component-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2968cc7f4a2bd8f51b09cfe4f1e77409951b48a0e2d48f372b010e69c3d7fb49" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui-kit" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be18e0417095ebfea77ac25962961ff4e41a120ea6cb8f05a7505b682478b83e" +dependencies = [ + "gpui-base", + "gpui-component", + "gpui-kit-assets", + "gpui-pre", + "gpui-pre-platform", + "gpui-pre-web", +] + +[[package]] +name = "gpui-kit-assets" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6c481ca987f1e6f10b1aed4d47f1b301cd1dc6f3016fac93c305f2a4f307c0" +dependencies = [ + "anyhow", + "gpui-pre", + "gpui-pre-reqwest", + "log", + "rust-embed", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] -name = "gdkx11" -version = "0.18.2" +name = "gpui-pre" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +checksum = "d7f0bc3ebba070f8981b3f8e905a12f47d27794a30d029e59cdb9edf7a4b3360" dependencies = [ - "gdk", - "gdkx11-sys", - "gio", - "glib", + "accesskit", + "anyhow", + "async-channel", + "async-task", + "backtrace", + "bindgen", + "bitflags 2.13.2", + "chrono", + "core-video", + "ctor", + "derive_more", + "embed-resource", + "etagere", + "futures", + "futures-concurrency", + "getrandom 0.3.4", + "gpui-pre-collections", + "gpui-pre-http-client", + "gpui-pre-macros", + "gpui-pre-refineable", + "gpui-pre-scheduler", + "gpui-pre-shared-string", + "gpui-pre-sum-tree", + "gpui-pre-util", + "gpui-pre-util-macros", + "heapless", + "image", + "inventory", + "itertools 0.14.0", "libc", - "x11", + "log", + "lyon", + "num_cpus", + "objc2-core-foundation", + "objc2-core-video", + "parking", + "parking_lot", + "pin-project", + "pollster 0.4.0", + "postage", + "profiling", + "rand 0.9.5", + "raw-window-handle", + "regex", + "resvg 0.46.0", + "schemars", + "seahash", + "serde", + "serde_json", + "slotmap", + "smallvec", + "spin 0.10.1", + "strum", + "taffy", + "thiserror 2.0.20", + "ttf-parser", + "url", + "usvg 0.46.0", + "uuid", + "waker-fn", + "web-time", + "windows 0.62.2", + "zed-font-kit", + "zed-scap", ] [[package]] -name = "gdkx11-sys" -version = "0.18.2" +name = "gpui-pre-apple" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +checksum = "bfdd914311cd12894367c8b9908947bb0c27fd42f8b9a5ff7a0730551a83fa4c" dependencies = [ - "gdk-sys", - "glib-sys", + "anyhow", + "async-task", + "block2 0.6.2", + "cbindgen", + "core-foundation 0.10.1", + "core-graphics 0.24.0", + "core-video", + "derive_more", + "dispatch2", + "etagere", + "foreign-types", + "gpui-pre", + "gpui-pre-util", + "image", "libc", - "system-deps", - "x11", + "log", + "mach2", + "metal", + "objc2 0.6.4", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "parking_lot", ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "gpui-pre-collections" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "2c060863025dc3b8e90febad2d1f4ffd23a5d0dcedba39d3f6b7820ea2fdd3a0" dependencies = [ - "typenum", - "version_check", + "gpui-pre-util", + "indexmap 2.14.2", + "rustc-hash 2.1.3", +] + +[[package]] +name = "gpui-pre-derive-refineable" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc834f30f5f49791e214f1fde01a009c7de91c9cd0a9f31dabfed130ba39e6cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui-pre-http-client" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2935cf82333da3f41774e5757837756408f01356197b41c817ad2666034453" +dependencies = [ + "anyhow", + "async-compression", + "bytes", + "derive_more", + "futures", + "http", + "http-body", + "log", + "parking_lot", + "serde", + "serde_json", + "serde_urlencoded", + "url", +] + +[[package]] +name = "gpui-pre-linux" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1131746fad5c87b5de74dc9c73105655b0c820a2dd4bb247586573e7cf5720e" +dependencies = [ + "accesskit", + "accesskit_unix", + "anyhow", + "as-raw-xcb-connection", + "ashpd 0.13.13", + "bytemuck", + "calloop", + "calloop-wayland-source", + "filedescriptor", + "futures", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-http-client", + "gpui-pre-util", + "gpui-pre-wgpu", + "libc", + "log", + "notify-rust", + "oo7", + "open", + "parking_lot", + "raw-window-handle", + "smallvec", + "smol", + "strum", + "url", + "uuid", + "wayland-backend", + "wayland-client", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-plasma", + "wayland-protocols-wlr", + "x11rb", + "xkbcommon", + "zed-scap", + "zed-xim", +] + +[[package]] +name = "gpui-pre-macos" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d756813ff1cd741a502af60fd18869e0677795936d7349642b41c49f43120c0" +dependencies = [ + "accesskit", + "accesskit_macos", + "anyhow", + "block2 0.6.2", + "cocoa 0.26.1", + "core-foundation 0.10.1", + "core-foundation-sys", + "core-graphics 0.24.0", + "core-text", + "core-video", + "ctor", + "dispatch2", + "foreign-types", + "futures", + "gpui-pre", + "gpui-pre-apple", + "gpui-pre-collections", + "gpui-pre-scheduler", + "gpui-pre-util", + "image", + "itertools 0.14.0", + "log", + "objc", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-graphics", + "objc2-core-media", + "objc2-foundation 0.3.2", + "objc2-screen-capture-kit", + "objc2-user-notifications", + "parking_lot", + "pathfinder_geometry", + "raw-window-handle", + "semver", + "smallvec", + "strum", + "uuid", + "zed-font-kit", +] + +[[package]] +name = "gpui-pre-macros" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ad26ca759364694cb55487bebff4c1b7dd6accab49990ff3cda6f7a818a9f4" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui-pre-perf" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcb288a506a476b6cbf97e6ec1cc0118db43f0ca91087588afa1c2d8cd4f842" +dependencies = [ + "gpui-pre-collections", + "serde", + "serde_json", +] + +[[package]] +name = "gpui-pre-platform" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e200b7b78836f1a0fc70767d971768e88f29d8b91b697285c15fb8e373635a3e" +dependencies = [ + "anyhow", + "console_error_panic_hook", + "gpui-pre", + "gpui-pre-linux", + "gpui-pre-macos", + "gpui-pre-web", + "gpui-pre-windows", +] + +[[package]] +name = "gpui-pre-refineable" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4042345bb5bd3ecbc5d15b21ed128154f462a992058d7038cd397631f5462338" +dependencies = [ + "gpui-pre-derive-refineable", +] + +[[package]] +name = "gpui-pre-reqwest" +version = "0.12.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05be23908e707966824f8c51a609904b7f30739e8d915e120a53c1b995ba964c" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry 0.4.0", ] [[package]] -name = "getopts" -version = "0.2.24" +name = "gpui-pre-scheduler" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +checksum = "f4acd73345bc3c09f1b98c1b21eb4459ad8336f89bb2edcef619e11c5a08456e" dependencies = [ - "unicode-width", + "async-task", + "backtrace", + "chrono", + "flume", + "futures", + "parking_lot", + "rand 0.9.5", + "wasm_thread", + "web-time", ] [[package]] -name = "getrandom" -version = "0.2.17" +name = "gpui-pre-shared-string" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "477529fd5aa90bff969785ee9a3938484d94aa5101cdf7b84719629c0345f98a" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", + "schemars", + "serde", + "smol_str", ] [[package]] -name = "getrandom" -version = "0.3.4" +name = "gpui-pre-sum-tree" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "9ec0ec176d576b82aed716cf47185856b54ea9528caaf9d01787024689bfd824" dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", + "gpui-pre-ztracing", + "heapless", + "log", + "rayon", + "tracing", ] [[package]] -name = "getrandom" -version = "0.4.3" +name = "gpui-pre-util" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +checksum = "e1635ae7d0b05400bfa211ddef6c67dc8d9f223c463022c2b3bf2c1c14c6044f" dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 6.0.0", - "rand_core 0.10.1", - "wasm-bindgen", + "anyhow", + "log", + "which", ] [[package]] -name = "gif" -version = "0.14.2" +name = "gpui-pre-util-macros" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +checksum = "01486fce7722e5d7d7e7bb71c719dfdcc6ed917112bdc7c56d0556fdb60d10ab" dependencies = [ - "color_quant", - "weezl", + "gpui-pre-perf", + "quote", + "syn 2.0.119", ] [[package]] -name = "gio" -version = "0.18.4" +name = "gpui-pre-web" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +checksum = "184776d0763e6889dd2842dcb1204c15055945f2bdfe926b5ef57bdbd1952a65" dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", + "anyhow", + "console_error_panic_hook", + "futures", + "gpui-pre", + "gpui-pre-http-client", + "gpui-pre-scheduler", + "gpui-pre-wgpu", + "js-sys", + "log", + "parking_lot", + "raw-window-handle", "smallvec", - "thiserror 1.0.69", + "unicode-properties", + "unicode-script", + "unicode-segmentation", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm_thread", + "web-sys", + "web-time", ] [[package]] -name = "gio-sys" -version = "0.18.1" +name = "gpui-pre-wgpu" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "439c5421cafe1a7cf68d0c0ae5cc82b26fad6b0e6345ac98d20acaf1f2463914" dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", + "anyhow", + "bytemuck", + "cosmic-text", + "etagere", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-util", + "itertools 0.14.0", + "log", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "swash", + "unicode-bidi", + "unicode-segmentation", + "web-sys", + "wgpu", + "zed-font-kit", ] [[package]] -name = "glib" -version = "0.18.5" +name = "gpui-pre-windows" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +checksum = "371f9bb13f97e3bea2f8b8e5e81232bb74d735153c244ae67f7400e743925b1c" dependencies = [ - "bitflags 2.13.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", + "accesskit", + "accesskit_windows", + "anyhow", + "dunce", + "etagere", + "futures", + "gpui-pre", + "gpui-pre-collections", + "gpui-pre-scheduler", + "gpui-pre-util", + "image", + "itertools 0.14.0", + "log", + "parking_lot", + "rand 0.9.5", + "raw-window-handle", "smallvec", - "thiserror 1.0.69", + "uuid", + "windows 0.62.2", + "windows-core 0.62.2", + "windows-numerics 0.3.1", + "windows-registry 0.6.1", ] [[package]] -name = "glib-macros" -version = "0.18.5" +name = "gpui-pre-zlog" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +checksum = "3bbe5b98630ef4bfed5a7105ec1062e78d571912bc491388f07ea22f2457c18b" dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.119", + "anyhow", + "chrono", + "gpui-pre-collections", + "log", ] [[package]] -name = "glib-sys" -version = "0.18.1" +name = "gpui-pre-ztracing" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "f806400f6bda1fdd9fc627e158c0e44fb0c4b7767582a3621efcf92f4a850db7" dependencies = [ - "libc", - "system-deps", + "gpui-pre-zlog", + "gpui-pre-ztracing-macro", + "tracing", + "tracing-subscriber", ] [[package]] -name = "glob" -version = "0.3.4" +name = "gpui-pre-ztracing-macro" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" +checksum = "d337c9acf058238e45ce0570d8a5958439242889492f91ae2d8e324211a61ccd" [[package]] -name = "gobject-sys" -version = "0.18.0" +name = "granit-parser" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "e20f99e46474f56bd905c56e817ebddcf377a611f94c53ac4649e4d3fa3c0cd0" dependencies = [ - "glib-sys", - "libc", - "system-deps", + "arraydeque", + "smallvec", ] [[package]] @@ -2082,23 +3310,26 @@ dependencies = [ "base64 0.22.1", "bytes", "chrono", - "dirs", + "dirs 6.0.0", + "embed-resource", "futures", + "gpui-kit", + "image", "kindling-mobi", "libc", "mobi", "mtp-rs", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", "parking_lot", "rars", - "reqwest 0.12.28", + "reqwest", + "rfd", "rusqlite", "sanitize-filename", "serde", "serde_json", - "tauri", - "tauri-build", - "tauri-plugin-dialog", - "tauri-plugin-opener", "thiserror 2.0.20", "tokio", "uuid", @@ -2106,66 +3337,56 @@ dependencies = [ ] [[package]] -name = "gtk" -version = "0.18.2" +name = "h2" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118" dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.2", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "gtk-sys" -version = "0.18.2" +name = "half" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", ] [[package]] -name = "gtk3-macros" -version = "0.18.2" +name = "harfrust" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.119", + "bitflags 2.13.2", + "bytemuck", + "core_maths", + "read-fonts 0.37.0", + "smallvec", ] [[package]] -name = "half" -version = "2.7.1" +name = "hash32" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", + "byteorder", ] [[package]] @@ -2186,6 +3407,26 @@ dependencies = [ "ahash 0.8.12", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -2201,6 +3442,16 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -2225,6 +3476,30 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "hmac" version = "0.13.0" @@ -2236,12 +3511,16 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.38.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", - "markup5ever 0.38.0", + "mac", + "markup5ever 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -2287,12 +3566,6 @@ dependencies = [ "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.10.1" @@ -2318,6 +3591,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -2338,6 +3612,7 @@ dependencies = [ "hyper", "hyper-util", "rustls", + "rustls-native-certs", "tokio", "tokio-rustls", "tower-service", @@ -2391,16 +3666,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ico" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" -dependencies = [ - "byteorder", - "png 0.17.16", -] - [[package]] name = "icu_collections" version = "2.3.0" @@ -2484,12 +3749,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "1.1.0" @@ -2511,6 +3770,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.25.10" @@ -2521,7 +3796,7 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif", + "gif 0.14.2", "image-webp", "moxcms", "num-traits", @@ -2531,8 +3806,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core", - "zune-jpeg", + "zune-core 0.5.3", + "zune-jpeg 0.5.15", ] [[package]] @@ -2545,6 +3820,18 @@ dependencies = [ "quick-error", ] +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "imagesize" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" + [[package]] name = "imgref" version = "1.12.3" @@ -2559,28 +3846,48 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] name = "indexmap" version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "inotify" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +dependencies = [ + "bitflags 2.13.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", + "libc", ] [[package]] -name = "infer" -version = "0.19.0" +name = "inout" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "cfb", + "block-padding", + "generic-array", ] [[package]] @@ -2592,6 +3899,18 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "interpolate_name" version = "0.2.4" @@ -2603,6 +3922,15 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "inventory" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6928282826c822ad91bf1c9a1cb90a30ba1c26770749929b4656cd6be829cd7c" +dependencies = [ + "rustversion", +] + [[package]] name = "io-kit-sys" version = "0.5.0" @@ -2613,6 +3941,18 @@ dependencies = [ "mach2", ] +[[package]] +name = "io-surface" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "554b8c5d64ec09a3a520fe58e4d48a73e00ff32899cdcbe32a4877afd4968b8e" +dependencies = [ + "cgl", + "core-foundation 0.10.1", + "core-foundation-sys", + "leaky-cow", +] + [[package]] name = "ipnet" version = "2.12.2" @@ -2655,110 +3995,36 @@ dependencies = [ [[package]] name = "itertools" -version = "0.14.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] [[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "javascriptcore-rs" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "jiff" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "jiff-tzdb-platform", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", - "windows-link 0.2.1", -] - -[[package]] -name = "jiff-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" -dependencies = [ - "defmt", -] - -[[package]] -name = "jiff-static" -version = "0.2.35" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", + "either", ] [[package]] -name = "jiff-tzdb" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.3" +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ - "jiff-tzdb", + "either", ] [[package]] -name = "jni" -version = "0.21.1" +name = "itoa" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jni-sys" @@ -2810,37 +4076,21 @@ dependencies = [ ] [[package]] -name = "json-patch" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" -dependencies = [ - "jsonptr", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "jsonptr" -version = "0.6.3" +name = "khronos-egl" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ - "serde", - "serde_json", + "libc", + "libloading 0.8.9", + "pkg-config", ] [[package]] -name = "keyboard-types" -version = "0.7.0" +name = "khronos_api" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.13.2", - "serde", - "unicode-segmentation", -] +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kindling-mobi" @@ -2862,56 +4112,87 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.5.0" +name = "kqueue" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +dependencies = [ + "kqueue-sys", + "libc", +] [[package]] -name = "lebe" -version = "0.5.3" +name = "kqueue-sys" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +dependencies = [ + "bitflags 2.13.2", + "libc", +] [[package]] -name = "libappindicator" -version = "0.9.0" +name = "kurbo" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ - "glib", - "gtk", - "gtk-sys", - "libappindicator-sys", - "log", + "arrayvec", + "euclid", + "smallvec", ] [[package]] -name = "libappindicator-sys" -version = "0.9.0" +name = "kurbo" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +checksum = "4b60dfc32f652b926df6192e55525b16d186c69d47876c3ead4da5cc9f8450e2" dependencies = [ - "gtk-sys", - "libloading", - "once_cell", + "arrayvec", + "euclid", + "polycool", + "smallvec", ] [[package]] -name = "libc" -version = "0.2.189" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "libdbus-sys" -version = "0.2.7" +name = "leak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" + +[[package]] +name = "leaky-cow" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" dependencies = [ - "pkg-config", + "leak", ] +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + [[package]] name = "libfuzzer-sys" version = "0.4.13" @@ -2932,6 +4213,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + [[package]] name = "libm" version = "0.2.16" @@ -2992,12 +4283,30 @@ version = "1.0.0-alpha.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c12744d1279367caed41739ef094c325d53fb0ffcd4f9b84a368796f870252" dependencies = [ - "convert_case", + "convert_case 0.6.0", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "linebender_resource_handle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" + +[[package]] +name = "link-section" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c29a617ce3df32c08497bdc1ab6e2376e0b17948ac166a2fbe5977c5954cd9" + +[[package]] +name = "linktime-proc-macro" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e57c38c1e860fd37c604281cdfb1dd2216977fd76a50f85ba2f388ef3219616" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -3010,6 +4319,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -3023,23 +4338,112 @@ dependencies = [ name = "log" version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +dependencies = [ + "serde_core", + "value-bag", +] + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lsp-types" +version = "0.97.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53353550a17c04ac46c585feb189c2db82154fc84b79c7a66c96c2c644f66071" +dependencies = [ + "bitflags 1.3.2", + "fluent-uri", + "serde", + "serde_json", + "serde_repr", +] + +[[package]] +name = "lyon" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0578bdecb7d6d88987b8b2b1e3a4e2f81df9d0ece1078623324a567904e7b7" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdfa8785f95e57914ddb35e3b59994aeba6f5e79e9cfd03da1c269f010f36009" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4336502e29e32af93cf2dad2214ed6003c17ceb5bd499df77b1de663b9042b92" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c463f9c428b7fc5ec885dcd39ce4aa61e29111d0e33483f6f98c74e89d8621e" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b8dcf906637ecef61b3c0740c7a4e7f27caeb31257cfac0cc579ce15be6005" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] -name = "loop9" -version = "0.1.5" +name = "mac-notification-sys" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +checksum = "fd604973958ddcc11b561193c0fb96ba146506ef2f231ef2e7c35fd2cbc9beca" dependencies = [ - "imgref", + "cc", + "log", + "objc2 0.6.4", + "objc2-foundation 0.3.2", + "time", + "uuid", ] -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "mach2" version = "0.5.0" @@ -3049,15 +4453,37 @@ dependencies = [ "libc", ] +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markdown" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5cab8f2cadc416a82d2e783a1946388b31654d391d1c7d92cc1f03e295b1deb" +dependencies = [ + "serde", + "unicode-id", +] + [[package]] name = "markup5ever" -version = "0.38.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", - "tendril", - "web_atoms", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache 0.8.9", + "string_cache_codegen 0.5.4", + "tendril 0.4.3", ] [[package]] @@ -3067,10 +4493,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", - "tendril", + "tendril 0.5.1", "web_atoms", ] +[[package]] +name = "markup5ever_rcdom" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18" +dependencies = [ + "html5ever 0.27.0", + "markup5ever 0.12.1", + "tendril 0.4.3", + "xml5ever", +] + [[package]] name = "matches" version = "0.1.10" @@ -3087,12 +4525,31 @@ dependencies = [ "rayon", ] +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest 0.10.7", +] + [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "memmap2" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -3102,12 +4559,43 @@ dependencies = [ "autocfg", ] +[[package]] +name = "metal" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7047791b5bc903b8cd963014b355f71dc9864a9a0b727057676c1dcae5cbc15" +dependencies = [ + "bitflags 2.13.2", + "block", + "core-graphics-types 0.2.0", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3135,6 +4623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -3178,39 +4667,35 @@ dependencies = [ ] [[package]] -name = "muda" -version = "0.19.3" +name = "multiversion_no_op" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" -dependencies = [ - "crossbeam-channel", - "dpi", - "gtk", - "keyboard-types", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", - "once_cell", - "png 0.18.1", - "serde", - "thiserror 2.0.20", - "windows-sys 0.61.2", -] +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" [[package]] -name = "ndk" -version = "0.9.0" +name = "naga" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" dependencies = [ + "arrayvec", + "bit-set", "bitflags 2.13.2", - "jni-sys 0.3.1", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap 2.14.2", + "libm", "log", - "ndk-sys", - "num_enum", - "raw-window-handle", - "thiserror 1.0.69", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "thiserror 2.0.20", + "unicode-ident", ] [[package]] @@ -3237,6 +4722,22 @@ dependencies = [ "memchr", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nom" version = "8.0.0" @@ -3252,6 +4753,88 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "normpath" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11ce00d2594068e8a27c9146fdc5cf9f3ac38eb42c7cd34d05fea618873ac9f" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.13.2", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-rust" +version = "4.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4587364a9a0074333429b3df75a30a205340c56a536ca3eb6ca0e59b87bbf8af" +dependencies = [ + "futures-lite", + "log", + "mac-notification-sys", + "serde", + "tauri-winrt-notification", + "zbus", +] + +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.8" @@ -3262,6 +4845,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f9a86e097b0d187ad0e65667c2f58b9254671e86e7dbb78036b16692eae099" +dependencies = [ + "libm", + "num-integer", + "num-iter", + "num-traits", + "once_cell", + "rand 0.9.5", + "serde", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.4.6" @@ -3298,6 +4898,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -3316,6 +4926,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", ] [[package]] @@ -3334,7 +4955,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.119", @@ -3346,7 +4967,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18ef13beb3b3a8fc16fd7aea912ebd3d45dde00a9a5b968d0742297468065845" dependencies = [ - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "futures-core", "io-kit-sys", @@ -3363,37 +4984,131 @@ dependencies = [ ] [[package]] -name = "objc2" -version = "0.6.4" +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-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d5490aaf8f1d7cf7688dfa9b0ce07900e168852c45cd2c03f534dfd27cfd0b" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.3", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "libc", + "objc2 0.6.4", + "objc2-cloud-kit", + "objc2-core-data 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.2", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-audio" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" dependencies = [ - "objc2-encode", - "objc2-exception-helper", + "dispatch2", + "objc2 0.6.4", + "objc2-core-audio-types", + "objc2-core-foundation", ] [[package]] -name = "objc2-app-kit" +name = "objc2-core-audio-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" dependencies = [ "bitflags 2.13.2", - "block2", - "objc2", - "objc2-core-foundation", - "objc2-foundation", + "objc2 0.6.4", ] [[package]] -name = "objc2-cloud-kit" -version = "0.3.2" +name = "objc2-core-data" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.13.2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", ] [[package]] @@ -3402,8 +5117,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "objc2", - "objc2-foundation", + "bitflags 2.13.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] @@ -3414,7 +5130,7 @@ checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.13.2", "dispatch2", - "objc2", + "objc2 0.6.4", ] [[package]] @@ -3425,19 +5141,31 @@ checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ "bitflags 2.13.2", "dispatch2", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", "objc2-io-surface", ] +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + [[package]] name = "objc2-core-image" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] @@ -3446,8 +5174,23 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-media" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec576860167a15dd9fce7fbee7512beb4e31f532159d3482d1f9c6caedf31d" +dependencies = [ + "bitflags 2.13.2", + "dispatch2", + "objc2 0.6.4", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-core-video", ] [[package]] @@ -3457,9 +5200,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ "bitflags 2.13.2", - "objc2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.13.2", + "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", + "objc2-io-surface", ] [[package]] @@ -3469,12 +5225,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] -name = "objc2-exception-helper" -version = "0.1.1" +name = "objc2-foundation" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "cc", + "bitflags 2.13.2", + "block2 0.5.1", + "libc", + "objc2 0.5.3", ] [[package]] @@ -3484,9 +5243,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.2", - "block2", + "block2 0.6.2", "libc", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -3497,10 +5256,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ "bitflags 2.13.2", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", ] +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.13.2", + "block2 0.5.1", + "objc2 0.5.3", + "objc2-foundation 0.2.2", + "objc2-metal 0.2.2", +] + [[package]] name = "objc2-quartz-core" version = "0.3.2" @@ -3508,30 +5304,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ "bitflags 2.13.2", - "objc2", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", ] [[package]] -name = "objc2-ui-kit" +name = "objc2-screen-capture-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +checksum = "74b7c5390f477482f001bc354d6571a70db7e4f8d5288e860c45521fbce11394" dependencies = [ - "bitflags 2.13.2", - "block2", - "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-foundation", + "block2 0.6.2", + "dispatch2", + "objc2 0.6.4", "objc2-core-graphics", - "objc2-core-image", - "objc2-core-location", - "objc2-core-text", - "objc2-foundation", - "objc2-quartz-core", - "objc2-user-notifications", + "objc2-core-media", + "objc2-foundation 0.3.2", ] [[package]] @@ -3540,22 +5330,38 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ - "objc2", - "objc2-foundation", + "bitflags 2.13.2", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-core-location", + "objc2-foundation 0.3.2", ] [[package]] -name = "objc2-web-kit" -version = "0.3.2" +name = "objc_exception" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" dependencies = [ - "bitflags 2.13.2", - "block2", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", + "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.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", ] [[package]] @@ -3570,23 +5376,72 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oo7" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f2bfed90f1618b4b48dcad9307f25e14ae894e2949642c87c351601d62cebd" +dependencies = [ + "aes 0.8.4", + "ashpd 0.13.13", + "async-fs", + "async-io", + "async-lock", + "blocking", + "cbc", + "cipher 0.4.4", + "digest 0.10.7", + "endi", + "futures-lite", + "futures-util", + "getrandom 0.4.3", + "hkdf", + "hmac 0.12.1", + "md-5", + "num", + "num-bigint-dig", + "pbkdf2", + "serde", + "serde_bytes", + "sha2 0.10.9", + "subtle", + "zbus", + "zbus_macros", + "zeroize", + "zvariant", +] + [[package]] name = "open" version = "5.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc" dependencies = [ - "dunce", "is-wsl", "libc", ] +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "5.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c9e0d9b23589f26070720bac724174bfec1083e82f7854cdd0267518343c0" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -3603,31 +5458,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - [[package]] name = "parcel_selectors" version = "0.28.3" @@ -3640,7 +5470,7 @@ dependencies = [ "phf 0.11.3", "phf_codegen 0.11.3", "precomputed-hash", - "rustc-hash", + "rustc-hash 2.1.3", "smallvec", ] @@ -3651,7 +5481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb" dependencies = [ "base64-simd", - "data-url", + "data-url 0.1.1", "rkyv", "serde", "serde_json", @@ -3705,6 +5535,35 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4500030c302e4af1d423f36f3b958d1aecb6c04184356ed5a833bf6b60435777" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3802,6 +5661,32 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -3825,19 +5710,6 @@ version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" -[[package]] -name = "plist" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896bade328c13f7042a297ea5ac5b0951f6cf989dea5f32c2fd98da398195cb" -dependencies = [ - "base64 0.23.1", - "indexmap 2.14.2", - "quick-xml 0.42.0", - "serde", - "time", -] - [[package]] name = "png" version = "0.17.16" @@ -3878,6 +5750,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "pollster" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "polycool" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50596ddc09eb5ad5f75cacd40209568e66df71baf86e1499a0e99c4cff12a5a6" +dependencies = [ + "arrayvec", +] + [[package]] name = "portable-atomic" version = "1.15.0" @@ -3893,6 +5786,23 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" +dependencies = [ + "atomic", + "crossbeam-queue", + "futures", + "log", + "parking_lot", + "pin-project", + "pollster 0.2.5", + "static_assertions", + "thiserror 1.0.69", +] + [[package]] name = "potential_utf" version = "0.1.6" @@ -3924,23 +5834,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "presser" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] -name = "proc-macro-crate" -version = "2.0.2" +name = "prettyplease" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "toml_datetime 0.6.3", - "toml_edit 0.20.2", + "proc-macro2", + "syn 2.0.119", ] [[package]] @@ -3952,30 +5858,6 @@ dependencies = [ "toml_edit 0.25.15+spec-1.1.0", ] -[[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-macro2" version = "1.0.107" @@ -4079,9 +5961,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.42.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" dependencies = [ "memchr", ] @@ -4097,7 +5979,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.1.3", "rustls", "socket2", "thiserror 2.0.20", @@ -4118,7 +6000,7 @@ dependencies = [ "rand 0.10.2", "rand_pcg", "ring", - "rustc-hash", + "rustc-hash 2.1.3", "rustls", "rustls-pki-types", "slab", @@ -4175,6 +6057,8 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ + "libc", + "rand_chacha 0.3.1", "rand_core 0.6.4", ] @@ -4184,7 +6068,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "rand_chacha", + "rand_chacha 0.9.0", "rand_core 0.9.5", ] @@ -4192,11 +6076,21 @@ dependencies = [ name = "rand" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "chacha20", - "getrandom 0.4.3", - "rand_core 0.10.1", + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] @@ -4214,6 +6108,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] [[package]] name = "rand_core" @@ -4239,16 +6136,28 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "rangemap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a611d15b50743feb4c76b7d03edcb0e64f399c26961e4efe6975bc398be6aa3d" + [[package]] name = "rars" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d61643d05cb7e5045cc4f3e066ecf4b4e7ac394b9a052067906f530e91d3c88" dependencies = [ - "aes", + "aes 0.9.3", "aho-corasick", "getrandom 0.4.3", - "hmac", + "hmac 0.13.0", "rayon", "sha1", "sha2 0.11.0", @@ -4283,7 +6192,7 @@ dependencies = [ "paste", "profiling", "rand 0.9.5", - "rand_chacha", + "rand_chacha 0.9.0", "simd_helpers", "thiserror 2.0.20", "v_frame", @@ -4320,6 +6229,18 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + [[package]] name = "rayon" version = "1.12.0" @@ -4340,6 +6261,28 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "read-fonts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" +dependencies = [ + "bytemuck", + "core_maths", + "font-types 0.11.3", +] + +[[package]] +name = "read-fonts" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" +dependencies = [ + "bytemuck", + "font-types 0.12.6", + "once_cell", +] + [[package]] name = "reborrow" version = "0.5.5" @@ -4355,6 +6298,17 @@ dependencies = [ "bitflags 2.13.2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -4424,6 +6378,12 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + [[package]] name = "reqwest" version = "0.12.28" @@ -4463,61 +6423,61 @@ dependencies = [ ] [[package]] -name = "reqwest" -version = "0.13.5" +name = "resvg" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a1cfa75cc186dd73d5818e510e042e40927bccc9c236b061cea97e1eb08029" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" dependencies = [ - "base64 0.23.1", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "js-sys", + "gif 0.13.3", + "image-webp", "log", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", + "pico-args", + "rgb", + "svgtypes 0.15.3", + "tiny-skia", + "usvg 0.45.1", + "zune-jpeg 0.4.21", +] + +[[package]] +name = "resvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" +dependencies = [ + "gif 0.14.2", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes 0.16.1", + "tiny-skia", + "usvg 0.46.0", + "zune-jpeg 0.5.15", ] [[package]] name = "rfd" -version = "0.16.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ - "block2", + "ashpd 0.11.1", + "block2 0.6.2", "dispatch2", - "glib-sys", - "gobject-sys", - "gtk-sys", "js-sys", "log", - "objc2", - "objc2-app-kit", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-core-foundation", - "objc2-foundation", + "objc2-foundation 0.3.2", + "pollster 0.4.0", "raw-window-handle", + "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -4525,6 +6485,9 @@ name = "rgb" version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] [[package]] name = "ring" @@ -4569,6 +6532,30 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ropey" +version = "2.0.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4045a00dc327d084a2bbf126976e14125b54f23bd30511d45b842eba76c52d74" +dependencies = [ + "str_indices", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "roxmltree" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb" +dependencies = [ + "memchr", +] + [[package]] name = "rusqlite" version = "0.32.1" @@ -4583,6 +6570,103 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rust-embed" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19afa5b4b6a611de00bd1bdae6ae6f39084c9399f0679c3f52d8469cf335cc23" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d8afda6374eac59e066abee06d265247ebbaf3006cf878e2879e8356e34053" +dependencies = [ + "mime_guess", + "proc-macro2", + "quote", + "rust-embed-utils", + "shellexpand", + "syn 2.0.119", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d84e8ba78bd384263e5922f084cbe1b081c3b7e69add59c8fb097b879ba968a" +dependencies = [ + "globset", + "sha2 0.11.0", + "walkdir", +] + +[[package]] +name = "rust-i18n" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8502e73e6461c738659455c5d2cf95bc132760a6f203c717e66ee72b78906d7f" +dependencies = [ + "globwalk", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54a28dd9d46108d56b9f0c85b220908844233e561c03087890325104f90f2c6" +dependencies = [ + "glob", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "syn 2.0.119", +] + +[[package]] +name = "rust-i18n-support" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e806c2a9701cbd6043992cd935c30cb1fdb128fa15e7b506f3b46f97045737c0" +dependencies = [ + "arc-swap", + "base62", + "foldhash 0.2.0", + "globwalk", + "itertools 0.11.0", + "normpath", + "serde", + "serde-saphyr", + "serde_json", + "siphasher", + "toml 0.8.23", + "triomphe", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.3" @@ -4625,6 +6709,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.15.1" @@ -4652,6 +6757,24 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.23" @@ -4677,30 +6800,12 @@ dependencies = [ ] [[package]] -name = "schemars" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" -dependencies = [ - "dyn-clone", - "indexmap 1.9.3", - "schemars_derive", - "serde", - "serde_json", - "url", - "uuid", -] - -[[package]] -name = "schemars" -version = "0.9.0" +name = "schannel" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", + "windows-sys 0.61.2", ] [[package]] @@ -4710,23 +6815,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", + "indexmap 2.14.2", "ref-cast", + "schemars_derive", "serde", "serde_json", ] [[package]] name = "schemars_derive" -version = "0.8.22" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +checksum = "d98c67716b46af2f0b8cf752abc930f6f9aecfbf671ecfb531db8a31dbe4e2ba" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.119", + "syn 3.0.5", ] +[[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" @@ -4745,7 +6858,30 @@ dependencies = [ "html5ever 0.39.0", "precomputed-hash", "selectors", - "tendril", + "tendril 0.5.1", +] + +[[package]] +name = "screencapturekit" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" +dependencies = [ + "screencapturekit-sys", +] + +[[package]] +name = "screencapturekit-sys" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" +dependencies = [ + "block", + "dispatch", + "objc", + "objc-foundation", + "objc_id", + "once_cell", ] [[package]] @@ -4754,6 +6890,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.36.1" @@ -4768,11 +6927,17 @@ dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", "precomputed-hash", - "rustc-hash", + "rustc-hash 2.1.3", "servo_arc", "smallvec", ] +[[package]] +name = "self_cell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" + [[package]] name = "semver" version = "1.0.28" @@ -4803,15 +6968,30 @@ dependencies = [ ] [[package]] -name = "serde-untagged" -version = "0.1.9" +name = "serde-saphyr" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8050abb251097357e24aff63ba2c52a6309ecb7d23a5474023df960a02694d8" +dependencies = [ + "annotate-snippets", + "base64 0.23.1", + "encoding_rs_io", + "granit-parser", + "nohash-hasher", + "num-traits", + "serde_core", + "smallvec", + "zmij", +] + +[[package]] +name = "serde_bytes" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ - "erased-serde", "serde", "serde_core", - "typeid", ] [[package]] @@ -4836,13 +7016,22 @@ dependencies = [ [[package]] name = "serde_derive_internals" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", +] + +[[package]] +name = "serde_fmt" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e497af288b3b95d067a23a4f749f2861121ffcb2f6d8379310dcda040c345ed" +dependencies = [ + "serde_core", ] [[package]] @@ -4851,6 +7040,7 @@ version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ + "indexmap 2.14.2", "itoa", "memchr", "serde", @@ -4899,61 +7089,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" -dependencies = [ - "base64 0.23.1", - "bs58", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.14.2", - "jiff", - "schemars 0.9.0", - "schemars 1.2.2", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 3.0.5", -] - -[[package]] -name = "serialize-to-javascript" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" -dependencies = [ - "serde", - "serde_json", - "serialize-to-javascript-impl", -] - -[[package]] -name = "serialize-to-javascript-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - [[package]] name = "servo_arc" version = "0.4.3" @@ -4974,6 +7109,12 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.9" @@ -4996,6 +7137,30 @@ dependencies = [ "digest 0.11.3", ] +[[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 = "shellexpand" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" +dependencies = [ + "dirs 6.0.0", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shlex" version = "2.0.1" @@ -5042,24 +7207,88 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +[[package]] +name = "skrifa" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" +dependencies = [ + "bytemuck", + "read-fonts 0.37.0", +] + +[[package]] +name = "skrifa" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" +dependencies = [ + "bytemuck", + "read-fonts 0.41.0", +] + [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" +[[package]] +name = "smol" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33bd3e260892199c3ccfc487c88b2da2265080acb316cd920da72fdfd7c599f" +dependencies = [ + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", +] + +[[package]] +name = "smol_str" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" +dependencies = [ + "serde_core", +] + [[package]] name = "socket2" version = "0.6.5" @@ -5071,51 +7300,30 @@ dependencies = [ ] [[package]] -name = "softbuffer" -version = "0.4.8" +name = "spin" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" dependencies = [ - "bytemuck", - "js-sys", - "ndk", - "objc2", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation", - "objc2-quartz-core", - "raw-window-handle", - "redox_syscall", - "tracing", - "wasm-bindgen", - "web-sys", - "windows-sys 0.61.2", + "lock_api", ] [[package]] -name = "soup3" -version = "0.5.0" +name = "spin" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" dependencies = [ - "futures-channel", - "gio", - "glib", - "libc", - "soup3-sys", + "lock_api", ] [[package]] -name = "soup3-sys" -version = "0.5.0" +name = "spirv" +version = "0.4.0+sdk-1.4.341.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", + "bitflags 2.13.2", ] [[package]] @@ -5124,6 +7332,40 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + [[package]] name = "string_cache" version = "0.9.0" @@ -5137,38 +7379,176 @@ dependencies = [ ] [[package]] -name = "string_cache_codegen" -version = "0.6.1" +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "sval" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b81b254da21fe1fcc4e3a74fe39b46e25e3a863078f8b71c954d47f84889dbc6" + +[[package]] +name = "sval_buffer" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50be352d2822ffafb59e3e2ddac9d5ee60f2eeadbb7b5a2a951b9f3651e87a6f" +dependencies = [ + "sval", + "sval_ref", + "zerocopy", +] + +[[package]] +name = "sval_dynamic" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048ca293b998d9a45659159f94a64063791e74cdc670164943dbb434405573d" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_fmt" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b5888e40f80568733217f27b7317b845f463400ced36c424b1a804730e53b2" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_json" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17664d6bb6b74947afaab9d7c991caa9bf5638d4dee16fcbef637f440796049" +dependencies = [ + "itoa", + "ryu", + "sval", +] + +[[package]] +name = "sval_nested" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c059969ca5ca163ea7fef6c9661758973d17691aba92abdcf5c428f4ec122c" +dependencies = [ + "sval", + "sval_buffer", + "sval_ref", +] + +[[package]] +name = "sval_ref" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d6b29ff568c85c87561807f51d2adfff4b6016c6363133f7cd1652a12548f3" +dependencies = [ + "sval", +] + +[[package]] +name = "sval_serde" +version = "2.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f33ec9edc42b12764d5c90ca0a1d84189c6bde81ed27507f1e661c6e4e05853" +dependencies = [ + "serde_core", + "sval", + "sval_nested", +] + +[[package]] +name = "svg_fmt" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" -dependencies = [ - "phf_generator 0.13.1", - "phf_shared 0.13.1", - "proc-macro2", - "quote", -] +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" [[package]] -name = "strsim" -version = "0.11.1" +name = "svgtypes" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo 0.11.3", + "siphasher", +] [[package]] -name = "subtle" -version = "2.6.1" +name = "svgtypes" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" +dependencies = [ + "kurbo 0.13.1", + "siphasher", +] [[package]] -name = "swift-rs" -version = "1.0.8" +name = "swash" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e45c444e496845d3f2a351146bff59aae4975b2280238df1dfaa0c7d1846f38e" +checksum = "6c2499c2d826531388872b2268718aed907a39bd785ab0dcfe57fab26283f92e" dependencies = [ - "base64 0.21.7", - "serde", - "serde_json", + "skrifa 0.44.0", + "yazi", + "zeno", ] [[package]] @@ -5225,373 +7605,88 @@ dependencies = [ ] [[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml 0.8.2", - "version-compare", -] - -[[package]] -name = "tao" -version = "0.35.3" +name = "sys-locale" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" dependencies = [ - "bitflags 2.13.2", - "block2", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dbus", - "dispatch2", - "dlopen2", - "dpi", - "gdkwayland-sys", - "gdkx11-sys", - "gtk", - "jni", "libc", - "log", - "ndk", - "ndk-sys", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "objc2-ui-kit", - "once_cell", - "parking_lot", - "percent-encoding", - "raw-window-handle", - "tao-macros", - "unicode-segmentation", - "url", - "windows 0.61.3", - "windows-core 0.61.2", - "windows-version", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", ] [[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - -[[package]] -name = "tauri" -version = "2.11.5" +name = "sysinfo" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" dependencies = [ - "anyhow", - "bytes", - "cookie", - "dirs", - "dunce", - "embed_plist", - "getrandom 0.3.4", - "glob", - "gtk", - "heck 0.5.0", - "http", - "http-range", - "jni", + "core-foundation-sys", "libc", - "log", - "mime", - "muda", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "objc2-ui-kit", - "objc2-web-kit", - "percent-encoding", - "plist", - "raw-window-handle", - "reqwest 0.13.5", - "serde", - "serde_json", - "serde_repr", - "serialize-to-javascript", - "swift-rs", - "tauri-build", - "tauri-macros", - "tauri-runtime", - "tauri-runtime-wry", - "tauri-utils", - "thiserror 2.0.20", - "tokio", - "tray-icon", - "url", - "webkit2gtk", - "webview2-com", - "window-vibrancy", - "windows 0.61.3", -] - -[[package]] -name = "tauri-build" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" -dependencies = [ - "anyhow", - "cargo_toml", - "dirs", - "glob", - "heck 0.5.0", - "json-patch", - "schemars 0.8.22", - "semver", - "serde", - "serde_json", - "tauri-utils", - "tauri-winres", - "walkdir", -] - -[[package]] -name = "tauri-codegen" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" -dependencies = [ - "base64 0.22.1", - "brotli", - "ico", - "json-patch", - "plist", - "png 0.17.16", - "proc-macro2", - "quote", - "semver", - "serde", - "serde_json", - "sha2 0.10.9", - "syn 2.0.119", - "tauri-utils", - "thiserror 2.0.20", - "time", - "url", - "uuid", - "walkdir", -] - -[[package]] -name = "tauri-macros" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.119", - "tauri-codegen", - "tauri-utils", -] - -[[package]] -name = "tauri-plugin" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020" -dependencies = [ - "anyhow", - "glob", - "plist", - "schemars 0.8.22", - "serde", - "serde_json", - "tauri-utils", - "walkdir", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", ] [[package]] -name = "tauri-plugin-dialog" -version = "2.7.3" +name = "system-configuration" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61854a36651aa48381e5e209f69a01273b77f3f9f91f0c430b1b98d33bd47229" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "log", - "raw-window-handle", - "rfd", - "serde", - "serde_json", - "tauri", - "tauri-plugin", - "tauri-plugin-fs", - "thiserror 2.0.20", - "url", + "bitflags 2.13.2", + "core-foundation 0.9.4", + "system-configuration-sys", ] [[package]] -name = "tauri-plugin-fs" -version = "2.5.2" +name = "system-configuration-sys" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de22eef34fd78c0da050e748710edd50bf127e651d02ea1b2bfada1523cc5c51" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ - "anyhow", - "dunce", - "glob", - "log", - "objc2-foundation", - "percent-encoding", - "schemars 0.8.22", - "serde", - "serde_json", - "serde_repr", - "tauri", - "tauri-plugin", - "tauri-utils", - "thiserror 2.0.20", - "toml 1.1.6+spec-1.1.0", - "url", + "core-foundation-sys", + "libc", ] [[package]] -name = "tauri-plugin-opener" -version = "2.5.5" +name = "taffy" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d60366174b745b4ef5824b8bbc1c457fd08f0ce101ff643c0a49181a9f4e91" +checksum = "c034e05f6ee85a12daa63863c2245797715075c70649947aa0da54f3f2ab1d0f" dependencies = [ - "dunce", - "glob", - "objc2-app-kit", - "objc2-foundation", - "open", - "schemars 0.8.22", + "arrayvec", "serde", - "serde_json", - "tauri", - "tauri-plugin", - "thiserror 2.0.20", - "url", - "windows 0.61.3", - "zbus", + "slotmap", + "smallvec", ] [[package]] -name = "tauri-runtime" -version = "2.11.3" +name = "tao-core-video-sys" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" dependencies = [ - "cookie", - "dpi", - "gtk", - "http", - "jni", - "objc2", - "objc2-ui-kit", - "objc2-web-kit", - "raw-window-handle", - "serde", - "serde_json", - "tauri-utils", - "thiserror 2.0.20", - "url", - "webkit2gtk", - "webview2-com", - "windows 0.61.3", + "cfg-if", + "core-foundation-sys", + "libc", + "objc", ] [[package]] -name = "tauri-runtime-wry" -version = "2.11.4" +name = "tap" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" -dependencies = [ - "gtk", - "http", - "jni", - "log", - "objc2", - "objc2-app-kit", - "once_cell", - "percent-encoding", - "raw-window-handle", - "softbuffer", - "tao", - "tauri-runtime", - "tauri-utils", - "url", - "webkit2gtk", - "webview2-com", - "windows 0.61.3", - "wry", -] +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "tauri-utils" -version = "2.9.3" +name = "tauri-winrt-notification" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36" dependencies = [ - "anyhow", - "brotli", - "cargo_metadata", - "ctor", - "dom_query", - "dunce", - "glob", - "http", - "infer", - "json-patch", - "log", - "memchr", - "phf 0.13.1", - "plist", - "proc-macro2", - "quote", - "regex", - "schemars 0.8.22", - "semver", - "serde", - "serde-untagged", - "serde_json", - "serde_with", - "swift-rs", "thiserror 2.0.20", - "toml 1.1.6+spec-1.1.0", - "url", - "urlpattern", - "uuid", - "walkdir", -] - -[[package]] -name = "tauri-winres" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" -dependencies = [ - "dunce", - "embed-resource", - "toml 1.1.6+spec-1.1.0", + "windows 0.62.2", + "windows-version", ] [[package]] @@ -5607,6 +7702,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[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 = "tendril" version = "0.5.1" @@ -5616,6 +7722,15 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -5656,6 +7771,15 @@ dependencies = [ "syn 3.0.5", ] +[[package]] +name = "thread_local" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" +dependencies = [ + "cfg-if", +] + [[package]] name = "tiff" version = "0.11.3" @@ -5667,7 +7791,7 @@ dependencies = [ "half", "quick-error", "weezl", - "zune-jpeg", + "zune-jpeg 0.5.15", ] [[package]] @@ -5681,7 +7805,6 @@ dependencies = [ "powerfmt", "serde_core", "time-core", - "time-macros", ] [[package]] @@ -5691,13 +7814,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] -name = "time-macros" -version = "0.2.32" +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" dependencies = [ - "num-conv", - "time-core", + "arrayref", + "bytemuck", + "strict-num", ] [[package]] @@ -5761,6 +7909,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-socks" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e2948f60dbe26b35f2c7fb74ac2854c1fddded0fe9d7548fcc674a246f7615" +dependencies = [ + "either", + "futures-util", + "thiserror 1.0.69", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.19" @@ -5770,35 +7930,21 @@ dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] [[package]] name = "toml" -version = "0.8.2" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "toml_edit 0.20.2", -] - -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap 2.14.2", - "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.15", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] @@ -5818,22 +7964,13 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -5845,26 +7982,16 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.14.2", - "toml_datetime 0.6.3", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.14.2", "serde", "serde_spanned 0.6.9", - "toml_datetime 0.6.3", - "winnow 0.5.40", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", ] [[package]] @@ -5888,6 +8015,12 @@ dependencies = [ "winnow 1.0.4", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" version = "1.1.2+spec-1.1.0" @@ -5945,6 +8078,7 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5968,28 +8102,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", ] [[package]] -name = "tray-icon" -version = "0.24.2" +name = "tracing-log" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ - "crossbeam-channel", - "dirs", - "libappindicator", - "muda", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation", + "log", "once_cell", - "png 0.18.1", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "triomphe" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d15f4f5898bcfc5d8a8e45ed30b2f108d710c1a2e5489dbf80e078f707181bc" +dependencies = [ + "arc-swap", "serde", - "thiserror 2.0.20", - "windows-sys 0.61.2", + "stable_deref_trait", ] [[package]] @@ -5998,6 +8147,15 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + [[package]] name = "typeid" version = "1.0.3" @@ -6022,45 +8180,34 @@ dependencies = [ ] [[package]] -name = "unic-char-property" -version = "0.9.0" +name = "unicase" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] +checksum = "357cc3acc6a036009fd6c973ed009037c732d60d0b4f6c673e9041497482a28f" [[package]] -name = "unic-char-range" -version = "0.9.0" +name = "unicode-bidi" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] -name = "unic-common" -version = "0.9.0" +name = "unicode-bidi-mirroring" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" [[package]] -name = "unic-ucd-ident" -version = "0.9.0" +name = "unicode-ccc" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" [[package]] -name = "unic-ucd-version" -version = "0.9.0" +name = "unicode-id" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] +checksum = "65dd75ab2d9d379dbe86d4059e91d8b5a6e7054865eaba1ddbfc7a817ff96392" [[package]] name = "unicode-ident" @@ -6068,18 +8215,48 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + [[package]] name = "unicode-segmentation" version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" @@ -6100,17 +8277,71 @@ dependencies = [ ] [[package]] -name = "urlpattern" -version = "0.3.0" +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "usvg" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" dependencies = [ - "regex", - "serde", - "unic-ucd-ident", - "url", + "base64 0.22.1", + "data-url 0.3.2", + "flate2", + "fontdb", + "imagesize 0.13.0", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes 0.15.3", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "usvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e419dff010bb12512b0ae9e3d2f318dfbdf0167fde7eb05465134d4e8756076f" +dependencies = [ + "base64 0.22.1", + "data-url 0.3.2", + "flate2", + "fontdb", + "imagesize 0.14.0", + "kurbo 0.13.1", + "log", + "pico-args", + "roxmltree 0.21.1", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes 0.16.1", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6132,6 +8363,7 @@ dependencies = [ "getrandom 0.4.3", "js-sys", "serde_core", + "sha1_smol", "wasm-bindgen", ] @@ -6147,16 +8379,52 @@ dependencies = [ ] [[package]] -name = "vcpkg" -version = "0.2.15" +name = "valuable" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] -name = "version-compare" -version = "0.2.1" +name = "value-bag" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2799ffb329a792ecfd902b71306c8a815a6ef1c0470fa9953a6aa4d4cecbe511" +dependencies = [ + "value-bag-serde1", + "value-bag-sval2", +] + +[[package]] +name = "value-bag-serde1" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0941feceafbe7a8f59ea1096d45b97002884a41306315ad797b3684b63a81d8c" +dependencies = [ + "erased-serde", + "serde_core", + "serde_fmt", +] + +[[package]] +name = "value-bag-sval2" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "839752af8179287d27eb2b94164641b1ede9e60ab7424163388dc21ebd0508cd" +dependencies = [ + "sval", + "sval_buffer", + "sval_dynamic", + "sval_fmt", + "sval_json", + "sval_ref", + "sval_serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" @@ -6190,6 +8458,12 @@ dependencies = [ "libc", ] +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "walkdir" version = "2.5.0" @@ -6210,86 +8484,197 @@ dependencies = [ ] [[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.5", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasm_thread" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7516db7f32decdadb1c3b8deb1b7d78b9df7606c5cc2f6241737c2ab3a0258e" +dependencies = [ + "futures", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078" +dependencies = [ + "cc", + "downcast-rs", + "log", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] [[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" +name = "wayland-client" +version = "0.31.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ - "wit-bindgen", + "bitflags 2.13.2", + "rustix", + "wayland-backend", + "wayland-scanner", ] [[package]] -name = "wasm-bindgen" -version = "0.2.128" +name = "wayland-cursor" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", + "rustix", + "wayland-client", + "xcursor", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.78" +name = "wayland-protocols" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef4c5d3d2cdf5c54f4231181768f5510842e350db025faf1f7163b1030ed928" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "js-sys", - "wasm-bindgen", + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-scanner", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.128" +name = "wayland-protocols-plasma" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.128" +name = "wayland-protocols-wlr" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 3.0.5", - "wasm-bindgen-shared", + "bitflags 2.13.2", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.128" +name = "wayland-scanner" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ - "unicode-ident", + "proc-macro2", + "quick-xml 0.41.0", + "quote", ] [[package]] -name = "wasm-streams" -version = "0.5.0" +name = "wayland-sys" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "dlib", + "log", + "once_cell", + "pkg-config", ] [[package]] @@ -6320,104 +8705,211 @@ checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" dependencies = [ "phf 0.13.1", "phf_codegen 0.13.1", - "string_cache", - "string_cache_codegen", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", ] [[package]] -name = "webkit2gtk" -version = "2.0.2" +name = "webpki-roots" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" +dependencies = [ + "arrayvec", + "bitflags 2.13.2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags 2.13.2", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap 2.14.2", + "log", + "naga", "once_cell", - "soup3", - "webkit2gtk-sys", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.20", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-wasm", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", ] [[package]] -name = "webkit2gtk-sys" -version = "2.0.2" +name = "wgpu-core-deps-apple" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pkg-config", - "soup3-sys", - "system-deps", + "wgpu-hal", ] [[package]] -name = "webpki-roots" -version = "1.0.9" +name = "wgpu-core-deps-emscripten" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +checksum = "01e09be551dc939498bdd5f6b2c66e55ab275dad25825267a08605a80fc9f0af" dependencies = [ - "rustls-pki-types", + "wgpu-hal", ] [[package]] -name = "webview2-com" -version = "0.38.2" +name = "wgpu-core-deps-wasm" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +checksum = "af1fb1798be2a912497d4c224f72d39bb0cb34af50e8bcc29865bc339c943059" dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.61.3", - "windows-core 0.61.2", - "windows-implement", - "windows-interface", + "wgpu-hal", ] [[package]] -name = "webview2-com-macros" -version = "0.8.1" +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", + "wgpu-hal", ] [[package]] -name = "webview2-com-sys" -version = "0.38.2" +name = "wgpu-hal" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.13.2", + "block2 0.6.2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.9", + "log", + "naga", + "ndk-sys", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", + "objc2-quartz-core 0.3.2", + "once_cell", + "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "raw-window-metal", + "renderdoc-sys", + "smallvec", "thiserror 2.0.20", - "windows 0.61.3", - "windows-core 0.61.2", + "wasm-bindgen", + "wayland-sys", + "web-sys", + "wgpu-naga-bridge", + "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", + "windows-result 0.4.1", ] [[package]] -name = "weezl" -version = "0.1.12" +name = "wgpu-naga-bridge" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" +dependencies = [ + "bitflags 2.13.2", + "bytemuck", + "js-sys", + "log", + "raw-window-handle", + "web-sys", +] + +[[package]] +name = "which" +version = "8.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae2f2b2b816647a1cab1acc91f5bd20812d53cb344382635ec2181940c8034f" +dependencies = [ + "libc", +] [[package]] name = "winapi" @@ -6451,18 +8943,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "window-vibrancy" -version = "0.6.0" +name = "windows" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", - "raw-window-handle", - "windows-sys 0.59.0", - "windows-version", + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", ] [[package]] @@ -6490,6 +8987,19 @@ dependencies = [ "windows-numerics 0.3.1", ] +[[package]] +name = "windows-capture" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4df73e95feddb9ec1a7e9c2ca6323b8c97d5eeeff78d28f1eccdf19c882b24" +dependencies = [ + "parking_lot", + "rayon", + "thiserror 2.0.20", + "windows 0.61.3", + "windows-future 0.2.1", +] + [[package]] name = "windows-collections" version = "0.2.0" @@ -6508,14 +9018,39 @@ dependencies = [ "windows-core 0.62.2", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", @@ -6527,40 +9062,84 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] -name = "windows-future" -version = "0.2.1" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] -name = "windows-future" -version = "0.3.2" +name = "windows-interface" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ - "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] -name = "windows-implement" -version = "0.60.2" +name = "windows-interface" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", @@ -6610,6 +9189,46 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +dependencies = [ + "windows-result 0.3.4", + "windows-strings 0.3.1", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -6628,6 +9247,25 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -6648,11 +9286,11 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.48.5", ] [[package]] @@ -6693,17 +9331,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 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", + "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]] @@ -6768,9 +9406,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" @@ -6786,9 +9424,9 @@ checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" @@ -6804,9 +9442,9 @@ checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" @@ -6834,9 +9472,9 @@ checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" @@ -6852,9 +9490,9 @@ checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" @@ -6870,9 +9508,9 @@ checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" @@ -6888,9 +9526,9 @@ checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" @@ -6906,19 +9544,13 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.5.40" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" - [[package]] name = "winnow" version = "1.0.4" @@ -6938,6 +9570,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "wit-bindgen" version = "0.57.1" @@ -6950,50 +9591,6 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" -[[package]] -name = "wry" -version = "0.55.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" -dependencies = [ - "base64 0.22.1", - "block2", - "cookie", - "crossbeam-channel", - "dirs", - "dom_query", - "dpi", - "dunce", - "gdkx11", - "gtk", - "http", - "javascriptcore-rs", - "jni", - "libc", - "ndk", - "objc2", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-foundation", - "objc2-ui-kit", - "objc2-web-kit", - "once_cell", - "percent-encoding", - "raw-window-handle", - "sha2 0.10.9", - "soup3", - "tao-macros", - "thiserror 2.0.20", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.61.3", - "windows-core 0.61.2", - "windows-version", - "x11-dl", -] - [[package]] name = "wyz" version = "0.5.1" @@ -7014,22 +9611,125 @@ dependencies = [ ] [[package]] -name = "x11-dl" -version = "2.21.0" +name = "x11rb" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ + "as-raw-xcb-connection", + "gethostname", "libc", - "once_cell", - "pkg-config", + "rustix", + "x11rb-protocol", + "xcursor", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcb" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c2ad15e0e922856ee89afe862b8992334bbe7953adad56cd1199358cb30566" +dependencies = [ + "bitflags 2.13.2", + "libc", + "quick-xml 0.41.0", + "x11", +] + +[[package]] +name = "xcursor" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23" + +[[package]] +name = "xim-ctext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac61a7062c40f3c37b6e82eeeef835d5cc7824b632a72784a89b3963c33284c" +dependencies = [ + "encoding_rs", +] + +[[package]] +name = "xim-parser" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dcee45f89572d5a65180af3a84e7ddb24f5ea690a6d3aa9de231281544dd7b7" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "xkbcommon" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9" +dependencies = [ + "as-raw-xcb-connection", + "libc", + "memmap2", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e450f9b2ed1dff33c94c12589a87338689467b9c4f5d8a5710bd09a847d2c8a7" + +[[package]] +name = "xml5ever" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69" +dependencies = [ + "log", + "mac", + "markup5ever 0.12.1", ] +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "y4m" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" +[[package]] +name = "yazi" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" + +[[package]] +name = "yeslogic-fontconfig-sys" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8b8abf912b9a29ff112e1671c97c33636903d13a69712037190e6805af4f76" +dependencies = [ + "dlib", + "once_cell", + "pkg-config", +] + [[package]] name = "yoke" version = "0.8.3" @@ -7088,13 +9788,37 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zbus-lockstep" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" +dependencies = [ + "zbus_xml", + "zvariant", +] + +[[package]] +name = "zbus-lockstep-macros" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] + [[package]] name = "zbus_macros" version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 3.0.5", @@ -7114,6 +9838,18 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zbus_xml" +version = "5.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1586c021a01ca0a9216dcd874e546382e156a5cbab5fab6cb5f10087e22682a" +dependencies = [ + "serde", + "winnow 1.0.4", + "zbus_names", + "zvariant", +] + [[package]] name = "zcheapstr" version = "1.1.0" @@ -7123,6 +9859,73 @@ dependencies = [ "serde", ] +[[package]] +name = "zed-font-kit" +version = "0.14.1-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3898e450f36f852edda72e3f985c34426042c4951790b23b107f93394f9bff5" +dependencies = [ + "bitflags 2.13.2", + "byteorder", + "core-foundation 0.10.1", + "core-graphics 0.24.0", + "core-text", + "dirs 5.0.1", + "dwrote", + "float-ord", + "freetype-sys", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "zed-scap" +version = "0.0.8-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b338d705ae33a43ca00287c11129303a7a0aa57b101b72a1c08c863f698ac8" +dependencies = [ + "anyhow", + "cocoa 0.25.0", + "core-graphics-helmer-fork", + "log", + "objc", + "rand 0.8.8", + "screencapturekit", + "screencapturekit-sys", + "sysinfo", + "tao-core-video-sys", + "windows 0.61.3", + "windows-capture", + "x11", + "xcb", +] + +[[package]] +name = "zed-xim" +version = "0.4.0-zed" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0b46ed118eba34d9ba53d94ddc0b665e0e06a2cf874cfa2dd5dec278148642" +dependencies = [ + "ahash 0.8.12", + "hashbrown 0.14.5", + "log", + "x11rb", + "xim-ctext", + "xim-parser", +] + +[[package]] +name = "zeno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" + [[package]] name = "zerocopy" version = "0.8.57" @@ -7258,6 +10061,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-core" version = "0.5.3" @@ -7273,13 +10082,22 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core 0.4.12", +] + [[package]] name = "zune-jpeg" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core", + "zune-core 0.5.3", ] [[package]] @@ -7291,6 +10109,8 @@ dependencies = [ "endi", "enumflags2", "serde", + "serde_bytes", + "url", "winnow 1.0.4", "zcheapstr", "zvariant_derive", @@ -7303,7 +10123,7 @@ version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 3.0.5", diff --git a/src-tauri/Cargo.toml b/app/Cargo.toml similarity index 73% rename from src-tauri/Cargo.toml rename to app/Cargo.toml index abdd7b8..f79a4c0 100644 --- a/src-tauri/Cargo.toml +++ b/app/Cargo.toml @@ -13,15 +13,11 @@ path = "src/main.rs" [lib] name = "grimoire_lib" -crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2", features = [] } +embed-resource = "3" [dependencies] -tauri = { version = "2", features = ["protocol-asset", "macos-private-api"] } -tauri-plugin-opener = "2" -tauri-plugin-dialog = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" rusqlite = { version = "0.32", features = ["bundled", "functions"] } @@ -43,6 +39,14 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "rus kindling-mobi = "0.42" mobi = "0.8" rars = "0.9.4" +gpui-kit = "0.7" +rfd = "0.15" +image = { version = "0.25", default-features = false, features = ["jpeg", "png", "gif", "webp"] } + +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.6" +objc2-app-kit = { version = "0.3", default-features = false, features = ["NSApplication", "NSImage"] } +objc2-foundation = { version = "0.3", default-features = false, features = ["NSData"] } [profile.release] codegen-units = 1 diff --git a/src-tauri/app-icon.png b/app/app-icon.png similarity index 100% rename from src-tauri/app-icon.png rename to app/app-icon.png diff --git a/app/build.rs b/app/build.rs new file mode 100644 index 0000000..1dc06c7 --- /dev/null +++ b/app/build.rs @@ -0,0 +1,10 @@ +fn main() { + println!("cargo:rerun-if-changed=icons/icon.rc"); + println!("cargo:rerun-if-changed=icons/icon.ico"); + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + if target_os == "windows" { + embed_resource::compile("icons/icon.rc", embed_resource::NONE) + .manifest_optional() + .unwrap(); + } +} diff --git a/src-tauri/icons/128x128.png b/app/icons/128x128.png similarity index 100% rename from src-tauri/icons/128x128.png rename to app/icons/128x128.png diff --git a/src-tauri/icons/128x128@2x.png b/app/icons/128x128@2x.png similarity index 100% rename from src-tauri/icons/128x128@2x.png rename to app/icons/128x128@2x.png diff --git a/src-tauri/icons/32x32.png b/app/icons/32x32.png similarity index 100% rename from src-tauri/icons/32x32.png rename to app/icons/32x32.png diff --git a/src-tauri/icons/64x64.png b/app/icons/64x64.png similarity index 100% rename from src-tauri/icons/64x64.png rename to app/icons/64x64.png diff --git a/src-tauri/icons/Square107x107Logo.png b/app/icons/Square107x107Logo.png similarity index 100% rename from src-tauri/icons/Square107x107Logo.png rename to app/icons/Square107x107Logo.png diff --git a/src-tauri/icons/Square142x142Logo.png b/app/icons/Square142x142Logo.png similarity index 100% rename from src-tauri/icons/Square142x142Logo.png rename to app/icons/Square142x142Logo.png diff --git a/src-tauri/icons/Square150x150Logo.png b/app/icons/Square150x150Logo.png similarity index 100% rename from src-tauri/icons/Square150x150Logo.png rename to app/icons/Square150x150Logo.png diff --git a/src-tauri/icons/Square284x284Logo.png b/app/icons/Square284x284Logo.png similarity index 100% rename from src-tauri/icons/Square284x284Logo.png rename to app/icons/Square284x284Logo.png diff --git a/src-tauri/icons/Square30x30Logo.png b/app/icons/Square30x30Logo.png similarity index 100% rename from src-tauri/icons/Square30x30Logo.png rename to app/icons/Square30x30Logo.png diff --git a/src-tauri/icons/Square310x310Logo.png b/app/icons/Square310x310Logo.png similarity index 100% rename from src-tauri/icons/Square310x310Logo.png rename to app/icons/Square310x310Logo.png diff --git a/src-tauri/icons/Square44x44Logo.png b/app/icons/Square44x44Logo.png similarity index 100% rename from src-tauri/icons/Square44x44Logo.png rename to app/icons/Square44x44Logo.png diff --git a/src-tauri/icons/Square71x71Logo.png b/app/icons/Square71x71Logo.png similarity index 100% rename from src-tauri/icons/Square71x71Logo.png rename to app/icons/Square71x71Logo.png diff --git a/src-tauri/icons/Square89x89Logo.png b/app/icons/Square89x89Logo.png similarity index 100% rename from src-tauri/icons/Square89x89Logo.png rename to app/icons/Square89x89Logo.png diff --git a/src-tauri/icons/StoreLogo.png b/app/icons/StoreLogo.png similarity index 100% rename from src-tauri/icons/StoreLogo.png rename to app/icons/StoreLogo.png diff --git a/src-tauri/icons/icon.icns b/app/icons/icon.icns similarity index 100% rename from src-tauri/icons/icon.icns rename to app/icons/icon.icns diff --git a/src-tauri/icons/icon.ico b/app/icons/icon.ico similarity index 100% rename from src-tauri/icons/icon.ico rename to app/icons/icon.ico diff --git a/src-tauri/icons/icon.png b/app/icons/icon.png similarity index 100% rename from src-tauri/icons/icon.png rename to app/icons/icon.png diff --git a/app/icons/icon.rc b/app/icons/icon.rc new file mode 100644 index 0000000..a1ec328 --- /dev/null +++ b/app/icons/icon.rc @@ -0,0 +1 @@ +1 ICON "icon.ico" diff --git a/src-tauri/icons/ios/AppIcon-20x20@1x.png b/app/icons/ios/AppIcon-20x20@1x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-20x20@1x.png rename to app/icons/ios/AppIcon-20x20@1x.png diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/app/icons/ios/AppIcon-20x20@2x-1.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-20x20@2x-1.png rename to app/icons/ios/AppIcon-20x20@2x-1.png diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x.png b/app/icons/ios/AppIcon-20x20@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-20x20@2x.png rename to app/icons/ios/AppIcon-20x20@2x.png diff --git a/src-tauri/icons/ios/AppIcon-20x20@3x.png b/app/icons/ios/AppIcon-20x20@3x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-20x20@3x.png rename to app/icons/ios/AppIcon-20x20@3x.png diff --git a/src-tauri/icons/ios/AppIcon-29x29@1x.png b/app/icons/ios/AppIcon-29x29@1x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-29x29@1x.png rename to app/icons/ios/AppIcon-29x29@1x.png diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/app/icons/ios/AppIcon-29x29@2x-1.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-29x29@2x-1.png rename to app/icons/ios/AppIcon-29x29@2x-1.png diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x.png b/app/icons/ios/AppIcon-29x29@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-29x29@2x.png rename to app/icons/ios/AppIcon-29x29@2x.png diff --git a/src-tauri/icons/ios/AppIcon-29x29@3x.png b/app/icons/ios/AppIcon-29x29@3x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-29x29@3x.png rename to app/icons/ios/AppIcon-29x29@3x.png diff --git a/src-tauri/icons/ios/AppIcon-40x40@1x.png b/app/icons/ios/AppIcon-40x40@1x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-40x40@1x.png rename to app/icons/ios/AppIcon-40x40@1x.png diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/app/icons/ios/AppIcon-40x40@2x-1.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-40x40@2x-1.png rename to app/icons/ios/AppIcon-40x40@2x-1.png diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x.png b/app/icons/ios/AppIcon-40x40@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-40x40@2x.png rename to app/icons/ios/AppIcon-40x40@2x.png diff --git a/src-tauri/icons/ios/AppIcon-40x40@3x.png b/app/icons/ios/AppIcon-40x40@3x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-40x40@3x.png rename to app/icons/ios/AppIcon-40x40@3x.png diff --git a/src-tauri/icons/ios/AppIcon-512@2x.png b/app/icons/ios/AppIcon-512@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-512@2x.png rename to app/icons/ios/AppIcon-512@2x.png diff --git a/src-tauri/icons/ios/AppIcon-60x60@2x.png b/app/icons/ios/AppIcon-60x60@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-60x60@2x.png rename to app/icons/ios/AppIcon-60x60@2x.png diff --git a/src-tauri/icons/ios/AppIcon-60x60@3x.png b/app/icons/ios/AppIcon-60x60@3x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-60x60@3x.png rename to app/icons/ios/AppIcon-60x60@3x.png diff --git a/src-tauri/icons/ios/AppIcon-76x76@1x.png b/app/icons/ios/AppIcon-76x76@1x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-76x76@1x.png rename to app/icons/ios/AppIcon-76x76@1x.png diff --git a/src-tauri/icons/ios/AppIcon-76x76@2x.png b/app/icons/ios/AppIcon-76x76@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-76x76@2x.png rename to app/icons/ios/AppIcon-76x76@2x.png diff --git a/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/app/icons/ios/AppIcon-83.5x83.5@2x.png similarity index 100% rename from src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png rename to app/icons/ios/AppIcon-83.5x83.5@2x.png diff --git a/src-tauri/macos/How to install.txt b/app/macos/How to install.txt similarity index 100% rename from src-tauri/macos/How to install.txt rename to app/macos/How to install.txt diff --git a/src-tauri/macos/Install Grimoire.command b/app/macos/Install Grimoire.command similarity index 100% rename from src-tauri/macos/Install Grimoire.command rename to app/macos/Install Grimoire.command diff --git a/src-tauri/migrations/001_init.sql b/app/migrations/001_init.sql similarity index 100% rename from src-tauri/migrations/001_init.sql rename to app/migrations/001_init.sql diff --git a/src-tauri/migrations/002_reading_progress.sql b/app/migrations/002_reading_progress.sql similarity index 100% rename from src-tauri/migrations/002_reading_progress.sql rename to app/migrations/002_reading_progress.sql diff --git a/src-tauri/src/comic.rs b/app/src/comic.rs similarity index 96% rename from src-tauri/src/comic.rs rename to app/src/comic.rs index 7d833d0..da5b18c 100644 --- a/src-tauri/src/comic.rs +++ b/app/src/comic.rs @@ -3,7 +3,6 @@ use crate::error::{err, AppResult}; use crate::metadata::cover_file_info; use base64::{engine::general_purpose::STANDARD, Engine as _}; -use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::Read; use std::path::Path; @@ -11,23 +10,6 @@ use zip::ZipArchive; const IMAGE_EXTS: &[&str] = &["jpg", "jpeg", "png", "gif", "webp", "bmp"]; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ComicPageList { - pub book_id: i64, - pub format: String, - pub page_count: usize, - pub pages: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ComicPage { - pub book_id: i64, - pub index: usize, - pub page_count: usize, - pub name: String, - pub data_url: String, -} - pub fn list_comic_image_names(path: &Path) -> AppResult> { let ext = path .extension() diff --git a/src-tauri/src/config.rs b/app/src/config.rs similarity index 62% rename from src-tauri/src/config.rs rename to app/src/config.rs index 59b61ae..aa0c55a 100644 --- a/src-tauri/src/config.rs +++ b/app/src/config.rs @@ -3,9 +3,33 @@ use serde::{Deserialize, Serialize}; use std::fs; use std::path::{Path, PathBuf}; -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppConfig { pub library_path: Option, + /// `light`, `dark`, or `system`. + #[serde(default = "default_theme")] + pub theme: String, + /// `publisher`, `serif`, `sans`, or `opendyslexic`. + #[serde(default = "default_reader_font")] + pub reader_font: String, +} + +fn default_theme() -> String { + "dark".into() +} + +fn default_reader_font() -> String { + "serif".into() +} + +impl Default for AppConfig { + fn default() -> Self { + Self { + library_path: None, + theme: default_theme(), + reader_font: default_reader_font(), + } + } } fn config_path() -> AppResult { @@ -58,6 +82,20 @@ pub fn clear_library_path() -> AppResult { Ok(config) } +pub fn set_theme_preference(theme: &str) -> AppResult { + let mut config = load_config()?; + config.theme = theme.to_string(); + save_config(&config)?; + Ok(config) +} + +pub fn set_reader_font(font: &str) -> AppResult { + let mut config = load_config()?; + config.reader_font = font.to_string(); + save_config(&config)?; + Ok(config) +} + #[cfg(test)] mod tests { use super::*; @@ -73,7 +111,17 @@ mod tests { fn parse_config_treats_empty_as_default() { let config = parse_config("").expect("empty config should default"); assert!(config.library_path.is_none()); + assert_eq!(config.theme, "dark"); + assert_eq!(config.reader_font, "serif"); let config = parse_config("\u{feff}").expect("BOM-only config should default"); assert!(config.library_path.is_none()); } + + #[test] + fn missing_theme_and_reader_font_use_defaults() { + let config = parse_config(r#"{"library_path":"C:\\Books"}"#).expect("partial config"); + assert_eq!(config.library_path.as_deref(), Some("C:\\Books")); + assert_eq!(config.theme, "dark"); + assert_eq!(config.reader_font, "serif"); + } } diff --git a/src-tauri/src/convert.rs b/app/src/convert.rs similarity index 100% rename from src-tauri/src/convert.rs rename to app/src/convert.rs diff --git a/src-tauri/src/db.rs b/app/src/db.rs similarity index 100% rename from src-tauri/src/db.rs rename to app/src/db.rs diff --git a/src-tauri/src/error.rs b/app/src/error.rs similarity index 100% rename from src-tauri/src/error.rs rename to app/src/error.rs diff --git a/src-tauri/src/kf8_extract.rs b/app/src/kf8_extract.rs similarity index 100% rename from src-tauri/src/kf8_extract.rs rename to app/src/kf8_extract.rs diff --git a/src-tauri/src/kindle.rs b/app/src/kindle.rs similarity index 97% rename from src-tauri/src/kindle.rs rename to app/src/kindle.rs index bdb41f1..259816f 100644 --- a/src-tauri/src/kindle.rs +++ b/app/src/kindle.rs @@ -80,18 +80,6 @@ pub struct EbookReaderDevice { pub notes: Option, } -/// Backward-compatible alias used by existing Kindle send paths. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct KindleDeviceInfo { - pub location_id: u64, - pub manufacturer: String, - pub product: String, - pub serial_number: Option, - pub is_kindle: bool, - pub free_space_bytes: Option, - pub display: String, -} - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TransferResult { pub remote_path: String, @@ -195,26 +183,6 @@ pub async fn list_ebook_readers() -> AppResult> { Ok(out) } -pub async fn list_kindles() -> AppResult> { - let readers = list_ebook_readers().await?; - Ok(readers - .into_iter() - .filter(|d| d.family == ReaderFamily::Kindle) - .filter_map(|d| { - let location_id = d.location_id?; - Some(KindleDeviceInfo { - location_id, - manufacturer: d.manufacturer, - product: d.product, - serial_number: d.serial_number, - is_kindle: true, - free_space_bytes: d.free_space_bytes, - display: d.display, - }) - }) - .collect()) -} - async fn list_mtp_readers() -> AppResult> { let devices = match MtpDevice::list_devices() { Ok(d) => d, diff --git a/app/src/lib.rs b/app/src/lib.rs new file mode 100644 index 0000000..bc9e8c9 --- /dev/null +++ b/app/src/lib.rs @@ -0,0 +1,17 @@ +mod comic; +mod config; +mod convert; +mod db; +mod error; +mod kf8_extract; +mod kindle; +mod library; +mod metadata; +mod openlibrary; +mod reader; +mod state; +mod ui; + +pub fn run() { + ui::launch(); +} diff --git a/src-tauri/src/library.rs b/app/src/library.rs similarity index 98% rename from src-tauri/src/library.rs rename to app/src/library.rs index c24d553..9cfa691 100644 --- a/src-tauri/src/library.rs +++ b/app/src/library.rs @@ -3,7 +3,6 @@ use crate::error::{err, AppResult}; use crate::openlibrary::MetadataMatch; use crate::metadata::{cover_file_info, extract_from_file}; use crate::state::LibrarySession; -use base64::{engine::general_purpose::STANDARD, Engine as _}; use chrono::Utc; use rusqlite::{params, Connection, OptionalExtension}; use serde::{Deserialize, Serialize}; @@ -743,7 +742,7 @@ fn patch_one_book(conn: &mut Connection, id: i64, patch: &BookBulkPatch) -> AppR Ok(()) } -pub fn cover_data_url(conn: &Connection, root: &Path, id: i64) -> AppResult> { +pub fn cover_path(conn: &Connection, root: &Path, id: i64) -> AppResult> { let (path, has_cover): (String, i64) = conn.query_row( "SELECT path, has_cover FROM books WHERE id = ?1", [id], @@ -753,24 +752,17 @@ pub fn cover_data_url(conn: &Connection, root: &Path, id: i64) -> AppResult, + pub kindle_convert_available: bool, +} + +pub fn status(state: &AppState) -> AppResult { + let config = load_config()?; + Ok(LibraryStatus { + open: state.is_open(), + path: config.library_path, + kindle_convert_available: kindle_convert_available(), + }) +} + +pub fn open_saved(state: &AppState) -> AppResult { + let config = load_config()?; + let Some(path) = config.library_path else { + return status(state); + }; + let root = PathBuf::from(&path); + if !root.exists() { + return status(state); + } + let session = if root.join("metadata.db").exists() { + crate::library::open_library(&root)? + } else { + crate::library::create_library(&root)? + }; + state.set_session(session); + status(state) +} + +pub fn create_or_open(state: &AppState, path: &Path) -> AppResult { + let session = if path.join("metadata.db").exists() { + crate::library::open_library(path)? + } else { + crate::library::create_library(path)? + }; + set_library_path(path)?; + state.set_session(session); + status(state) +} + +pub fn clear_library(state: &AppState) -> AppResult { + clear_library_path()?; + state.clear_session(); + status(state) +} + +pub fn list(state: &AppState, query: Option<&str>) -> AppResult> { + state.with_conn(|conn, _| list_books(conn, query)) +} + +pub fn covers_for(state: &AppState, books: &[BookSummary]) -> std::collections::HashMap { + let mut covers = std::collections::HashMap::new(); + let _ = state.with_conn(|conn, root| { + for book in books { + if book.has_cover { + if let Ok(Some(path)) = cover_path(conn, root, book.id) { + covers.insert(book.id, path); + } + } + } + Ok(()) + }); + covers +} + +pub fn import(state: &AppState, path: &Path) -> AppResult { + state.with_conn_mut(|conn, root| import_book(conn, root, path)) +} + +pub fn book(state: &AppState, id: i64) -> AppResult { + state.with_conn(|conn, root| get_book(conn, root, id)) +} + +pub fn save_book(state: &AppState, id: i64, update: BookUpdate) -> AppResult { + state.with_conn_mut(|conn, root| update_book(conn, root, id, update)) +} + +pub fn remove_book(state: &AppState, id: i64) -> AppResult<()> { + state.with_conn_mut(|conn, root| delete_book(conn, root, id)) +} + +pub fn remove_books(state: &AppState, ids: &[i64]) -> AppResult { + state.with_conn_mut(|conn, root| delete_ebooks(conn, root, ids)) +} + +pub fn patch_books(state: &AppState, ids: &[i64], patch: BookBulkPatch) -> AppResult { + state.with_conn_mut(|conn, root| bulk_patch_ebooks(conn, root, ids, &patch)) +} + +pub fn convert_targets(state: &AppState, id: i64) -> AppResult> { + state.with_conn(|conn, _| list_convertible_targets(conn, id)) +} + +pub fn convert(state: &AppState, id: i64, target: &str) -> AppResult { + state.with_conn_mut(|conn, root| convert_book_format(conn, root, id, target)) +} + +pub fn reader_source(state: &AppState, id: i64) -> AppResult { + state.with_conn_mut(|conn, root| resolve_reader_source(conn, root, id, None)) +} + +pub fn progress(state: &AppState, id: i64) -> AppResult> { + state.with_conn(|conn, _| get_reading_progress(conn, id)) +} + +pub fn save_progress(state: &AppState, id: i64, update: ReadingProgressUpdate) -> AppResult { + state.with_conn(|conn, _| save_reading_progress(conn, id, update)) +} + +pub fn comic_page(path: &Path, index: usize) -> AppResult<(String, String, usize)> { + comic_page_data_url(path, index) +} + +pub fn sync_history(state: &AppState) -> AppResult> { + state.with_conn(|conn, _| list_sync_records(conn)) +} + +pub async fn readers() -> AppResult> { + list_ebook_readers().await +} + +pub async fn scan_device(id: String) -> AppResult { + scan_device_library(id).await +} + +pub async fn delete_on_device(id: String, books: Vec) -> AppResult { + delete_device_books(id, books).await +} + +pub async fn send(state: &AppState, book_id: i64, device_id: &str) -> AppResult { + let ready = state.with_conn_mut(|conn, root| resolve_kindle_file(conn, root, book_id))?; + let local = PathBuf::from(&ready.path); + let remote_name = local + .file_name() + .map(|name| name.to_string_lossy().to_string()) + .unwrap_or_else(|| format!("book.{}", ready.format.to_ascii_lowercase())); + let (remote_path, serial) = send_file_to_kindle(device_id, &local, &remote_name).await?; + state.with_conn(|conn, _| record_sync(conn, book_id, &serial, &remote_path, &ready.format))?; + Ok(TransferResult { + remote_path, + device_serial: serial, + format: ready.format, + }) +} + +pub async fn enrich(state: &AppState, id: i64) -> AppResult { + let (title, authors, isbn, has_cover) = state.with_conn(|conn, root| { + let book = get_book(conn, root, id)?; + let isbn = isbn_of(&book); + Ok((book.title, book.authors, isbn, book.has_cover)) + })?; + let hit = lookup_book(&title, &authors, isbn.as_deref()) + .await? + .ok_or_else(|| err("No Open Library match found for this title/author/ISBN."))?; + let cover_bytes = if !has_cover { + match download_cover_from_urls(&hit.cover_urls).await { + Ok(bytes) => Some(bytes), + Err(error) => { + eprintln!("Grimoire cover download skipped: {error}"); + None + } + } + } else { + None + }; + state.with_conn_mut(|conn, root| { + let current = get_book(conn, root, id)?; + apply_remote_match(conn, root, id, ¤t, &hit, cover_bytes.as_deref()) + }) +} + +pub async fn cover_candidates(state: &AppState, id: i64) -> AppResult> { + let (title, authors, isbn) = state.with_conn(|conn, root| { + let book = get_book(conn, root, id)?; + let isbn = isbn_of(&book); + Ok((book.title, book.authors, isbn)) + })?; + let matches = lookup_book_candidates(&title, &authors, isbn.as_deref(), 12).await?; + let candidates = cover_candidates_from_matches(&matches); + if candidates.is_empty() { + return Err(err( + "No cover images found on Open Library for this title/author/ISBN.", + )); + } + Ok(candidates) +} + +pub async fn apply_cover(state: &AppState, id: i64, urls: &[String]) -> AppResult { + if urls.is_empty() { + return Err(err("Pick a cover first.")); + } + state.with_conn(|conn, root| { + let _ = get_book(conn, root, id)?; + Ok(()) + })?; + let bytes = download_cover_from_urls(urls).await?; + state.with_conn_mut(|conn, root| set_book_cover(conn, root, id, &bytes)) +} + +pub fn config() -> AppResult { + load_config() +} + +pub fn save_reader_font(font: &str) -> AppResult { + set_reader_font(font) +} + +pub fn isbn_of(book: &BookDetail) -> Option { + book.identifiers + .iter() + .find(|id| id.type_name.eq_ignore_ascii_case("isbn")) + .map(|id| id.value.clone()) +} + +pub fn with_isbn(book: &BookDetail, isbn: &str) -> Vec { + let preserved = book + .identifiers + .iter() + .filter(|id| !id.type_name.eq_ignore_ascii_case("isbn")) + .cloned() + .collect::>(); + let trimmed = isbn.trim(); + if trimmed.is_empty() { + preserved + } else { + let mut ids = vec![IdentifierInfo { + type_name: "isbn".into(), + value: trimmed.to_string(), + }]; + ids.extend(preserved); + ids + } +} + +pub const READABLE: &[&str] = &["EPUB", "PDF", "TXT", "MOBI", "CBZ", "CBR"]; +pub const SUPPORTED_EXT: &[&str] = &["epub", "pdf", "azw3", "mobi", "txt", "cbz", "cbr"]; + +pub fn can_read(formats: &[String]) -> bool { + formats + .iter() + .any(|format| READABLE.iter().any(|ok| format.eq_ignore_ascii_case(ok))) +} + +pub fn extension_of(path: &Path) -> String { + path.extension() + .and_then(|ext| ext.to_str()) + .unwrap_or("") + .to_ascii_lowercase() +} diff --git a/app/src/ui/book.rs b/app/src/ui/book.rs new file mode 100644 index 0000000..415d2cd --- /dev/null +++ b/app/src/ui/book.rs @@ -0,0 +1,531 @@ +use gpui_kit::component::button::{Button, ButtonVariants as _}; +use gpui_kit::component::input::{Input, Textarea}; +use gpui_kit::component::{ActiveTheme as _, Disableable as _, WindowExt as _}; +use gpui_kit::prelude::{ + FluentBuilder as _, InteractiveElement as _, StatefulInteractiveElement as _, Styled as _, + StyledImage as _, +}; +use gpui_kit::{Context, IntoElement, ObjectFit, ParentElement as _, Window, div, img, px}; + +use super::backend; +use super::shell::Shell; +use super::theme::DISPLAY; +use crate::library::{BookDetail, BookUpdate}; + +impl Shell { + pub fn book_page(&mut self, window: &mut Window, cx: &mut Context) -> gpui_kit::AnyElement { + let Some(id) = self.book_id else { + return div().child("No book selected.").into_any_element(); + }; + if self.book.as_ref().map(|book| book.id) != Some(id) && !self.busy && self.error.is_empty() + { + self.load_book(id, window, cx); + } + let title = self.book.as_ref().map(|book| book.title.clone()).unwrap_or_else(|| "Book".into()); + let cover = self.book_cover.clone(); + let formats = self + .book + .as_ref() + .map(|book| { + book.formats + .iter() + .map(|format| format.format.clone()) + .collect::>() + .join(", ") + }) + .unwrap_or_default(); + let can_read = self + .book + .as_ref() + .map(|book| backend::can_read(&book.formats.iter().map(|format| format.format.clone()).collect::>())) + .unwrap_or(false); + let targets = self.convert_targets.clone(); + let devices = self.devices.clone(); + + div() + .id("book-page") + .size_full() + .overflow_y_scroll() + .p(px(24.)) + .flex() + .flex_col() + .gap(px(14.)) + .child( + Button::new("back-library") + .ghost() + .label("Back to library") + .on_click(cx.listener(|this, _, window, cx| { + this.book_id = None; + this.book = None; + this.reload_books(window, cx); + })), + ) + .child(div().font_family(DISPLAY).text_size(px(28.)).child(title)) + .when(!self.message.is_empty(), |this| { + this.child(div().text_color(cx.theme().primary).child(self.message.clone())) + }) + .when(!self.error.is_empty(), |this| { + this.child(div().text_color(cx.theme().danger).child(self.error.clone())) + }) + .child( + div().flex().gap(px(20.)).child(book_cover(cover, cx)).child( + div() + .flex_1() + .flex() + .flex_col() + .gap(px(8.)) + .child(field("Title", Input::new(&self.title_input))) + .child(field("Authors", Input::new(&self.authors_input))) + .child(field("Tags", Input::new(&self.tags_input))) + .child(field("Series", Input::new(&self.series_input))) + .child(field("Series index", Input::new(&self.series_index_input))) + .child(field("ISBN", Input::new(&self.isbn_input))) + .child(field("Notes", Textarea::new(&self.comment_input).h(px(90.)))), + ), + ) + .child( + div() + .flex() + .flex_wrap() + .gap(px(8.)) + .child( + Button::new("save-book") + .primary() + .label(if self.busy { "Working…" } else { "Save" }) + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.save_book_form(window, cx))), + ) + .child( + Button::new("fetch-ol") + .outline() + .label("Fetch from Open Library") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.fetch_book(window, cx))), + ) + .child( + Button::new("choose-cover") + .outline() + .label("Choose cover") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.choose_cover(window, cx))), + ) + .when(can_read, |this| { + this.child( + Button::new("read-book") + .outline() + .label("Read") + .on_click(cx.listener(|this, _, _, cx| { + if let Some(id) = this.book_id { + this.open_reader(id, cx); + } + })), + ) + }) + .child( + Button::new("delete-book") + .danger() + .label("Delete") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.confirm_delete_book(window, cx))), + ), + ) + .child( + div() + .text_color(cx.theme().muted_foreground) + .child(format!("Formats: {formats}")), + ) + .when(!targets.is_empty(), |this| { + let buttons = targets.into_iter().map(|target| { + let label = format!("Convert to {target}"); + let target_format = target.clone(); + Button::new(SharedId(format!("convert-{target}"))) + .outline() + .label(label) + .disabled(self.busy) + .on_click(cx.listener(move |this, _, window, cx| { + this.convert_to(target_format.clone(), window, cx); + })) + }); + this.child(div().flex().flex_wrap().gap(px(8.)).children(buttons)) + }) + .child(div().font_family(DISPLAY).text_size(px(18.)).child("Send to device")) + .children(devices.into_iter().map(|device| { + let id = device.id.clone(); + let label = format!( + "{} · {}", + device.product, + if device.supports_send { "Send" } else { "Detected" } + ); + Button::new(SharedId(format!("send-{}", device.id))) + .outline() + .label(label) + .disabled(self.busy || !device.supports_send) + .on_click(cx.listener(move |this, _, window, cx| { + this.send_open_book(id.clone(), window, cx); + })) + })) + .into_any_element() + } + + pub fn load_book(&mut self, id: i64, window: &mut Window, cx: &mut Context) { + if self.busy { + return; + } + let state = self.state.clone(); + self.busy = true; + self.error.clear(); + self.spawn_io( + window, + cx, + move || { + let book = backend::book(&state, id).map_err(|e| e.to_string())?; + let cover = state + .with_conn(|conn, root| crate::library::cover_path(conn, root, id)) + .ok() + .flatten(); + let targets = backend::convert_targets(&state, id).unwrap_or_default(); + Ok((book, cover, targets)) + }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok((book, cover, targets)) => { + this.sync_form(&book, window, cx); + this.book_cover = cover; + this.convert_targets = targets; + this.book = Some(book); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn sync_form(&mut self, book: &BookDetail, window: &mut Window, cx: &mut Context) { + let title = book.title.clone(); + let authors = book.authors.join(", "); + let tags = book.tags.join(", "); + let series = book.series.clone().unwrap_or_default(); + let index = book + .series_index + .map(|value| value.to_string()) + .unwrap_or_default(); + let isbn = backend::isbn_of(book).unwrap_or_default(); + let comment = book.comment.clone(); + self.title_input + .update(cx, |input, cx| input.set_value(title, window, cx)); + self.authors_input + .update(cx, |input, cx| input.set_value(authors, window, cx)); + self.tags_input + .update(cx, |input, cx| input.set_value(tags, window, cx)); + self.series_input + .update(cx, |input, cx| input.set_value(series, window, cx)); + self.series_index_input + .update(cx, |input, cx| input.set_value(index, window, cx)); + self.isbn_input + .update(cx, |input, cx| input.set_value(isbn, window, cx)); + self.comment_input + .update(cx, |input, cx| input.set_value(comment, window, cx)); + } + + fn save_book_form(&mut self, window: &mut Window, cx: &mut Context) { + let Some(book) = self.book.clone() else { + return; + }; + let title = self.title_input.read(cx).value().to_string(); + let authors = split_list(&self.authors_input.read(cx).value()); + let tags = split_list(&self.tags_input.read(cx).value()); + let series = self.series_input.read(cx).value().to_string(); + let index = self.series_index_input.read(cx).value().to_string(); + let isbn = self.isbn_input.read(cx).value().to_string(); + let comment = self.comment_input.read(cx).value().to_string(); + let update = BookUpdate { + title: if title.trim().is_empty() { + "Untitled".into() + } else { + title.trim().to_string() + }, + authors, + tags, + series: { + let series = series.trim().to_string(); + if series.is_empty() { None } else { Some(series) } + }, + series_index: index.trim().parse().ok(), + comment, + identifiers: backend::with_isbn(&book, &isbn), + }; + let state = self.state.clone(); + self.busy = true; + self.spawn_io( + window, + cx, + move || backend::save_book(&state, book.id, update).map_err(|e| e.to_string()), + |this, result, window, cx| { + this.busy = false; + match result { + Ok(book) => { + this.sync_form(&book, window, cx); + this.book = Some(book); + this.message = "Saved.".into(); + this.error.clear(); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn fetch_book(&mut self, window: &mut Window, cx: &mut Context) { + let Some(id) = self.book_id else { + return; + }; + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::enrich(&state, id).await.map_err(|e| e.to_string()) }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = if result.updated_fields.is_empty() { + "No missing fields to fill.".into() + } else { + format!("Updated: {}", result.updated_fields.join(", ")) + }; + this.sync_form(&result.book, window, cx); + let id = result.book.id; + this.book = Some(result.book); + if let Ok(cover) = this + .state + .with_conn(|conn, root| crate::library::cover_path(conn, root, id)) + { + this.book_cover = cover; + } + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn choose_cover(&mut self, window: &mut Window, cx: &mut Context) { + let Some(id) = self.book_id else { + return; + }; + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::cover_candidates(&state, id).await.map_err(|e| e.to_string()) }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok(candidates) => this.open_cover_picker(candidates, window, cx), + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn open_cover_picker( + &mut self, + candidates: Vec, + window: &mut Window, + cx: &mut Context, + ) { + let shell = cx.entity().clone(); + window.open_dialog(cx, move |dialog, _, _| { + let mut dialog = dialog.title("Choose cover"); + for candidate in &candidates { + let urls = candidate.download_urls.clone(); + let shell = shell.clone(); + let label = format!("{} · {}", candidate.title, candidate.authors); + dialog = dialog.child( + Button::new(SharedId(format!("cover-{}", candidate.id))) + .outline() + .label(label) + .on_click(move |_, _, cx| { + let urls = urls.clone(); + shell + .downgrade().update_in(cx, |this, window, cx| this.apply_picked_cover(urls, window, cx)) + .ok(); + }), + ); + } + dialog + }); + } + + fn apply_picked_cover(&mut self, urls: Vec, window: &mut Window, cx: &mut Context) { + let Some(id) = self.book_id else { + return; + }; + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::apply_cover(&state, id, &urls).await.map_err(|e| e.to_string()) }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok(book) => { + this.message = "Cover updated.".into(); + let id = book.id; + this.sync_form(&book, window, cx); + this.book = Some(book); + if let Ok(cover) = this + .state + .with_conn(|conn, root| crate::library::cover_path(conn, root, id)) + { + this.book_cover = cover; + } + window.close_dialog(cx); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn convert_to(&mut self, target: String, window: &mut Window, cx: &mut Context) { + let Some(id) = self.book_id else { + return; + }; + let state = self.state.clone(); + self.busy = true; + self.spawn_io( + window, + cx, + move || backend::convert(&state, id, &target).map_err(|e| e.to_string()), + |this, result, window, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = if result.converted { + format!("Converted to {}.", result.format) + } else { + format!("{} already available.", result.format) + }; + if let Some(id) = this.book_id { + this.busy = false; + this.load_book(id, window, cx); + } + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn send_open_book(&mut self, device_id: String, window: &mut Window, cx: &mut Context) { + let Some(id) = self.book_id else { + return; + }; + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::send(&state, id, &device_id).await.map_err(|e| e.to_string()) }, + |this, result, _, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = format!("Sent {} as {}.", result.format, result.remote_path); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn confirm_delete_book(&mut self, window: &mut Window, cx: &mut Context) { + let shell = cx.entity().clone(); + window.open_alert_dialog(cx, move |alert, _, _| { + alert + .confirm() + .title("Delete book") + .description("Delete this book from the library?") + .on_ok({ + let shell = shell.clone(); + move |_, _, cx| { + shell + .downgrade().update_in(cx, |this, window, cx| { + if let Some(id) = this.book_id { + let state = this.state.clone(); + this.spawn_io( + window, + cx, + move || backend::remove_book(&state, id).map_err(|e| e.to_string()), + |this, result, window, cx| { + if let Err(error) = result { + this.error = error; + cx.notify(); + return; + } + this.book_id = None; + this.book = None; + this.reload_books(window, cx); + }, + ); + } + }) + .ok(); + true + } + }) + }); + } +} + +fn field(label: &str, control: impl IntoElement) -> impl IntoElement { + div() + .flex() + .flex_col() + .gap(px(4.)) + .child(div().text_size(px(12.)).child(label.to_string())) + .child(control) +} + +fn book_cover(path: Option, cx: &gpui_kit::App) -> impl IntoElement { + div() + .w(px(160.)) + .h(px(240.)) + .rounded(px(6.)) + .overflow_hidden() + .bg(cx.theme().muted) + .child(match path { + Some(path) => img(path).size_full().object_fit(ObjectFit::Cover).into_any_element(), + None => div().size_full().into_any_element(), + }) +} + +fn split_list(value: &str) -> Vec { + value + .split(',') + .map(str::trim) + .filter(|part| !part.is_empty()) + .map(str::to_string) + .collect() +} + +/// ElementId from an owned string. GPUI accepts `Into`. +struct SharedId(String); + +impl From for gpui_kit::ElementId { + fn from(value: SharedId) -> Self { + value.0.into() + } +} diff --git a/app/src/ui/devices.rs b/app/src/ui/devices.rs new file mode 100644 index 0000000..7f0fc64 --- /dev/null +++ b/app/src/ui/devices.rs @@ -0,0 +1,404 @@ +use gpui_kit::component::button::{Button, ButtonVariants as _}; +use gpui_kit::component::checkbox::Checkbox; +use gpui_kit::component::input::Input; +use gpui_kit::component::{ActiveTheme as _, Disableable as _, Selectable as _, WindowExt as _}; +use gpui_kit::prelude::{ + FluentBuilder as _, InteractiveElement as _, StatefulInteractiveElement as _, Styled as _, +}; +use gpui_kit::{Context, IntoElement, ParentElement as _, Window, div, px}; + +use super::backend; +use super::shell::Shell; +use super::theme::DISPLAY; +use crate::kindle::{ConnectionKind, DeviceBook, DeviceBookTarget}; + +impl Shell { + pub fn devices_page(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let devices = self.devices.clone(); + let history = self.sync_history.clone(); + div() + .id("devices-page") + .size_full() + .overflow_y_scroll() + .p(px(24.)) + .flex() + .flex_col() + .gap(px(16.)) + .child( + div() + .flex() + .items_center() + .justify_between() + .child(div().flex().flex_col().gap(px(4.)).child( + div() + .font_family(DISPLAY) + .text_size(px(28.)) + .child("Devices"), + ).child( + div().text_color(cx.theme().muted_foreground).child( + "Detects Kindles, Kobos, BOOX, PocketBook, reMarkable, Tolino, and Nook over USB/MTP or mounted volumes.", + ), + )) + .child( + Button::new("refresh-devices") + .outline() + .label(if self.devices_loading { "Scanning…" } else { "Refresh" }) + .disabled(self.devices_loading) + .on_click(cx.listener(|this, _, window, cx| this.refresh_devices(window, cx))), + ), + ) + .when(!self.error.is_empty(), |this| { + this.child(div().text_color(cx.theme().danger).child(self.error.clone())) + }) + .child(panel_title("Connected eReaders", cx)) + .when(devices.is_empty(), |this| { + this.child( + div().text_color(cx.theme().muted_foreground).child( + "No eReaders found yet. Grimoire checks USB/MTP and mounted volumes every few seconds. On macOS, quit Android File Transfer if an MTP device stays locked.", + ), + ) + }) + .children(devices.into_iter().map(|device| { + let id = device.id.clone(); + let send = if device.supports_send { "Send ready" } else { "Detected" }; + let connection = match device.connection { + ConnectionKind::Mtp => "USB/MTP", + ConnectionKind::Mount => "Mounted volume", + }; + div() + .p(px(12.)) + .rounded(px(10.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .flex() + .items_center() + .justify_between() + .gap(px(12.)) + .child( + div() + .flex() + .flex_col() + .gap(px(2.)) + .child(device.product.clone()) + .child( + div() + .text_color(cx.theme().muted_foreground) + .child(format!( + "{} · {} · {} · prefers {}", + device.family_label, + connection, + format_bytes(device.free_space_bytes), + device.preferred_formats.join(", ") + )), + ), + ) + .child( + div().flex().items_center().gap(px(8.)).child( + Button::new(Id(format!("view-{id}"))) + .outline() + .label("View books") + .on_click(cx.listener(move |this, _, window, cx| { + this.device_id = Some(id.clone()); + this.device_library = None; + this.scan_device(window, cx); + })), + ).child(send), + ) + })) + .child(panel_title("Recent sends", cx)) + .when(history.is_empty(), |this| { + this.child(div().text_color(cx.theme().muted_foreground).child( + "No transfers recorded yet.", + )) + }) + .children(history.into_iter().take(20).map(|record| { + div().text_size(px(14.)).child(format!( + "Book {} · {} · {} · {}", + record.book_id, record.format, record.remote_path, record.last_sent_at + )) + })) + } + + pub fn refresh_devices(&mut self, window: &mut Window, cx: &mut Context) { + self.devices_loading = true; + let state = self.state.clone(); + self.spawn_task( + window, + cx, + async move { + let devices = backend::readers().await.map_err(|e| e.to_string())?; + let history = if state.is_open() { + backend::sync_history(&state).map_err(|e| e.to_string())? + } else { + Vec::new() + }; + Ok((devices, history)) + }, + |this, result, _, cx| { + this.devices_loading = false; + match result { + Ok((devices, history)) => { + this.devices = devices; + this.sync_history = history; + this.error.clear(); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + pub fn device_library_page(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let books = self + .device_library + .as_ref() + .map(|library| library.books.clone()) + .unwrap_or_default(); + let query = self.device_query.read(cx).value().to_string().to_lowercase(); + let filtered: Vec = books + .into_iter() + .filter(|book| { + query.is_empty() + || book.title.to_lowercase().contains(&query) + || book.filename.to_lowercase().contains(&query) + || book.format.to_lowercase().contains(&query) + }) + .collect(); + let name = self + .device_library + .as_ref() + .map(|library| library.device_name.clone()) + .unwrap_or_else(|| "Device".into()); + let selected = self.device_selected.len(); + + div() + .size_full() + .flex() + .flex_col() + .child( + div() + .flex() + .items_center() + .gap(px(8.)) + .px(px(16.)) + .py(px(10.)) + .border_b_1() + .border_color(cx.theme().border) + .child( + Button::new("back-devices") + .ghost() + .label("Devices") + .on_click(cx.listener(|this, _, _, cx| { + this.device_id = None; + this.device_library = None; + cx.notify(); + })), + ) + .child(div().font_family(DISPLAY).text_size(px(22.)).child(name)) + .child(div().flex_1().child(Input::new(&self.device_query))) + .child( + Button::new("device-select") + .ghost() + .label(if self.device_select_mode { "Done" } else { "Select" }) + .selected(self.device_select_mode) + .on_click(cx.listener(|this, _, _, cx| { + this.device_select_mode = !this.device_select_mode; + if !this.device_select_mode { + this.device_selected.clear(); + } + cx.notify(); + })), + ) + .when(self.device_select_mode && selected > 0, |this| { + this.child( + Button::new("device-delete") + .danger() + .label(format!("Delete {selected}")) + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.confirm_device_delete(window, cx))), + ) + }), + ) + .child( + div() + .id("device-books") + .flex_1() + .overflow_y_scroll() + .p(px(16.)) + .flex() + .flex_col() + .gap(px(8.)) + .when(self.device_library.is_none(), |this| { + this.child(div().text_color(cx.theme().muted_foreground).child("Scanning device…")) + }) + .when(!self.message.is_empty(), |this| { + this.child(div().text_color(cx.theme().primary).child(self.message.clone())) + }) + .when(!self.error.is_empty(), |this| { + this.child(div().text_color(cx.theme().danger).child(self.error.clone())) + }) + .children(filtered.into_iter().map(|book| { + let key = book_key(&book); + let checked = self.device_selected.contains(&key); + let select_mode = self.device_select_mode; + div() + .flex() + .items_center() + .gap(px(10.)) + .p(px(8.)) + .rounded(px(8.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .when(select_mode, |row| { + let key = key.clone(); + row.child( + Checkbox::new(Id(format!("dev-{key}"))) + .checked(checked) + .on_change(cx.listener(move |this, checked, _, cx| { + if *checked { + this.device_selected.insert(key.clone()); + } else { + this.device_selected.remove(&key); + } + cx.notify(); + })), + ) + }) + .child( + div() + .flex_1() + .flex() + .flex_col() + .child(book.title.clone()) + .child( + div().text_size(px(12.)).text_color(cx.theme().muted_foreground).child( + format!("{} · {} · {}", book.format, book.filename, format_size(book.size)), + ), + ), + ) + })), + ) + } + + pub fn scan_device(&mut self, window: &mut Window, cx: &mut Context) { + let Some(id) = self.device_id.clone() else { + return; + }; + self.busy = true; + self.error.clear(); + self.spawn_task( + window, + cx, + async move { backend::scan_device(id).await.map_err(|e| e.to_string()) }, + |this, result, _, cx| { + this.busy = false; + match result { + Ok(library) => this.device_library = Some(library), + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn confirm_device_delete(&mut self, window: &mut Window, cx: &mut Context) { + let count = self.device_selected.len(); + let shell = cx.entity().clone(); + window.open_alert_dialog(cx, move |alert, _, _| { + alert + .confirm() + .title(format!("Delete {count} books")) + .description("Remove the selected files from the device?") + .on_ok({ + let shell = shell.clone(); + move |_, _, cx| { + shell + .downgrade().update_in(cx, |this, window, cx| this.delete_device_selection(window, cx)) + .ok(); + true + } + }) + }); + } + + fn delete_device_selection(&mut self, window: &mut Window, cx: &mut Context) { + let Some(device_id) = self.device_id.clone() else { + return; + }; + let Some(library) = self.device_library.clone() else { + return; + }; + let targets = library + .books + .iter() + .filter(|book| self.device_selected.contains(&book_key(book))) + .map(|book| DeviceBookTarget { + path: book.path.clone(), + filename: book.filename.clone(), + handle: book.handle, + }) + .collect::>(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::delete_on_device(device_id, targets).await.map_err(|e| e.to_string()) }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = format!("Removed {} books from the device.", result.removed); + this.device_selected.clear(); + this.scan_device(window, cx); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } +} + +fn book_key(book: &DeviceBook) -> String { + format!("{}|{}|{}", book.path, book.handle.unwrap_or(0), book.filename) +} + +fn panel_title(text: &str, _cx: &gpui_kit::App) -> impl IntoElement { + div() + .font_family(DISPLAY) + .text_size(px(20.)) + .child(text.to_string()) +} + +fn format_bytes(bytes: Option) -> String { + let Some(bytes) = bytes else { + return "Unknown free space".into(); + }; + let gb = bytes as f64 / 1e9; + if gb >= 1.0 { + format!("{gb:.1} GB free") + } else { + format!("{:.0} MB free", bytes as f64 / 1e6) + } +} + +fn format_size(bytes: u64) -> String { + if bytes < 1024 { + format!("{bytes} B") + } else if bytes < 1024 * 1024 { + format!("{:.1} KB", bytes as f64 / 1024.0) + } else { + format!("{:.1} MB", bytes as f64 / (1024.0 * 1024.0)) + } +} + +struct Id(String); + +impl From for gpui_kit::ElementId { + fn from(value: Id) -> Self { + value.0.into() + } +} diff --git a/app/src/ui/document.rs b/app/src/ui/document.rs new file mode 100644 index 0000000..e2e8a67 --- /dev/null +++ b/app/src/ui/document.rs @@ -0,0 +1,208 @@ +use std::fs::File; +use std::io::Read; +use std::path::Path; + +use zip::ZipArchive; + +#[derive(Clone, Debug)] +pub struct Chapter { + pub title: String, + pub body: String, +} + +pub fn load_chapters(path: &Path, format: &str) -> Result, String> { + match format.to_ascii_uppercase().as_str() { + "TXT" => { + let text = std::fs::read_to_string(path).map_err(|e| e.to_string())?; + Ok(vec![Chapter { + title: "Text".into(), + body: text, + }]) + } + "EPUB" => load_epub(path), + other => Err(format!("{other} is not a text chapter format.")), + } +} + +fn load_epub(path: &Path) -> Result, String> { + let file = File::open(path).map_err(|e| e.to_string())?; + let mut archive = ZipArchive::new(file).map_err(|e| e.to_string())?; + let container = read_zip(&mut archive, "META-INF/container.xml")?; + let opf_path = attr_value(&container, "full-path") + .ok_or_else(|| "EPUB is missing a rootfile path.".to_string())?; + let opf = read_zip(&mut archive, &opf_path)?; + let base = opf_path + .rsplit_once('/') + .map(|(dir, _)| format!("{dir}/")) + .unwrap_or_default(); + + let manifest = manifest_hrefs(&opf); + let spine = spine_ids(&opf); + if spine.is_empty() { + return Err("EPUB spine is empty.".into()); + } + + let mut chapters = Vec::new(); + for (index, id) in spine.iter().enumerate() { + let Some(href) = manifest.get(id) else { + continue; + }; + let full = join_zip(&base, href); + let html = match read_zip(&mut archive, &full) { + Ok(html) => html, + Err(_) => continue, + }; + let body = html_to_text(&html); + if body.trim().is_empty() { + continue; + } + let title = html_title(&html).unwrap_or_else(|| format!("Chapter {}", index + 1)); + chapters.push(Chapter { title, body }); + } + if chapters.is_empty() { + return Err("No readable chapters in this EPUB.".into()); + } + Ok(chapters) +} + +fn read_zip(archive: &mut ZipArchive, name: &str) -> Result { + let mut buf = String::new(); + if let Ok(mut file) = archive.by_name(name) { + file.read_to_string(&mut buf).map_err(|e| e.to_string())?; + return Ok(buf); + } + let alt = name.replace('\\', "/"); + let mut file = archive + .by_name(&alt) + .map_err(|_| format!("Missing {name} in the EPUB."))?; + file.read_to_string(&mut buf).map_err(|e| e.to_string())?; + Ok(buf) +} + +fn join_zip(base: &str, href: &str) -> String { + let href = href.split('#').next().unwrap_or(href); + if href.starts_with('/') { + return href.trim_start_matches('/').to_string(); + } + format!("{base}{href}") +} + +fn attr_value(xml: &str, name: &str) -> Option { + let needle = format!("{name}=\""); + let start = xml.find(&needle)? + needle.len(); + let rest = &xml[start..]; + let end = rest.find('"')?; + Some(rest[..end].to_string()) +} + +fn manifest_hrefs(opf: &str) -> std::collections::HashMap { + let mut map = std::collections::HashMap::new(); + for item in opf.split("").next().unwrap_or(item); + let chunk = chunk.split("/>").next().unwrap_or(chunk); + if let (Some(id), Some(href)) = (attr_value(chunk, "id"), attr_value(chunk, "href")) { + map.insert(id, href); + } + } + map +} + +fn spine_ids(opf: &str) -> Vec { + let mut ids = Vec::new(); + for item in opf.split("").next().unwrap_or(item); + if let Some(id) = attr_value(chunk, "idref") { + ids.push(id); + } + } + ids +} + +fn html_title(html: &str) -> Option { + let lower = html.to_ascii_lowercase(); + let start = lower.find("')? + start + 1; + let end_rel = lower[after..].find(" String { + let mut out = String::new(); + let mut chars = html.chars().peekable(); + let mut in_tag = false; + let mut tag = String::new(); + while let Some(ch) = chars.next() { + if ch == '<' { + in_tag = true; + tag.clear(); + continue; + } + if in_tag { + if ch == '>' { + in_tag = false; + let name = tag + .split_whitespace() + .next() + .unwrap_or("") + .trim_start_matches('/') + .to_ascii_lowercase(); + if matches!( + name.as_str(), + "p" | "div" | "br" | "h1" | "h2" | "h3" | "h4" | "li" | "tr" | "blockquote" + ) { + if !out.ends_with("\n\n") { + out.push_str("\n\n"); + } + } + } else { + tag.push(ch); + } + continue; + } + if ch == '&' { + let mut entity = String::new(); + while let Some(next) = chars.peek().copied() { + if next == ';' || entity.len() > 12 { + break; + } + entity.push(next); + chars.next(); + } + if chars.peek() == Some(&';') { + chars.next(); + } + out.push_str(match entity.as_str() { + "amp" => "&", + "lt" => "<", + "gt" => ">", + "quot" => "\"", + "apos" | "#39" => "'", + "nbsp" => " ", + _ => " ", + }); + continue; + } + out.push(ch); + } + let mut collapsed = String::new(); + let mut blank = false; + for line in out.lines() { + let line = line.split_whitespace().collect::>().join(" "); + if line.is_empty() { + if !blank && !collapsed.is_empty() { + collapsed.push('\n'); + } + blank = true; + } else { + if !collapsed.is_empty() && !collapsed.ends_with('\n') { + collapsed.push('\n'); + } + collapsed.push_str(&line); + collapsed.push('\n'); + blank = false; + } + } + collapsed.trim().to_string() +} diff --git a/app/src/ui/icon.rs b/app/src/ui/icon.rs new file mode 100644 index 0000000..a995a1f --- /dev/null +++ b/app/src/ui/icon.rs @@ -0,0 +1,42 @@ +use std::sync::Arc; + +use gpui_kit::WindowOptions; + +const ICON_PNG: &[u8] = include_bytes!("../../icons/icon.png"); + +pub fn apply(options: &mut WindowOptions) { + options.icon = Some(raster()); +} + +/// Dock and Cmd-Tab icon. The Windows executable icon is `icons/icon.ico`, embedded as resource 1. +pub fn install() { + #[cfg(target_os = "macos")] + install_macos(); +} + +fn raster() -> Arc { + Arc::new( + image::load_from_memory(ICON_PNG) + .expect("Grimoire icon.png") + .into_rgba8(), + ) +} + +#[cfg(target_os = "macos")] +fn install_macos() { + use objc2::{AnyThread, MainThreadMarker}; + use objc2_app_kit::{NSApplication, NSImage}; + use objc2_foundation::NSData; + + let Some(marker) = MainThreadMarker::new() else { + return; + }; + let data = NSData::with_bytes(include_bytes!("../../icons/icon.icns")); + let Some(image) = NSImage::initWithData(NSImage::alloc(), &data) else { + return; + }; + let app = NSApplication::sharedApplication(marker); + unsafe { + app.setApplicationIconImage(Some(&image)); + } +} diff --git a/app/src/ui/library.rs b/app/src/ui/library.rs new file mode 100644 index 0000000..c18fb18 --- /dev/null +++ b/app/src/ui/library.rs @@ -0,0 +1,1244 @@ +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; + +use gpui_kit::component::button::{Button, ButtonVariants as _}; +use gpui_kit::component::checkbox::Checkbox; +use gpui_kit::component::input::Input; +use gpui_kit::component::menu::{ContextMenuExt as _, PopupMenuItem}; +use gpui_kit::component::{ + ActiveTheme as _, Disableable as _, IconName, Selectable as _, WindowExt as _, +}; +use gpui_kit::prelude::{ + FluentBuilder as _, InteractiveElement as _, StatefulInteractiveElement as _, Styled as _, + StyledImage as _, +}; +use gpui_kit::AppContext as _; +use gpui_kit::{AnyElement, Context, IntoElement, ObjectFit, ParentElement as _, Window, div, img, px}; + +use super::backend::{self, LibraryStatus}; +use super::shell::{FormatKind, ReadingStatus, Route, Shell}; +use super::theme::DISPLAY; +use crate::library::{BookBulkPatch, BookSummary}; + +impl Shell { + pub fn library_page(&mut self, window: &mut Window, cx: &mut Context) -> AnyElement { + let shell = cx.entity().clone(); + if self.loading && self.books.is_empty() && self.library_open { + return super::shell::muted_page("Opening library…", cx).into_any_element(); + } + if !self.library_open { + return self.closed_library(window, cx).into_any_element(); + } + + let filtered = self.filtered_books(); + let mut reading = Vec::new(); + let mut to_read = Vec::new(); + let mut read = Vec::new(); + for book in &filtered { + match book_status(book) { + ReadingStatus::Reading => reading.push(book.clone()), + ReadingStatus::ToRead => to_read.push(book.clone()), + ReadingStatus::Read => read.push(book.clone()), + } + } + + div() + .size_full() + .flex() + .flex_col() + .child(self.library_bar(filtered.is_empty(), window, cx)) + .child( + div() + .flex_1() + .min_h(px(0.)) + .flex() + .overflow_hidden() + .when(self.filters_open, |this| { + let books = self.books.clone(); + this.child(self.filters(&books, cx)) + }) + .child(self.library_grid( + &reading, + &to_read, + &read, + filtered.is_empty(), + shell, + window, + cx, + )), + ) + .into_any_element() + } + + fn closed_library(&mut self, _window: &mut Window, cx: &mut Context) -> gpui_kit::Div { + div().p(px(28.)).child( + div() + .max_w(px(560.)) + .p(px(22.)) + .rounded(px(10.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .flex() + .flex_col() + .gap(px(10.)) + .child( + div() + .font_family(DISPLAY) + .text_size(px(26.)) + .child("Your library starts here"), + ) + .child( + div().text_color(cx.theme().muted_foreground).child( + "Set a library folder. Grimoire stores metadata.db and your book files locally, Calibre-style.", + ), + ) + .child( + Button::new("choose-library") + .primary() + .label(if self.busy { + "Working…" + } else { + "Choose library folder" + }) + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.pick_library(window, cx))), + ) + .child( + Button::new("open-settings") + .ghost() + .label("Open Settings") + .on_click(cx.listener(|this, _, _, cx| this.go(Route::Settings, cx))), + ) + .when(!self.error.is_empty(), |this| { + this.child(div().text_color(cx.theme().danger).child(self.error.clone())) + }), + ) + } + + fn library_bar( + &mut self, + empty: bool, + _window: &mut Window, + cx: &mut Context, + ) -> impl IntoElement { + div() + .flex() + .items_center() + .gap(px(8.)) + .px(px(16.)) + .py(px(10.)) + .border_b_1() + .border_color(cx.theme().border) + .bg(cx.theme().sidebar) + .child( + div() + .flex_1() + .min_w(px(180.)) + .child(Input::new(&self.search)), + ) + .child( + Button::new("toggle-filters") + .ghost() + .compact() + .icon(IconName::PanelLeft) + .selected(self.filters_open) + .tooltip(if self.filters_open { + "Hide filters" + } else { + "Show filters" + }) + .on_click(cx.listener(|this, _, _, cx| { + this.filters_open = !this.filters_open; + cx.notify(); + })), + ) + .child( + Button::new("select-books") + .ghost() + .compact() + .icon(if self.select_mode { + IconName::Check + } else { + IconName::Menu + }) + .selected(self.select_mode) + .disabled(self.busy || empty) + .tooltip(if self.select_mode { + "Done selecting" + } else { + "Select books" + }) + .on_click(cx.listener(|this, _, _, cx| { + this.select_mode = !this.select_mode; + if !this.select_mode { + this.selected.clear(); + this.bulk_open = false; + } + cx.notify(); + })), + ) + .child( + Button::new("import-books") + .primary() + .compact() + .icon(IconName::Plus) + .tooltip("Import books") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.import_files(window, cx))), + ) + .child( + Button::new("switch-library") + .ghost() + .compact() + .icon(IconName::FolderOpen) + .tooltip("Switch library") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.pick_library(window, cx))), + ) + } + + fn filters(&mut self, books: &[BookSummary], cx: &mut Context) -> impl IntoElement { + let mut genres: HashMap = HashMap::new(); + let mut authors: HashMap = HashMap::new(); + let mut status_counts = [0usize; 3]; + let mut format_counts = [0usize; 2]; + for book in books { + match book_status(book) { + ReadingStatus::Reading => status_counts[0] += 1, + ReadingStatus::ToRead => status_counts[1] += 1, + ReadingStatus::Read => status_counts[2] += 1, + } + match format_kind(book) { + FormatKind::Novel => format_counts[0] += 1, + FormatKind::Comic => format_counts[1] += 1, + } + for tag in &book.tags { + let tag = tag.trim(); + if !tag.is_empty() { + *genres.entry(tag.to_string()).or_default() += 1; + } + } + for author in author_labels(book) { + *authors.entry(author).or_default() += 1; + } + } + let mut genre_list: Vec<_> = genres.into_iter().collect(); + genre_list.sort_by(|a, b| a.0.cmp(&b.0)); + let mut author_list: Vec<_> = authors.into_iter().collect(); + author_list.sort_by(|a, b| a.0.cmp(&b.0)); + + let active = !self.filter_status.is_empty() + || !self.filter_genres.is_empty() + || !self.filter_formats.is_empty() + || !self.filter_authors.is_empty(); + + div() + .id("library-filters") + .w(px(230.)) + .h_full() + .overflow_y_scroll() + .border_r_1() + .border_color(cx.theme().border) + .p(px(14.)) + .flex() + .flex_col() + .gap(px(12.)) + .child(filter_heading("Status")) + .children(status_row( + "reading", + "Reading", + status_counts[0], + self.filter_status.contains(&ReadingStatus::Reading), + cx, + )) + .children(status_row( + "to-read", + "To Read", + status_counts[1], + self.filter_status.contains(&ReadingStatus::ToRead), + cx, + )) + .children(status_row( + "read", + "Read", + status_counts[2], + self.filter_status.contains(&ReadingStatus::Read), + cx, + )) + .child(filter_heading("Format")) + .children(format_row( + "novel", + "Novel", + format_counts[0], + self.filter_formats.contains(&FormatKind::Novel), + cx, + )) + .children(format_row( + "comic", + "Comic Book", + format_counts[1], + self.filter_formats.contains(&FormatKind::Comic), + cx, + )) + .when(!genre_list.is_empty(), |this| { + let rows = genre_list.into_iter().map(|(name, count)| { + let checked = self.filter_genres.contains(&name); + let id = format!("genre-{name}"); + let label = format!("{name} {count}"); + let key = name.clone(); + Checkbox::new(id) + .label(label) + .checked(checked) + .on_change(cx.listener(move |this, checked, _, cx| { + if *checked { + this.filter_genres.insert(key.clone()); + } else { + this.filter_genres.remove(&key); + } + cx.notify(); + })) + }); + this.child(filter_heading("Genres")).children(rows) + }) + .when(!author_list.is_empty(), |this| { + let rows = author_list.into_iter().take(40).map(|(name, count)| { + let checked = self.filter_authors.contains(&name); + let id = format!("author-{name}"); + let label = format!("{name} {count}"); + let key = name.clone(); + Checkbox::new(id) + .label(label) + .checked(checked) + .on_change(cx.listener(move |this, checked, _, cx| { + if *checked { + this.filter_authors.insert(key.clone()); + } else { + this.filter_authors.remove(&key); + } + cx.notify(); + })) + }); + this.child(filter_heading("Authors")).children(rows) + }) + .when(active, |this| { + this.child( + Button::new("clear-filters") + .ghost() + .label("Clear filters") + .on_click(cx.listener(|this, _, _, cx| { + this.filter_status.clear(); + this.filter_genres.clear(); + this.filter_formats.clear(); + this.filter_authors.clear(); + cx.notify(); + })), + ) + }) + } + + fn library_grid( + &mut self, + reading: &[BookSummary], + to_read: &[BookSummary], + read: &[BookSummary], + none: bool, + shell: gpui_kit::Entity, + _window: &mut Window, + cx: &mut Context, + ) -> impl IntoElement { + let selected_count = self.selected.len(); + div() + .id("library-grid") + .flex_1() + .h_full() + .overflow_y_scroll() + .p(px(18.)) + .flex() + .flex_col() + .gap(px(14.)) + .when(!self.message.is_empty(), |this| { + this.child(div().text_color(cx.theme().primary).child(self.message.clone())) + }) + .when(!self.error.is_empty(), |this| { + this.child(div().text_color(cx.theme().danger).child(self.error.clone())) + }) + .when(self.select_mode && selected_count > 0, |this| { + this.child(self.selection_bar(cx)) + }) + .when(self.bulk_open, |this| this.child(self.bulk_panel(cx))) + .when(self.books.is_empty(), |this| { + this.child(empty_panel( + "No books yet", + "Import EPUB, PDF, TXT, CBZ/CBR comics, or DRM-free MOBI to read in Grimoire. AZW3 can be imported for Kindle send.", + cx, + )) + }) + .when(!self.books.is_empty() && none, |this| { + this.child(empty_panel( + "No books match these filters", + "Try clearing some filters, or adjust your search.", + cx, + )) + }) + .child(book_section("Reading", reading, &self.covers, self.select_mode, &self.selected, shell.clone(), cx)) + .child(book_section("To Read", to_read, &self.covers, self.select_mode, &self.selected, shell.clone(), cx)) + .child(book_section("Read", read, &self.covers, self.select_mode, &self.selected, shell, cx)) + } + + fn selection_bar(&mut self, cx: &mut Context) -> impl IntoElement { + let count = self.selected.len(); + let can_send = self.devices.iter().any(|device| device.supports_send); + div() + .flex() + .flex_wrap() + .items_center() + .gap(px(8.)) + .p(px(10.)) + .rounded(px(10.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .child(format!("{count} selected")) + .child( + Button::new("select-all") + .outline() + .label("Select all") + .disabled(self.busy) + .on_click(cx.listener(|this, _, _, cx| { + this.selected = this.filtered_books().into_iter().map(|book| book.id).collect(); + cx.notify(); + })), + ) + .child( + Button::new("clear-selection") + .outline() + .label("Clear") + .on_click(cx.listener(|this, _, _, cx| { + this.selected.clear(); + cx.notify(); + })), + ) + .child( + Button::new("bulk-fetch") + .outline() + .label("Fetch metadata") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.bulk_fetch(window, cx))), + ) + .child( + Button::new("bulk-edit") + .outline() + .label("Edit metadata") + .disabled(self.busy) + .on_click(cx.listener(|this, _, _, cx| { + this.bulk_open = true; + cx.notify(); + })), + ) + .child( + Button::new("bulk-send") + .outline() + .label("Send to device") + .disabled(self.busy || !can_send) + .on_click(cx.listener(|this, _, window, cx| this.bulk_send(window, cx))), + ) + .child( + Button::new("bulk-delete") + .danger() + .label("Delete") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.confirm_bulk_delete(window, cx))), + ) + } + + fn bulk_panel(&mut self, cx: &mut Context) -> impl IntoElement { + div() + .p(px(14.)) + .rounded(px(10.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .flex() + .flex_col() + .gap(px(8.)) + .child("Only checked fields are applied. Tags are added, not replaced.") + .child(Input::new(&self.bulk_tags)) + .child( + Checkbox::new("bulk-series") + .label("Apply series") + .checked(self.bulk_series) + .on_change(cx.listener(|this, checked, _, cx| { + this.bulk_series = *checked; + cx.notify(); + })), + ) + .child(Input::new(&self.bulk_series_input)) + .child( + Checkbox::new("bulk-index") + .label("Apply series index") + .checked(self.bulk_index) + .on_change(cx.listener(|this, checked, _, cx| { + this.bulk_index = *checked; + cx.notify(); + })), + ) + .child(Input::new(&self.bulk_index_input)) + .child( + Checkbox::new("bulk-notes") + .label("Replace notes") + .checked(self.bulk_notes) + .on_change(cx.listener(|this, checked, _, cx| { + this.bulk_notes = *checked; + cx.notify(); + })), + ) + .child(gpui_kit::component::input::Textarea::new(&self.bulk_notes_input)) + .child( + div().flex().gap(px(8.)).child( + Button::new("bulk-cancel") + .outline() + .label("Cancel") + .on_click(cx.listener(|this, _, _, cx| { + this.bulk_open = false; + cx.notify(); + })), + ).child( + Button::new("bulk-apply") + .primary() + .label("Apply") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.apply_bulk(window, cx))), + ), + ) + } + + pub fn filtered_books(&self) -> Vec { + self.books + .iter() + .filter(|book| book_matches(book, self)) + .cloned() + .collect() + } + + pub fn reload_books(&mut self, window: &mut Window, cx: &mut Context) { + if !self.library_open { + self.books.clear(); + self.covers.clear(); + self.loading = false; + cx.notify(); + return; + } + let state = self.state.clone(); + let query = self.search.read(cx).value().to_string(); + self.loading = true; + self.spawn_io( + window, + cx, + move || { + let query = query.trim(); + let books = backend::list(&state, if query.is_empty() { None } else { Some(query) }) + .map_err(|e| e.to_string())?; + let covers = backend::covers_for(&state, &books); + Ok((books, covers)) + }, + |this, result, _, cx| { + this.loading = false; + match result { + Ok((books, covers)) => { + this.books = books; + this.covers = covers; + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + pub fn pick_library(&mut self, window: &mut Window, cx: &mut Context) { + let Some(path) = rfd::FileDialog::new() + .set_title("Choose or create a Grimoire library folder") + .pick_folder() + else { + return; + }; + match backend::create_or_open(&self.state, &path) { + Ok(status) => { + self.apply_status(status); + self.error.clear(); + self.message = "Library path saved. Grimoire will open it on the next launch too.".into(); + self.reload_books(window, cx); + } + Err(error) => self.error = error.to_string(), + } + cx.notify(); + } + + pub fn import_files(&mut self, window: &mut Window, cx: &mut Context) { + let Some(paths) = rfd::FileDialog::new() + .set_title("Import eBooks") + .add_filter("eBooks", backend::SUPPORTED_EXT) + .pick_files() + else { + return; + }; + self.import_paths(paths, window, cx); + } + + pub fn import_paths(&mut self, paths: Vec, window: &mut Window, cx: &mut Context) { + let ebook_paths: Vec<_> = paths + .into_iter() + .filter(|path| backend::SUPPORTED_EXT.contains(&backend::extension_of(path).as_str())) + .collect(); + if ebook_paths.is_empty() { + self.error = "No supported eBooks in that drop. Use EPUB, PDF, AZW3, MOBI, TXT, CBZ, or CBR.".into(); + cx.notify(); + return; + } + if !self.library_open { + self.error = "Open a library folder before importing books.".into(); + cx.notify(); + return; + } + self.busy = true; + self.error.clear(); + self.message.clear(); + let state = self.state.clone(); + self.spawn_task( + window, + cx, + async move { + let mut imported = 0; + let mut failures = Vec::new(); + for path in ebook_paths { + match backend::import(&state, &path) { + Ok(book) => { + imported += 1; + if needs_enrichment(&book) { + let _ = backend::enrich(&state, book.id).await; + } + } + Err(error) => { + let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("book"); + failures.push(format!("{name}: {error}")); + } + } + } + Ok((imported, failures)) + }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok((imported, failures)) => { + if imported > 0 { + this.message = if imported == 1 { + "Imported 1 book.".into() + } else { + format!("Imported {imported} books.") + }; + } + if !failures.is_empty() { + this.error = failures.into_iter().take(3).collect::>().join(" · "); + } + } + Err(error) => this.error = error, + } + this.reload_books(window, cx); + }, + ); + } + + fn apply_bulk(&mut self, window: &mut Window, cx: &mut Context) { + let ids: Vec = self.selected.iter().copied().collect(); + let tags = self + .bulk_tags + .read(cx) + .value() + .split(',') + .map(|tag| tag.trim().to_string()) + .filter(|tag| !tag.is_empty()) + .collect::>(); + let series = self.bulk_series_input.read(cx).value().to_string(); + let index = self.bulk_index_input.read(cx).value().to_string(); + let notes = self.bulk_notes_input.read(cx).value().to_string(); + if tags.is_empty() && !self.bulk_series && !self.bulk_index && !self.bulk_notes { + self.error = "Choose at least one field to update.".into(); + cx.notify(); + return; + } + let patch = BookBulkPatch { + add_tags: tags, + set_series: self.bulk_series, + series: if self.bulk_series { + let series = series.trim().to_string(); + if series.is_empty() { None } else { Some(series) } + } else { + None + }, + set_series_index: self.bulk_index, + series_index: if self.bulk_index { + Some(index.trim().parse().unwrap_or(1.0)) + } else { + None + }, + set_comment: self.bulk_notes, + comment: notes, + }; + self.busy = true; + let state = self.state.clone(); + self.spawn_io( + window, + cx, + move || backend::patch_books(&state, &ids, patch).map_err(|e| e.to_string()), + |this, result, window, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = format!("Updated {} books.", result.updated); + this.bulk_open = false; + if !result.failed.is_empty() { + this.error = format!("{} failed.", result.failed.len()); + } + } + Err(error) => this.error = error, + } + this.reload_books(window, cx); + }, + ); + } + + fn bulk_fetch(&mut self, window: &mut Window, cx: &mut Context) { + let ids: Vec = self.selected.iter().copied().collect(); + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { + let mut updated = 0; + for id in ids { + if let Ok(result) = backend::enrich(&state, id).await { + if !result.updated_fields.is_empty() { + updated += 1; + } + } + } + Ok(updated) + }, + |this, result, window, cx| { + this.busy = false; + if let Ok(updated) = result { + this.message = format!("Fetched metadata for {updated} books."); + } + this.reload_books(window, cx); + }, + ); + } + + fn bulk_send(&mut self, window: &mut Window, cx: &mut Context) { + let Some(device) = self.devices.iter().find(|device| device.supports_send).cloned() else { + self.error = "No Kindle connected. Connect over MTP / USB file transfer, then try again.".into(); + cx.notify(); + return; + }; + let ids: Vec = self.selected.iter().copied().collect(); + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { + let mut sent = 0; + for id in ids { + if backend::send(&state, id, &device.id).await.is_ok() { + sent += 1; + } + } + Ok((sent, device.product)) + }, + |this, result, _, cx| { + this.busy = false; + if let Ok((sent, product)) = result { + this.message = format!("Sent {sent} books to {product}."); + } + cx.notify(); + }, + ); + } + + fn confirm_bulk_delete(&mut self, window: &mut Window, cx: &mut Context) { + let count = self.selected.len(); + let shell = cx.entity().clone(); + window.open_alert_dialog(cx, move |alert, _, _| { + alert + .confirm() + .title(format!("Delete {count} books")) + .description("Delete the selected books from the library?") + .on_ok({ + let shell = shell.clone(); + move |_, window, cx| { + shell + .downgrade().update_in(cx, |this, window, cx| this.bulk_delete(window, cx)) + .ok(); + let _ = window; + true + } + }) + }); + } + + fn bulk_delete(&mut self, window: &mut Window, cx: &mut Context) { + let ids: Vec = self.selected.iter().copied().collect(); + let state = self.state.clone(); + self.busy = true; + self.spawn_io( + window, + cx, + move || backend::remove_books(&state, &ids).map_err(|e| e.to_string()), + |this, result, window, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = format!("Deleted {} books.", result.updated); + this.selected.clear(); + } + Err(error) => this.error = error, + } + this.reload_books(window, cx); + }, + ); + } + + pub fn show_book(&mut self, id: i64, window: &mut Window, cx: &mut Context) { + self.book_id = Some(id); + self.load_book(id, window, cx); + } + + pub fn open_reader_for(&mut self, book: &BookSummary, window: &mut Window, cx: &mut Context) { + if backend::can_read(&book.formats) { + self.open_reader(book.id, cx); + return; + } + let title = book.title.clone(); + let formats = if book.formats.is_empty() { + "none".into() + } else { + book.formats.join(", ") + }; + window.open_alert_dialog(cx, move |alert, _, _| { + alert.title("Cannot open book").description(format!( + "\"{title}\" can't be opened in the reader. Available formats: {formats}. Grimoire can open EPUB, PDF, TXT, MOBI, CBZ, and CBR." + )) + }); + } + + pub fn open_reader(&mut self, id: i64, cx: &mut Context) { + let state = self.state.clone(); + let _ = gpui_kit::open_window(super::reader_window_options("Grimoire", cx), cx, |window, cx| { + cx.new(|cx| super::reader::ReaderWindow::new(state, id, window, cx)) + }); + } +} + +fn book_matches(book: &BookSummary, shell: &Shell) -> bool { + if !shell.filter_status.is_empty() && !shell.filter_status.contains(&book_status(book)) { + return false; + } + if !shell.filter_formats.is_empty() && !shell.filter_formats.contains(&format_kind(book)) { + return false; + } + if !shell.filter_genres.is_empty() + && !book.tags.iter().any(|tag| shell.filter_genres.contains(tag.trim())) + { + return false; + } + if !shell.filter_authors.is_empty() + && !author_labels(book) + .iter() + .any(|author| shell.filter_authors.contains(author)) + { + return false; + } + true +} + +fn book_status(book: &BookSummary) -> ReadingStatus { + match book.progress_percent { + None => ReadingStatus::ToRead, + Some(percent) if percent >= 100.0 => ReadingStatus::Read, + Some(_) => ReadingStatus::Reading, + } +} + +fn format_kind(book: &BookSummary) -> FormatKind { + if book + .formats + .iter() + .any(|format| format.eq_ignore_ascii_case("cbz") || format.eq_ignore_ascii_case("cbr")) + { + FormatKind::Comic + } else { + FormatKind::Novel + } +} + +fn author_labels(book: &BookSummary) -> Vec { + let labels = book + .authors + .iter() + .map(|author| author.trim().to_string()) + .filter(|author| !author.is_empty()) + .collect::>(); + if labels.is_empty() { + vec!["Unknown".into()] + } else { + labels + } +} + +fn needs_enrichment(book: &crate::library::BookDetail) -> bool { + let authors_missing = book.authors.is_empty() + || book + .authors + .iter() + .all(|author| author.trim().eq_ignore_ascii_case("unknown")); + !book.has_cover || authors_missing +} + +fn status_row( + id: &str, + label: &str, + count: usize, + checked: bool, + cx: &mut Context, +) -> Option { + let status = match id { + "reading" => ReadingStatus::Reading, + "read" => ReadingStatus::Read, + _ => ReadingStatus::ToRead, + }; + Some( + Checkbox::new(id.to_string()) + .label(format!("{label} {count}")) + .checked(checked) + .on_change(cx.listener(move |this, checked, _, cx| { + if *checked { + this.filter_status.insert(status); + } else { + this.filter_status.remove(&status); + } + cx.notify(); + })), + ) +} + +fn format_row( + id: &str, + label: &str, + count: usize, + checked: bool, + cx: &mut Context, +) -> Option { + let kind = if id == "comic" { + FormatKind::Comic + } else { + FormatKind::Novel + }; + Some( + Checkbox::new(id.to_string()) + .label(format!("{label} {count}")) + .checked(checked) + .on_change(cx.listener(move |this, checked, _, cx| { + if *checked { + this.filter_formats.insert(kind); + } else { + this.filter_formats.remove(&kind); + } + cx.notify(); + })), + ) +} + +fn filter_heading(text: &str) -> impl IntoElement { + div() + .text_size(px(12.)) + .font_weight(gpui_kit::FontWeight::SEMIBOLD) + .child(text.to_string()) +} + +fn empty_panel(title: &str, body: &str, cx: &gpui_kit::App) -> impl IntoElement { + div() + .p(px(22.)) + .rounded(px(10.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .child(div().font_family(DISPLAY).text_size(px(22.)).child(title.to_string())) + .child(div().mt(px(6.)).text_color(cx.theme().muted_foreground).child(body.to_string())) +} + +fn book_section( + title: &str, + books: &[BookSummary], + covers: &HashMap, + select_mode: bool, + selected: &HashSet, + shell: gpui_kit::Entity, + cx: &mut Context, +) -> impl IntoElement { + if books.is_empty() { + return div(); + } + div() + .flex() + .flex_col() + .gap(px(10.)) + .child(super::shell::section_title(title, Some(books.len()))) + .child( + div().flex().flex_wrap().gap(px(16.)).children(books.iter().map(|book| { + book_card(book, covers.get(&book.id), select_mode, selected.contains(&book.id), shell.clone(), cx) + })), + ) +} + +fn book_card( + book: &BookSummary, + cover: Option<&PathBuf>, + select_mode: bool, + is_selected: bool, + shell: gpui_kit::Entity, + cx: &mut Context, +) -> impl IntoElement { + let id = book.id; + let title = book.title.clone(); + let authors = author_labels(book).join(", "); + let can_read = backend::can_read(&book.formats); + let progress = book.progress_percent.filter(|percent| *percent < 100.0); + let menu_shell = shell.clone(); + let open_shell = shell.clone(); + let card_title = title.clone(); + let menu_title = title.clone(); + + div() + .id(format!("book-card-{id}")) + .w(px(148.)) + .cursor_pointer() + .when(is_selected, |this| { + this.border_1().border_color(cx.theme().primary).rounded(px(8.)).p(px(4.)) + }) + .on_click(move |_, window, cx| { + open_shell + .downgrade().update_in(cx, |this, window, cx| { + if this.select_mode { + if this.selected.contains(&id) { + this.selected.remove(&id); + } else { + this.selected.insert(id); + } + cx.notify(); + } else if let Some(book) = this.books.iter().find(|book| book.id == id).cloned() { + this.open_reader_for(&book, window, cx); + } + }) + .ok(); + let _ = window; + }) + .context_menu(move |menu, _, _| { + let info = menu_shell.clone(); + let read = menu_shell.clone(); + let fetch = menu_shell.clone(); + let send = menu_shell.clone(); + let delete = menu_shell.clone(); + let title_for_delete = card_title.clone(); + let send_title = menu_title.clone(); + menu.item( + PopupMenuItem::new("Info").on_click(move |_, _, cx| { + info.downgrade().update_in(cx, |this, window, cx| this.show_book(id, window, cx)).ok(); + }), + ) + .when(can_read, |menu| { + menu.item(PopupMenuItem::new("Read").on_click(move |_, _, cx| { + read.update(cx, |this, cx| this.open_reader(id, cx)); + })) + }) + .item(PopupMenuItem::new("Edit metadata").on_click({ + let info = menu_shell.clone(); + move |_, _, cx| { + info.downgrade().update_in(cx, |this, window, cx| this.show_book(id, window, cx)).ok(); + } + })) + .item(PopupMenuItem::new("Fetch metadata").on_click(move |_, _, cx| { + fetch + .downgrade().update_in(cx, |this, window, cx| this.fetch_one(id, window, cx)) + .ok(); + })) + .item(PopupMenuItem::new("Send to device").on_click({ + let send_title = send_title.clone(); + move |_, _, cx| { + send.downgrade().update_in(cx, |this, window, cx| this.send_one(id, &send_title, window, cx)) + .ok(); + } + })) + .separator() + .item( + PopupMenuItem::new("Delete").on_click(move |_, window, cx| { + let delete = delete.clone(); + let title = title_for_delete.clone(); + window.open_alert_dialog(cx, move |alert, _, _| { + alert + .confirm() + .title("Delete book") + .description(format!("Delete \"{title}\" from the library?")) + .on_ok({ + let delete = delete.clone(); + move |_, _, cx| { + delete + .downgrade().update_in(cx, |this, window, cx| this.delete_one(id, window, cx)) + .ok(); + true + } + }) + }); + }), + ) + }) + .child(cover_box(cover, &title, progress, cx)) + .child( + div() + .mt(px(8.)) + .font_family(DISPLAY) + .text_size(px(15.)) + .line_height(px(19.)) + .child(title.clone()), + ) + .child( + div() + .text_size(px(13.)) + .text_color(cx.theme().muted_foreground) + .child(authors), + ) + .when(select_mode, |this| { + this.child(if is_selected { "Selected" } else { "" }) + }) +} + +fn cover_box( + cover: Option<&PathBuf>, + title: &str, + progress: Option, + cx: &gpui_kit::App, +) -> impl IntoElement { + let letter = title.chars().next().unwrap_or('?').to_string(); + div() + .relative() + .w(px(140.)) + .h(px(210.)) + .rounded(px(6.)) + .overflow_hidden() + .bg(cx.theme().muted) + .child(match cover { + Some(path) => img(path.clone()).size_full().object_fit(ObjectFit::Cover).into_any_element(), + None => div() + .size_full() + .flex() + .items_center() + .justify_center() + .font_family(DISPLAY) + .text_size(px(36.)) + .text_color(cx.theme().muted_foreground) + .child(letter) + .into_any_element(), + }) + .when_some(progress, |this, percent| { + this.child( + div() + .absolute() + .bottom(px(6.)) + .right(px(6.)) + .px(px(6.)) + .py(px(2.)) + .rounded(px(99.)) + .text_size(px(11.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .child(format!("{}%", percent.round() as i64)), + ) + }) +} + +impl Shell { + fn fetch_one(&mut self, id: i64, window: &mut Window, cx: &mut Context) { + let state = self.state.clone(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::enrich(&state, id).await.map_err(|e| e.to_string()) }, + |this, result, window, cx| { + this.busy = false; + match result { + Ok(result) => { + this.message = if result.updated_fields.is_empty() { + "No missing fields to fill.".into() + } else { + format!("Updated: {}", result.updated_fields.join(", ")) + }; + } + Err(error) => this.error = error, + } + this.reload_books(window, cx); + }, + ); + } + + fn send_one(&mut self, id: i64, title: &str, window: &mut Window, cx: &mut Context) { + let Some(device) = self.devices.iter().find(|device| device.supports_send).cloned() else { + self.error = "No Kindle connected. Connect over MTP / USB file transfer, then try again.".into(); + cx.notify(); + return; + }; + let state = self.state.clone(); + let title = title.to_string(); + self.busy = true; + self.spawn_task( + window, + cx, + async move { backend::send(&state, id, &device.id).await.map(|result| (result, device.product)).map_err(|e| e.to_string()) }, + move |this, result, _, cx| { + this.busy = false; + match result { + Ok((result, product)) => { + this.message = format!( + "Sent \"{title}\" ({}) to {product} as {}.", + result.format, result.remote_path + ); + } + Err(error) => this.error = error, + } + cx.notify(); + }, + ); + } + + fn delete_one(&mut self, id: i64, window: &mut Window, cx: &mut Context) { + let state = self.state.clone(); + self.spawn_io( + window, + cx, + move || backend::remove_book(&state, id).map_err(|e| e.to_string()), + |this, result, window, cx| { + if let Err(error) = result { + this.error = error; + } + this.reload_books(window, cx); + }, + ); + } +} + +#[allow(dead_code)] +fn _status_used(status: LibraryStatus) -> bool { + status.open +} diff --git a/app/src/ui/mod.rs b/app/src/ui/mod.rs new file mode 100644 index 0000000..1b08d20 --- /dev/null +++ b/app/src/ui/mod.rs @@ -0,0 +1,71 @@ +mod backend; +mod book; +mod devices; +mod document; +mod icon; +mod library; +mod reader; +mod settings; +mod shell; +mod theme; + +use gpui_kit::AppContext as _; +use gpui_kit::{ + App, TitlebarOptions, WindowBounds, WindowOptions, application, point, px, size, +}; +use std::future::pending; + +pub use shell::Shell; + +pub fn launch() { + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("tokio runtime"); + let handle = runtime.handle().clone(); + std::thread::Builder::new() + .name("grimoire-tokio".into()) + .spawn(move || runtime.block_on(pending::<()>())) + .expect("tokio thread"); + + application() + .with_assets(gpui_kit::assets::Assets) + .run(move |cx| { + gpui_kit::init(cx); + cx.set_global(backend::TokioRt(handle)); + theme::apply_saved(cx); + icon::install(); + gpui_kit::open_window(main_window_options(cx), cx, |window, cx| { + cx.new(|cx| Shell::new(window, cx)) + }) + .expect("failed to open Grimoire"); + }); +} + +fn main_window_options(cx: &App) -> WindowOptions { + let mut options = WindowOptions::default(); + options.window_bounds = Some(WindowBounds::centered(size(px(1180.), px(780.)), cx)); + options.window_min_size = Some(size(px(880.), px(560.))); + options.titlebar = Some(TitlebarOptions { + title: Some("Grimoire".into()), + appears_transparent: true, + traffic_light_position: Some(point(px(14.), px(18.))), + }); + options.app_owns_titlebar_drag = true; + icon::apply(&mut options); + options +} + +pub(crate) fn reader_window_options(title: &str, cx: &App) -> WindowOptions { + let mut options = WindowOptions::default(); + options.window_bounds = Some(WindowBounds::centered(size(px(920.), px(760.)), cx)); + options.window_min_size = Some(size(px(640.), px(480.))); + options.titlebar = Some(TitlebarOptions { + title: Some(title.to_string().into()), + appears_transparent: true, + traffic_light_position: Some(point(px(14.), px(16.))), + }); + options.app_owns_titlebar_drag = true; + icon::apply(&mut options); + options +} diff --git a/app/src/ui/reader.rs b/app/src/ui/reader.rs new file mode 100644 index 0000000..bffc26e --- /dev/null +++ b/app/src/ui/reader.rs @@ -0,0 +1,522 @@ +use std::path::PathBuf; +use std::sync::Arc; + +use gpui_kit::component::button::{Button, ButtonVariants as _}; +use gpui_kit::base::InteractiveElementExt as _; +use gpui_kit::component::{ActiveTheme as _, Disableable as _}; +use gpui_kit::prelude::{ + InteractiveElement as _, StatefulInteractiveElement as _, Styled as _, StyledImage as _, +}; +use gpui_kit::{ + Context, Image, ImageFormat, IntoElement, MouseButton, ObjectFit, ParentElement as _, Render, + ScrollHandle, Window, div, img, px, +}; + +use super::backend; +use super::document::{self, Chapter}; +use super::settings::reader_family; +use super::theme::DISPLAY; +use crate::reader::ReadingProgressUpdate; +use crate::state::AppState; + +pub struct ReaderWindow { + state: AppState, + book_id: i64, + title: String, + authors: String, + format: String, + path: PathBuf, + error: String, + loading: bool, + chapters: Vec, + chapter: usize, + comic_count: usize, + comic_index: usize, + comic_image: Option>, + scroll: ScrollHandle, + percent: f64, + titlebar_drag: bool, +} + +impl ReaderWindow { + pub fn new(state: AppState, book_id: i64, window: &mut Window, cx: &mut Context) -> Self { + let reader = Self { + state, + book_id, + title: "Reading".into(), + authors: String::new(), + format: String::new(), + path: PathBuf::new(), + error: String::new(), + loading: true, + chapters: Vec::new(), + chapter: 0, + comic_count: 0, + comic_index: 0, + comic_image: None, + scroll: ScrollHandle::new(), + percent: 0.0, + titlebar_drag: false, + }; + reader.load(window, cx); + reader + } + + fn runtime(&self, cx: &gpui_kit::App) -> tokio::runtime::Handle { + cx.global::().0.clone() + } + + fn load(&self, window: &mut Window, cx: &mut Context) { + let state = self.state.clone(); + let book_id = self.book_id; + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + let loaded = runtime + .spawn_blocking(move || { + let source = backend::reader_source(&state, book_id).map_err(|e| e.to_string())?; + let saved = backend::progress(&state, book_id).ok().flatten(); + Ok::<_, String>((source, saved)) + }) + .await + .unwrap_or_else(|error| Err(error.to_string())); + this.update_in(cx, |this, window, cx| { + this.loading = false; + match loaded { + Ok((source, saved)) => { + this.title = source.title.clone(); + this.authors = source.authors.join(", "); + this.format = source.format.clone(); + this.path = PathBuf::from(&source.path); + if let Some(saved) = saved { + this.percent = saved.percent; + if let Some(index) = saved.location.strip_prefix("chapter:") { + this.chapter = index.parse().unwrap_or(0); + } + if let Some(index) = saved.location.strip_prefix("page:") { + this.comic_index = index.parse().unwrap_or(0); + } + } + this.open_format(window, cx); + } + Err(error) => this.error = error, + } + cx.notify(); + }) + .ok(); + }) + .detach(); + } + + fn open_format(&mut self, window: &mut Window, cx: &mut Context) { + let format = self.format.to_ascii_uppercase(); + if format == "PDF" { + return; + } + if format == "CBZ" || format == "CBR" { + self.load_comic_page(window, cx); + return; + } + let path = self.path.clone(); + let format = format.clone(); + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + let chapters = runtime + .spawn_blocking(move || document::load_chapters(&path, &format)) + .await + .unwrap_or_else(|error| Err(error.to_string())); + this.update(cx, |this, cx| { + match chapters { + Ok(chapters) => { + if this.chapter >= chapters.len() { + this.chapter = 0; + } + this.chapters = chapters; + } + Err(error) => this.error = error, + } + cx.notify(); + }) + .ok(); + }) + .detach(); + } + + fn load_comic_page(&mut self, window: &mut Window, cx: &mut Context) { + let path = self.path.clone(); + let index = self.comic_index; + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + let page = runtime + .spawn_blocking(move || backend::comic_page(&path, index).map_err(|e| e.to_string())) + .await + .unwrap_or_else(|error| Err(error.to_string())); + this.update(cx, |this, cx| { + match page { + Ok((name, data_url, count)) => { + this.comic_count = count; + this.comic_image = image_from_data_url(&data_url); + let _ = name; + this.remember("page", this.comic_index, cx); + } + Err(error) => this.error = error, + } + cx.notify(); + }) + .ok(); + }) + .detach(); + } + + fn remember(&mut self, kind: &str, index: usize, cx: &mut Context) { + let total = if kind == "page" { + self.comic_count.max(1) + } else { + self.chapters.len().max(1) + }; + let percent = ((index as f64 + 1.0) / total as f64) * 100.0; + self.percent = percent; + let state = self.state.clone(); + let book_id = self.book_id; + let format = self.format.clone(); + let location = format!("{kind}:{index}"); + let runtime = self.runtime(cx); + cx.spawn(async move |_this, _cx| { + let _ = runtime + .spawn_blocking(move || { + backend::save_progress( + &state, + book_id, + ReadingProgressUpdate { + format, + location, + percent, + }, + ) + }) + .await; + }) + .detach(); + } + + fn note_scroll(&mut self, cx: &mut Context) { + let max = self.scroll.max_offset().y.as_f32(); + if max <= 1.0 || self.chapters.is_empty() { + return; + } + let offset = self.scroll.offset().y.as_f32().max(0.0); + let within = (offset / max).clamp(0.0, 1.0) as f64; + let total = self.chapters.len() as f64; + let percent = ((self.chapter as f64 + within) / total) * 100.0; + if (percent - self.percent).abs() < 1.0 { + return; + } + self.percent = percent; + let state = self.state.clone(); + let book_id = self.book_id; + let format = self.format.clone(); + let location = format!("chapter:{}", self.chapter); + let runtime = self.runtime(cx); + cx.spawn(async move |_this, _cx| { + let _ = runtime + .spawn_blocking(move || { + backend::save_progress( + &state, + book_id, + ReadingProgressUpdate { + format, + location, + percent, + }, + ) + }) + .await; + }) + .detach(); + } +} + +impl Render for ReaderWindow { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + let font = backend::config() + .map(|config| config.reader_font) + .unwrap_or_else(|_| "serif".into()); + let family = reader_family(&font); + let paper = cx.theme().popover; + let ink = cx.theme().foreground; + + div() + .size_full() + .flex() + .flex_col() + .bg(cx.theme().background) + .text_color(ink) + .child(self.chrome(window, cx)) + .child(self.content(family, paper, window, cx)) + } +} + +impl ReaderWindow { + fn chrome(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + div() + .h(px(48.)) + .px(px(16.)) + .flex() + .items_center() + .gap(px(10.)) + .id("reader-titlebar") + .border_b_1() + .border_color(cx.theme().border) + .on_double_click(cx.listener(|this, _, window, _| { + this.titlebar_drag = false; + window.zoom_window(); + })) + .on_mouse_down( + MouseButton::Left, + cx.listener(|this, _, _, _| this.titlebar_drag = true), + ) + .on_mouse_up( + MouseButton::Left, + cx.listener(|this, _, _, _| this.titlebar_drag = false), + ) + .on_mouse_down_out(cx.listener(|this, _, _, _| this.titlebar_drag = false)) + .on_mouse_move(cx.listener(|this, _, window, _| { + if this.titlebar_drag { + this.titlebar_drag = false; + window.start_window_move(); + } + })) + .child( + div() + .pl(px(72.)) + .font_family(DISPLAY) + .text_size(px(16.)) + .child(self.title.clone()), + ) + .child( + div() + .text_size(px(12.)) + .text_color(cx.theme().muted_foreground) + .child(format!("{} · {:.0}%", self.format, self.percent)), + ) + } + + fn content( + &mut self, + family: &str, + paper: gpui_kit::Hsla, + window: &mut Window, + cx: &mut Context, + ) -> impl IntoElement { + if self.loading { + return div().p(px(24.)).child("Opening book…").into_any_element(); + } + if !self.error.is_empty() { + return div() + .p(px(24.)) + .text_color(cx.theme().danger) + .child(self.error.clone()) + .into_any_element(); + } + let format = self.format.to_ascii_uppercase(); + if format == "PDF" { + let path = self.path.clone(); + return div() + .p(px(28.)) + .flex() + .flex_col() + .gap(px(12.)) + .child(div().font_family(DISPLAY).text_size(px(24.)).child(self.title.clone())) + .child("PDF opens in your system viewer. EPUB, TXT, and comics stay inside Grimoire.") + .child( + Button::new("open-pdf") + .primary() + .label("Open PDF") + .on_click(move |_, _, _| open_path(&path)), + ) + .into_any_element(); + } + if format == "CBZ" || format == "CBR" { + return self.comic_view(cx).into_any_element(); + } + self.text_view(family, paper, window, cx).into_any_element() + } + + fn text_view( + &mut self, + family: &str, + paper: gpui_kit::Hsla, + _window: &mut Window, + cx: &mut Context, + ) -> impl IntoElement { + let chapter = self.chapters.get(self.chapter).cloned(); + let count = self.chapters.len(); + let index = self.chapter; + div() + .flex_1() + .min_h(px(0.)) + .flex() + .flex_col() + .child( + div() + .flex() + .items_center() + .justify_between() + .px(px(20.)) + .py(px(8.)) + .child( + Button::new("prev-chapter") + .ghost() + .label("Previous") + .disabled(index == 0) + .on_click(cx.listener(|this, _, _, cx| { + if this.chapter > 0 { + this.chapter -= 1; + this.scroll.set_offset(gpui_kit::point(px(0.), px(0.))); + this.remember("chapter", this.chapter, cx); + cx.notify(); + } + })), + ) + .child( + div().text_color(cx.theme().muted_foreground).child(format!( + "{} / {}", + index + 1, + count.max(1) + )), + ) + .child( + Button::new("next-chapter") + .ghost() + .label("Next") + .disabled(count == 0 || index + 1 >= count) + .on_click(cx.listener(|this, _, _, cx| { + if this.chapter + 1 < this.chapters.len() { + this.chapter += 1; + this.scroll.set_offset(gpui_kit::point(px(0.), px(0.))); + this.remember("chapter", this.chapter, cx); + cx.notify(); + } + })), + ), + ) + .child( + div() + .id("reader-scroll") + .flex_1() + .overflow_y_scroll() + .track_scroll(&self.scroll) + .on_scroll_wheel(cx.listener(|this, _, _, cx| this.note_scroll(cx))) + .px(px(28.)) + .py(px(18.)) + .child( + div() + .max_w(px(680.)) + .mx_auto() + .p(px(22.)) + .rounded(px(10.)) + .bg(paper) + .font_family(family) + .text_size(px(18.)) + .line_height(px(30.)) + .child(match chapter { + Some(Chapter { title, body }) => div() + .flex() + .flex_col() + .gap(px(14.)) + .child( + div() + .font_family(DISPLAY) + .text_size(px(26.)) + .child(title), + ) + .child(body) + .into_any_element(), + None => div().child("No chapter text.").into_any_element(), + }), + ), + ) + } + + fn comic_view(&mut self, cx: &mut Context) -> impl IntoElement { + let image = self.comic_image.clone(); + let index = self.comic_index; + let count = self.comic_count; + div() + .flex_1() + .min_h(px(0.)) + .flex() + .flex_col() + .child( + div() + .flex() + .items_center() + .justify_center() + .gap(px(12.)) + .py(px(8.)) + .child( + Button::new("prev-page") + .ghost() + .label("Previous") + .disabled(index == 0) + .on_click(cx.listener(|this, _, window, cx| { + if this.comic_index > 0 { + this.comic_index -= 1; + this.load_comic_page(window, cx); + } + })), + ) + .child(format!("{} / {}", index + 1, count.max(1))) + .child( + Button::new("next-page") + .ghost() + .label("Next") + .disabled(count == 0 || index + 1 >= count) + .on_click(cx.listener(|this, _, window, cx| { + if this.comic_index + 1 < this.comic_count { + this.comic_index += 1; + this.load_comic_page(window, cx); + } + })), + ), + ) + .child( + div() + .id("comic-scroll") + .flex_1() + .overflow_y_scroll() + .flex() + .justify_center() + .p(px(12.)) + .child(match image { + Some(image) => img(image).max_w(px(900.)).object_fit(ObjectFit::Contain).into_any_element(), + None => div().child("Loading page…").into_any_element(), + }), + ) + } +} + +fn image_from_data_url(data_url: &str) -> Option> { + let encoded = data_url.split_once("base64,")?.1; + let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, encoded).ok()?; + let format = image::guess_format(&bytes).ok()?; + let gpui_format = match format { + image::ImageFormat::Png => ImageFormat::Png, + image::ImageFormat::Jpeg => ImageFormat::Jpeg, + image::ImageFormat::WebP => ImageFormat::Webp, + image::ImageFormat::Gif => ImageFormat::Gif, + image::ImageFormat::Bmp => ImageFormat::Bmp, + _ => return None, + }; + Some(Arc::new(Image::from_bytes(gpui_format, bytes))) +} + +fn open_path(path: &std::path::Path) { + #[cfg(target_os = "macos")] + let _ = std::process::Command::new("open").arg(path).spawn(); + #[cfg(target_os = "windows")] + let _ = std::process::Command::new("cmd") + .args(["/C", "start", "", &path.display().to_string()]) + .spawn(); + #[cfg(all(unix, not(target_os = "macos")))] + let _ = std::process::Command::new("xdg-open").arg(path).spawn(); +} diff --git a/app/src/ui/settings.rs b/app/src/ui/settings.rs new file mode 100644 index 0000000..040e9d3 --- /dev/null +++ b/app/src/ui/settings.rs @@ -0,0 +1,221 @@ +use gpui_kit::component::button::{Button, ButtonVariants as _}; +use gpui_kit::component::{ActiveTheme as _, Disableable as _, WindowExt as _}; +use gpui_kit::prelude::{ + FluentBuilder as _, InteractiveElement as _, StatefulInteractiveElement as _, Styled as _, +}; +use gpui_kit::{Context, FontWeight, IntoElement, ParentElement as _, Window, div, px}; + +use super::backend; +use super::shell::Shell; +use super::theme::DISPLAY; + +const FONTS: &[(&str, &str, &str)] = &[ + ( + "publisher", + "Publisher default", + "Keep the ebook's own fonts when the file supplies them.", + ), + ( + "serif", + "Serif", + "A reading serif, Iowan Old Style or Georgia.", + ), + ( + "sans", + "Sans", + "A clean sans for long stretches of text.", + ), + ( + "opendyslexic", + "OpenDyslexic", + "Used when OpenDyslexic is installed on this computer.", + ), +]; + +impl Shell { + pub fn settings_page(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let path = self + .library_path + .clone() + .unwrap_or_else(|| "No library path saved yet.".into()); + let open = if self.library_open { + "Open and ready" + } else if self.library_path.is_some() { + "Saved, but not open right now" + } else { + "" + }; + let font = self.reader_font.clone(); + + div() + .id("settings-page") + .size_full() + .overflow_y_scroll() + .p(px(24.)) + .flex() + .flex_col() + .gap(px(16.)) + .child(div().font_family(DISPLAY).text_size(px(28.)).child("Settings")) + .child( + div() + .text_color(cx.theme().muted_foreground) + .child("Choose a library folder to open automatically when Grimoire starts."), + ) + .when(!self.message.is_empty(), |this| { + this.child(div().text_color(cx.theme().primary).child(self.message.clone())) + }) + .when(!self.error.is_empty(), |this| { + this.child(div().text_color(cx.theme().danger).child(self.error.clone())) + }) + .child(settings_block( + "Library location", + "Grimoire stores metadata.db and book files in this folder. The path is saved in your OS config directory.", + cx, + ).child(div().mt(px(8.)).child(path)).when(!open.is_empty(), |this| { + this.child(div().text_color(cx.theme().muted_foreground).child(open)) + }).child( + div().mt(px(10.)).flex().gap(px(8.)).child( + Button::new("settings-choose") + .primary() + .label(if self.library_path.is_some() { + "Change library folder" + } else { + "Choose library folder" + }) + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.pick_library(window, cx))), + ).when(self.library_path.is_some(), |this| { + this.child( + Button::new("settings-clear") + .outline() + .label("Clear saved path") + .disabled(self.busy) + .on_click(cx.listener(|this, _, window, cx| this.confirm_clear(window, cx))), + ) + }), + )) + .child({ + let mut block = settings_block( + "Reader font", + "Applies to EPUB and TXT reading.", + cx, + ); + for (id, label, description) in FONTS { + let selected = font == *id; + let font_id = (*id).to_string(); + block = block.child( + div() + .mt(px(8.)) + .child( + Button::new(format!("font-{id}")) + .when(selected, |button| button.primary()) + .when(!selected, |button| button.outline()) + .label(*label) + .on_click(cx.listener(move |this, _, _, cx| { + this.reader_font = font_id.clone(); + let _ = backend::save_reader_font(&this.reader_font); + this.message = format!("Reader font set to {label}."); + cx.notify(); + })), + ) + .child( + div() + .text_size(px(13.)) + .text_color(cx.theme().muted_foreground) + .child(*description), + ), + ); + } + block.child( + div() + .mt(px(10.)) + .font_family(reader_family(&font)) + .child("The quick brown fox jumps over the lazy dog."), + ) + }) + } + + fn confirm_clear(&mut self, window: &mut Window, cx: &mut Context) { + let shell = cx.entity().clone(); + window.open_alert_dialog(cx, move |alert, _, _| { + alert + .confirm() + .title("Clear library path") + .description("Grimoire will not open a library until you choose one again.") + .on_ok({ + let shell = shell.clone(); + move |_, _, cx| { + shell + .downgrade().update_in(cx, |this, _, cx| { + match backend::clear_library(&this.state) { + Ok(status) => { + this.apply_status(status); + this.books.clear(); + this.message = "Saved library path cleared.".into(); + } + Err(error) => this.error = error.to_string(), + } + cx.notify(); + }) + .ok(); + true + } + }) + }); + } +} + +fn settings_block(title: &str, body: &str, cx: &gpui_kit::App) -> gpui_kit::Div { + div() + .max_w(px(640.)) + .p(px(16.)) + .rounded(px(10.)) + .bg(cx.theme().popover) + .border_1() + .border_color(cx.theme().border) + .child( + div() + .font_family(DISPLAY) + .text_size(px(18.)) + .font_weight(FontWeight::SEMIBOLD) + .child(title.to_string()), + ) + .child( + div() + .mt(px(6.)) + .text_color(cx.theme().muted_foreground) + .child(body.to_string()), + ) +} + +pub fn reader_family(id: &str) -> &'static str { + match id { + "sans" => ".SystemUIFont", + "opendyslexic" => "OpenDyslexic", + _ => DISPLAY, + } +} + +#[cfg(test)] +mod tests { + use super::{reader_family, DISPLAY, FONTS}; + + #[test] + fn settings_offers_the_reader_font_choices() { + let ids: Vec<_> = FONTS.iter().map(|font| font.0).collect(); + assert_eq!(ids, ["publisher", "serif", "sans", "opendyslexic"]); + } + + #[test] + fn reader_family_maps_each_choice() { + assert_eq!(reader_family("publisher"), DISPLAY); + assert_eq!(reader_family("serif"), DISPLAY); + assert_eq!(reader_family("sans"), ".SystemUIFont"); + assert_eq!(reader_family("opendyslexic"), "OpenDyslexic"); + } + + #[test] + fn unknown_reader_font_uses_the_reading_serif() { + assert_eq!(reader_family("comic-sans"), DISPLAY); + } +} diff --git a/app/src/ui/shell.rs b/app/src/ui/shell.rs new file mode 100644 index 0000000..ecec8da --- /dev/null +++ b/app/src/ui/shell.rs @@ -0,0 +1,648 @@ +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; +use std::time::Duration; + +use gpui_kit::component::button::{Button, ButtonVariants as _}; +use gpui_kit::component::input::{InputEvent, InputState, TextareaState}; +use gpui_kit::base::InteractiveElementExt as _; +use gpui_kit::component::menu::{DropdownMenu as _, PopupMenuItem}; +use gpui_kit::component::{ActiveTheme as _, IconName, Selectable as _}; +use gpui_kit::prelude::FluentBuilder as _; +use gpui_kit::AppContext as _; +use gpui_kit::{ + Anchor, App, Context, Entity, ExternalPaths, FontWeight, Image, ImageFormat, + InteractiveElement as _, IntoElement, MouseButton, ParentElement as _, Render, Styled as _, + Subscription, Window, div, img, px, +}; + +use super::backend::{self, LibraryStatus, TokioRt}; +use super::theme::{self, DISPLAY}; +use crate::kindle::{DeviceLibrary, EbookReaderDevice, SyncRecord}; +use crate::library::{BookDetail, BookSummary}; +use crate::state::AppState; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Route { + Library, + Devices, + Settings, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum ReadingStatus { + Reading, + ToRead, + Read, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum FormatKind { + Novel, + Comic, +} + +pub struct Shell { + pub state: AppState, + pub route: Route, + pub book_id: Option, + pub device_id: Option, + pub theme_pref: String, + pub reader_font: String, + pub devices: Vec, + pub notice: Option, + pub booting: bool, + pub boot_error: String, + pub library_open: bool, + pub library_path: Option, + pub kindle_convert: bool, + pub books: Vec, + pub covers: HashMap, + pub search: Entity, + pub search_gen: u64, + pub loading: bool, + pub busy: bool, + pub error: String, + pub message: String, + pub filters_open: bool, + pub select_mode: bool, + pub selected: HashSet, + pub filter_status: HashSet, + pub filter_genres: HashSet, + pub filter_formats: HashSet, + pub filter_authors: HashSet, + pub bulk_open: bool, + pub bulk_series: bool, + pub bulk_index: bool, + pub bulk_notes: bool, + pub bulk_tags: Entity, + pub bulk_series_input: Entity, + pub bulk_index_input: Entity, + pub bulk_notes_input: Entity, + pub book: Option, + pub book_cover: Option, + pub convert_targets: Vec, + pub title_input: Entity, + pub authors_input: Entity, + pub tags_input: Entity, + pub series_input: Entity, + pub series_index_input: Entity, + pub isbn_input: Entity, + pub comment_input: Entity, + pub sync_history: Vec, + pub devices_loading: bool, + pub device_library: Option, + pub device_query: Entity, + pub device_selected: HashSet, + pub device_select_mode: bool, + titlebar_drag: bool, + _subs: Vec, +} + +impl Shell { + pub fn new(window: &mut Window, cx: &mut Context) -> Self { + let search = cx.new(|cx| { + InputState::new(window, cx).placeholder("Search title, author, or tag") + }); + let bulk_tags = cx.new(|cx| InputState::new(window, cx).placeholder("Tags to add")); + let bulk_series_input = + cx.new(|cx| InputState::new(window, cx).placeholder("Series name (blank clears)")); + let bulk_index_input = cx.new(|cx| InputState::new(window, cx).placeholder("Index")); + let bulk_notes_input = cx.new(|cx| TextareaState::new(window, cx).placeholder("Notes")); + let title_input = cx.new(|cx| InputState::new(window, cx)); + let authors_input = cx.new(|cx| InputState::new(window, cx).placeholder("Author, Author")); + let tags_input = cx.new(|cx| InputState::new(window, cx).placeholder("tag, tag")); + let series_input = cx.new(|cx| InputState::new(window, cx)); + let series_index_input = cx.new(|cx| InputState::new(window, cx)); + let isbn_input = cx.new(|cx| InputState::new(window, cx)); + let comment_input = cx.new(|cx| TextareaState::new(window, cx).placeholder("Notes")); + let device_query = cx.new(|cx| InputState::new(window, cx).placeholder("Filter device books")); + + let search_sub = cx.subscribe_in(&search, window, |this, input, event, window, cx| { + if matches!(event, InputEvent::Change) { + let query = input.read(cx).value().to_string(); + this.queue_search(query, window, cx); + } + }); + + let config = backend::config().unwrap_or_default(); + let theme_pref = config.theme.clone(); + let reader_font = config.reader_font.clone(); + + let shell = Self { + state: AppState::new(), + route: Route::Library, + book_id: None, + device_id: None, + theme_pref, + reader_font, + devices: Vec::new(), + notice: None, + booting: true, + boot_error: String::new(), + library_open: false, + library_path: None, + kindle_convert: true, + books: Vec::new(), + covers: HashMap::new(), + search, + search_gen: 0, + loading: true, + busy: false, + error: String::new(), + message: String::new(), + filters_open: true, + select_mode: false, + selected: HashSet::new(), + filter_status: HashSet::new(), + filter_genres: HashSet::new(), + filter_formats: HashSet::new(), + filter_authors: HashSet::new(), + bulk_open: false, + bulk_series: false, + bulk_index: false, + bulk_notes: false, + bulk_tags, + bulk_series_input, + bulk_index_input, + bulk_notes_input, + book: None, + book_cover: None, + convert_targets: Vec::new(), + title_input, + authors_input, + tags_input, + series_input, + series_index_input, + isbn_input, + comment_input, + sync_history: Vec::new(), + devices_loading: false, + device_library: None, + device_query, + device_selected: HashSet::new(), + device_select_mode: false, + titlebar_drag: false, + _subs: vec![search_sub], + }; + shell.boot(window, cx); + shell.watch_devices(window, cx); + shell + } + + pub fn runtime(&self, cx: &App) -> tokio::runtime::Handle { + cx.global::().0.clone() + } + + pub fn spawn_io( + &self, + window: &mut Window, + cx: &mut Context, + work: impl FnOnce() -> Result + Send + 'static, + done: Done, + ) where + T: Send + 'static, + Done: FnOnce(&mut Self, Result, &mut Window, &mut Context) + 'static, + { + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + let result = match runtime.spawn_blocking(work).await { + Ok(result) => result, + Err(error) => Err(error.to_string()), + }; + this.update_in(cx, |this, window, cx| done(this, result, window, cx)) + .ok(); + }) + .detach(); + } + + pub fn spawn_task( + &self, + window: &mut Window, + cx: &mut Context, + work: Fut, + done: Done, + ) where + T: Send + 'static, + Fut: std::future::Future> + Send + 'static, + Done: FnOnce(&mut Self, Result, &mut Window, &mut Context) + 'static, + { + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + let result = match runtime.spawn(work).await { + Ok(result) => result, + Err(error) => Err(error.to_string()), + }; + this.update_in(cx, |this, window, cx| done(this, result, window, cx)) + .ok(); + }) + .detach(); + } + + fn boot(&self, window: &mut Window, cx: &mut Context) { + let state = self.state.clone(); + self.spawn_io( + window, + cx, + move || backend::open_saved(&state).map_err(|e| e.to_string()), + |this, result, window, cx| { + this.booting = false; + match result { + Ok(status) => { + this.apply_status(status); + if this.library_open { + this.reload_books(window, cx); + } else { + this.loading = false; + } + } + Err(error) => { + this.boot_error = error; + this.loading = false; + } + } + cx.notify(); + }, + ); + } + + fn watch_devices(&self, window: &mut Window, cx: &mut Context) { + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + let mut seen: Vec = Vec::new(); + loop { + let next = runtime.spawn(async { backend::readers().await }).await; + let _ = this.update(cx, |this, cx| { + if let Ok(Ok(devices)) = next { + let ids: Vec = devices.iter().map(|d| d.id.clone()).collect(); + if !seen.is_empty() { + for device in &devices { + if !seen.iter().any(|id| id == &device.id) { + this.notice = Some(format!("{} connected.", device.product)); + } + } + for id in &seen { + if !ids.iter().any(|next| next == id) { + let name = this + .devices + .iter() + .find(|device| &device.id == id) + .map(|device| device.product.clone()) + .unwrap_or_else(|| "eReader".into()); + this.notice = Some(format!("{name} disconnected.")); + } + } + } + seen = ids; + this.devices = devices; + cx.notify(); + } + }); + runtime + .spawn(async { tokio::time::sleep(Duration::from_secs(4)).await }) + .await + .ok(); + } + }) + .detach(); + } + + pub fn apply_status(&mut self, status: LibraryStatus) { + self.library_open = status.open; + self.library_path = status.path; + self.kindle_convert = status.kindle_convert_available; + } + + pub fn go(&mut self, route: Route, cx: &mut Context) { + self.route = route; + if !matches!(route, Route::Library) { + self.book_id = None; + } + cx.notify(); + } + + pub fn queue_search(&mut self, _query: String, window: &mut Window, cx: &mut Context) { + self.search_gen = self.search_gen.wrapping_add(1); + let generation = self.search_gen; + let runtime = self.runtime(cx); + cx.spawn_in(window, async move |this, cx| { + runtime + .spawn(async { tokio::time::sleep(Duration::from_millis(220)).await }) + .await + .ok(); + this.update_in(cx, |this, window, cx| { + if this.search_gen == generation { + this.reload_books(window, cx); + } + }) + .ok(); + }) + .detach(); + } + + pub fn set_theme(&mut self, pref: &str, cx: &mut Context) { + self.theme_pref = pref.to_string(); + theme::persist_and_apply(pref, cx); + cx.notify(); + } +} + +impl Render for Shell { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + let paper = cx.theme().background; + let ink = cx.theme().foreground; + let line = cx.theme().border; + let panel = cx.theme().popover; + + div() + .id("grimoire-root") + .size_full() + .flex() + .flex_col() + .bg(paper) + .text_color(ink) + .font_family(super::theme::UI_FONT) + .text_size(px(15.)) + .on_drop(cx.listener(|this, paths: &ExternalPaths, window, cx| { + let files = paths.paths().to_vec(); + this.import_paths(files, window, cx); + })) + .child(self.topbar(window, cx)) + .child( + div() + .flex_1() + .min_h(px(0.)) + .overflow_hidden() + .child(self.body(window, cx)), + ) + .when_some(self.notice.clone(), |this, notice| { + this.child(notice_toast(notice, panel, line, cx)) + }) + } +} + +impl Shell { + fn topbar(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let accent = cx.theme().primary; + let muted = cx.theme().muted_foreground; + let line = cx.theme().border; + let bar = cx.theme().title_bar; + #[cfg(target_os = "macos")] + let leading = px(78.); + #[cfg(not(target_os = "macos"))] + let leading = px(16.); + + div() + .h(px(52.)) + .flex() + .items_center() + .px(px(12.)) + .gap(px(8.)) + .bg(bar) + .border_b_1() + .border_color(line) + .child( + div() + .id("titlebar-drag") + .h_full() + .flex() + .items_center() + .gap(px(8.)) + .pl(leading) + .on_double_click(cx.listener(|this, _, window, _| { + this.titlebar_drag = false; + window.zoom_window(); + })) + .on_mouse_down( + MouseButton::Left, + cx.listener(|this, _, _, _| this.titlebar_drag = true), + ) + .on_mouse_up( + MouseButton::Left, + cx.listener(|this, _, _, _| this.titlebar_drag = false), + ) + .on_mouse_down_out(cx.listener(|this, _, _, _| this.titlebar_drag = false)) + .on_mouse_move(cx.listener(|this, _, window, _| { + if this.titlebar_drag { + this.titlebar_drag = false; + window.start_window_move(); + } + })) + .child( + img(std::sync::Arc::new(Image::from_bytes( + ImageFormat::Svg, + include_bytes!("../../../static/grimoire_icon.svg").to_vec(), + ))) + .size(px(28.)), + ) + .child( + div() + .flex() + .items_end() + .font_family(DISPLAY) + .text_size(px(24.)) + .font_weight(FontWeight::SEMIBOLD) + .child("Grimoire") + .child(div().text_color(accent).child(".")), + ), + ) + .child( + div() + .id("titlebar-spacer") + .flex_1() + .h_full() + .on_double_click(cx.listener(|this, _, window, _| { + this.titlebar_drag = false; + window.zoom_window(); + })) + .on_mouse_down( + MouseButton::Left, + cx.listener(|this, _, _, _| this.titlebar_drag = true), + ) + .on_mouse_up( + MouseButton::Left, + cx.listener(|this, _, _, _| this.titlebar_drag = false), + ) + .on_mouse_down_out(cx.listener(|this, _, _, _| this.titlebar_drag = false)) + .on_mouse_move(cx.listener(|this, _, window, _| { + if this.titlebar_drag { + this.titlebar_drag = false; + window.start_window_move(); + } + })), + ) + .child(self.nav_button("nav-library", IconName::BookOpen, "Library", self.route == Route::Library && self.book_id.is_none(), Route::Library, cx)) + .child(self.nav_button("nav-devices", IconName::HardDrive, "Devices", self.route == Route::Devices, Route::Devices, cx)) + .child(self.nav_button("nav-settings", IconName::Settings, "Settings", self.route == Route::Settings, Route::Settings, cx)) + .child(self.theme_menu(cx)) + .child(div().w(px(4.)).text_color(muted)) + } + + fn theme_menu(&mut self, cx: &mut Context) -> impl IntoElement { + let current = self.theme_pref.clone(); + let shell = cx.entity(); + Button::new("theme") + .ghost() + .compact() + .icon(if cx.theme().is_dark() { + IconName::Sun + } else { + IconName::Moon + }) + .tooltip(format!("Theme: {}", theme::label(¤t))) + .dropdown_menu_with_anchor(Anchor::TopRight, move |menu, _, _| { + menu.item(theme_choice("Dark", "dark", ¤t, shell.clone())) + .item(theme_choice("Light", "light", ¤t, shell.clone())) + .item(theme_choice( + "Follow System", + "system", + ¤t, + shell.clone(), + )) + }) + } + + fn nav_button( + &self, + id: &'static str, + icon: IconName, + tip: &'static str, + active: bool, + route: Route, + cx: &mut Context, + ) -> impl IntoElement { + let count = if id == "nav-devices" { + self.devices.len() + } else { + 0 + }; + div().relative().child( + Button::new(id) + .ghost() + .compact() + .icon(icon) + .selected(active) + .tooltip(tip) + .on_click(cx.listener(move |this, _, window, cx| { + this.book_id = None; + this.device_id = None; + this.route = route; + if route == Route::Devices { + this.refresh_devices(window, cx); + } + cx.notify(); + })), + ).when(count > 0, |this| { + this.child( + div() + .absolute() + .top(px(0.)) + .right(px(0.)) + .px(px(4.)) + .rounded(px(99.)) + .text_size(px(10.)) + .bg(cx.theme().primary) + .text_color(cx.theme().primary_foreground) + .child(count.to_string()), + ) + }) + } + + fn body(&mut self, window: &mut Window, cx: &mut Context) -> gpui_kit::AnyElement { + if self.booting { + return muted_page("Opening library…", cx).into_any_element(); + } + if !self.boot_error.is_empty() && !self.library_open && self.route == Route::Library { + return muted_page(&self.boot_error, cx).into_any_element(); + } + if self.book_id.is_some() { + return self.book_page(window, cx).into_any_element(); + } + match self.route { + Route::Library => self.library_page(window, cx).into_any_element(), + Route::Devices => { + if self.device_id.is_some() { + self.device_library_page(window, cx).into_any_element() + } else { + self.devices_page(window, cx).into_any_element() + } + } + Route::Settings => self.settings_page(window, cx).into_any_element(), + } + } +} + +fn notice_toast( + notice: String, + panel: gpui_kit::Hsla, + line: gpui_kit::Hsla, + cx: &mut Context, +) -> impl IntoElement { + div() + .absolute() + .bottom(px(18.)) + .right(px(18.)) + .max_w(px(360.)) + .flex() + .items_center() + .gap(px(10.)) + .px(px(14.)) + .py(px(10.)) + .rounded(px(10.)) + .bg(panel) + .border_1() + .border_color(line) + .child(div().flex_1().child(notice)) + .child( + Button::new("dismiss-notice") + .ghost() + .compact() + .icon(IconName::Close) + .tooltip("Dismiss") + .on_click(cx.listener(|this, _, _, cx| { + this.notice = None; + cx.notify(); + })), + ) +} + +pub fn muted_page(text: &str, cx: &App) -> impl IntoElement { + div() + .p(px(28.)) + .text_color(cx.theme().muted_foreground) + .child(text.to_string()) +} + +pub fn section_title(text: &str, count: Option) -> impl IntoElement { + div() + .flex() + .items_end() + .gap(px(8.)) + .child( + div() + .font_family(DISPLAY) + .text_size(px(20.)) + .font_weight(FontWeight::SEMIBOLD) + .child(text.to_string()), + ) + .when_some(count, |this, count| { + this.child( + div() + .text_size(px(13.)) + .font_weight(FontWeight::SEMIBOLD) + .child(count.to_string()), + ) + }) +} + +fn theme_choice( + label: &'static str, + pref: &'static str, + current: &str, + shell: Entity, +) -> PopupMenuItem { + PopupMenuItem::new(label) + .checked(current == pref) + .on_click(move |_, _, cx| { + shell.update(cx, |this, cx| this.set_theme(pref, cx)); + }) +} diff --git a/app/src/ui/theme.rs b/app/src/ui/theme.rs new file mode 100644 index 0000000..f5a2d41 --- /dev/null +++ b/app/src/ui/theme.rs @@ -0,0 +1,214 @@ +use gpui_kit::component::theme::{Theme, ThemeColor, ThemeMode}; +use gpui_kit::{App, Hsla, WindowAppearance, px, rgb}; + +use crate::config::{load_config, set_theme_preference}; + +pub const DISPLAY: &str = "Iowan Old Style"; +pub const UI_FONT: &str = ".SystemUIFont"; + +pub fn apply_saved(cx: &mut App) { + let pref = load_config() + .map(|config| config.theme) + .unwrap_or_else(|_| "dark".into()); + apply(&pref, cx); +} + +pub fn label(pref: &str) -> &'static str { + match pref { + "light" => "Light", + "system" => "Follow System", + _ => "Dark", + } +} + +pub fn persist_and_apply(pref: &str, cx: &mut App) { + let _ = set_theme_preference(pref); + apply(pref, cx); +} + +pub fn apply(pref: &str, cx: &mut App) { + let dark = match pref { + "light" => false, + "system" => matches!( + cx.window_appearance(), + WindowAppearance::Dark | WindowAppearance::VibrantDark + ), + _ => true, + }; + if pref == "system" { + cx.set_window_appearance(None); + } else if dark { + cx.set_window_appearance(Some(WindowAppearance::Dark)); + } else { + cx.set_window_appearance(Some(WindowAppearance::Light)); + } + Theme::change( + if dark { ThemeMode::Dark } else { ThemeMode::Light }, + None, + cx, + ); + Theme::update(cx, |theme| { + theme.colors = if dark { dark_palette() } else { light_palette() }; + theme.font_family = UI_FONT.into(); + theme.font_size = px(15.); + theme.radius = px(10.); + theme.radius_lg = px(14.); + }); +} + +fn paint(hex: u32) -> Hsla { + rgb(hex).into() +} + +fn paint_a(hex: u32, alpha: f32) -> Hsla { + let mut color: Hsla = rgb(hex).into(); + color.a = alpha; + color +} + +fn light_palette() -> ThemeColor { + let mut color = *ThemeColor::light(); + let ink = paint(0x1c1814); + let muted = paint(0x5c534a); + let paper = paint(0xf3ece0); + let paper_deep = paint(0xe6dccb); + let panel = paint(0xfaf6ee); + let accent = paint(0x2f5d50); + let accent_hover = paint(0x244a40); + let danger = paint(0x8b3a2f); + let line = paint_a(0x1c1814, 0.12); + let control = paint(0xfffdf8); + let btn = paint(0xf8f5ef); + fill( + &mut color, + ink, + muted, + paper, + paper_deep, + panel, + accent, + accent_hover, + danger, + line, + control, + btn, + paint(0xe4efe9), + ); + color +} + +fn dark_palette() -> ThemeColor { + let mut color = *ThemeColor::dark(); + let ink = paint(0xefe6d8); + let muted = paint(0xb0a493); + let paper = paint(0x1a1714); + let paper_deep = paint(0x12100e); + let panel = paint(0x24201c); + let accent = paint(0x7fb5a4); + let accent_hover = paint(0x9ac9ba); + let danger = paint(0xd88476); + let line = paint_a(0xefe6d8, 0.12); + let control = paint(0x2c2823); + let btn = paint(0x1a1714); + fill( + &mut color, + ink, + muted, + paper, + paper_deep, + panel, + accent, + accent_hover, + danger, + line, + control, + btn, + paint(0x2c3834), + ); + color +} + +fn fill( + color: &mut ThemeColor, + ink: Hsla, + muted: Hsla, + paper: Hsla, + paper_deep: Hsla, + panel: Hsla, + accent: Hsla, + accent_hover: Hsla, + danger: Hsla, + line: Hsla, + control: Hsla, + btn: Hsla, + accent_wash: Hsla, +) { + color.background = paper; + color.foreground = ink; + color.muted = paper_deep; + color.muted_foreground = muted; + color.border = line; + color.popover = panel; + color.popover_foreground = ink; + color.group_box = panel; + color.group_box_foreground = ink; + color.primary = accent; + color.primary_hover = accent_hover; + color.primary_active = accent_hover; + color.primary_foreground = btn; + color.accent = accent; + color.accent_foreground = btn; + color.ring = accent; + color.danger = danger; + color.danger_hover = danger; + color.danger_active = danger; + color.danger_foreground = paint(0xfff8f5); + color.input = control; + color.caret = accent; + color.title_bar = panel; + color.title_bar_border = line; + color.sidebar = paper_deep; + color.sidebar_foreground = ink; + color.sidebar_border = line; + color.sidebar_accent = accent_wash; + color.sidebar_accent_foreground = accent; + color.sidebar_primary = accent; + color.sidebar_primary_foreground = btn; + color.list = panel; + color.list_hover = accent_wash; + color.list_active = accent_wash; + color.list_active_border = accent; + color.selection = accent_wash; + color.link = accent; + color.link_hover = accent_hover; + color.link_active = accent_hover; + color.button_primary = accent; + color.button_primary_hover = accent_hover; + color.button_primary_active = accent_hover; + color.button_primary_foreground = btn; + color.button_danger = danger; + color.button_danger_hover = danger; + color.button_danger_active = danger; + color.button_danger_foreground = paint(0xfff8f5); + color.tab_active = accent; + color.tab_active_foreground = btn; + color.scrollbar_thumb = line; + color.window_border = line; +} + +#[cfg(test)] +mod tests { + use super::label; + + #[test] + fn labels_known_preferences() { + assert_eq!(label("light"), "Light"); + assert_eq!(label("dark"), "Dark"); + assert_eq!(label("system"), "Follow System"); + } + + #[test] + fn unknown_preference_labels_as_dark() { + assert_eq!(label("neon"), "Dark"); + } +} diff --git a/package.json b/package.json deleted file mode 100644 index e7623c2..0000000 --- a/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "grimoire", - "version": "0.1.9", - "description": "Grimoire - eBook collection manager", - "type": "module", - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "prepare": "svelte-kit sync || echo ''", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "test": "vitest run", - "test:watch": "vitest", - "test:rust": "cargo test --manifest-path src-tauri/Cargo.toml", - "test:all": "pnpm test && pnpm test:rust", - "tauri": "tauri" - }, - "license": "MPL-2.0", - "dependencies": { - "@lucide/svelte": "^1.44.0", - "@tauri-apps/api": "^2", - "@tauri-apps/plugin-dialog": "^2.7.3", - "@tauri-apps/plugin-fs": "^2.5.2", - "@tauri-apps/plugin-opener": "^2", - "epubjs": "^0.3.93" - }, - "devDependencies": { - "@sveltejs/adapter-static": "^3.0.10", - "@sveltejs/kit": "^2.65.1", - "@sveltejs/vite-plugin-svelte": "^7.1.2", - "@tauri-apps/cli": "^2", - "happy-dom": "^20.14.3", - "svelte": "^5.56.3", - "svelte-check": "^4.6.0", - "typescript": "~6.0.3", - "vite": "^8.0.16", - "vitest": "^5.0.0" - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index 6caf776..0000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,1462 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - '@xmldom/xmldom': 0.8.15 - -importers: - - .: - dependencies: - '@lucide/svelte': - specifier: ^1.44.0 - version: 1.44.0(svelte@5.57.0) - '@tauri-apps/api': - specifier: ^2 - version: 2.11.1 - '@tauri-apps/plugin-dialog': - specifier: ^2.7.3 - version: 2.7.3 - '@tauri-apps/plugin-fs': - specifier: ^2.5.2 - version: 2.5.2 - '@tauri-apps/plugin-opener': - specifier: ^2 - version: 2.5.5 - epubjs: - specifier: ^0.3.93 - version: 0.3.93 - devDependencies: - '@sveltejs/adapter-static': - specifier: ^3.0.10 - version: 3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)))(svelte@5.57.0)(typescript@6.0.3)(vite@8.2.2(@types/node@22.20.2))) - '@sveltejs/kit': - specifier: ^2.65.1 - version: 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)))(svelte@5.57.0)(typescript@6.0.3)(vite@8.2.2(@types/node@22.20.2)) - '@sveltejs/vite-plugin-svelte': - specifier: ^7.1.2 - version: 7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)) - '@tauri-apps/cli': - specifier: ^2 - version: 2.11.4 - happy-dom: - specifier: ^20.14.3 - version: 20.14.3 - svelte: - specifier: ^5.56.3 - version: 5.57.0 - svelte-check: - specifier: ^4.6.0 - version: 4.7.6(picomatch@4.0.7)(svelte@5.57.0)(typescript@6.0.3) - typescript: - specifier: ~6.0.3 - version: 6.0.3 - vite: - specifier: ^8.0.16 - version: 8.2.2(@types/node@22.20.2) - vitest: - specifier: ^5.0.0 - version: 5.0.0(@types/node@22.20.2)(happy-dom@20.14.3)(vite@8.2.2(@types/node@22.20.2)) - -packages: - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.6.0': - resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@lucide/svelte@1.44.0': - resolution: {integrity: sha512-67510ne/YowceSyN9BECQaWfp5Ed446X6xpUE5C0e4rRV0e1jRj1KzIlKJCXcGc21hgr/VBDkqQXY7v4VQDeBw==} - peerDependencies: - svelte: ^5 - - '@oxc-project/types@0.149.0': - resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} - - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - - '@rolldown/binding-android-arm-eabi@1.2.8': - resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@rolldown/binding-android-arm64@1.2.8': - resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.2.8': - resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.2.8': - resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.2.8': - resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-linux-arm-gnueabihf@1.2.8': - resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm64-gnu@1.2.8': - resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-arm64-musl@1.2.8': - resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-ppc64-gnu@1.2.8': - resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-s390x-gnu@1.2.8': - resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-gnu@1.2.8': - resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-musl@1.2.8': - resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rolldown/binding-openharmony-arm64@1.2.8': - resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-win32-arm64-msvc@1.2.8': - resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.2.8': - resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@sveltejs/acorn-typescript@1.0.13': - resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} - peerDependencies: - acorn: ^8.9.0 - - '@sveltejs/adapter-static@3.0.10': - resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} - peerDependencies: - '@sveltejs/kit': ^2.0.0 - - '@sveltejs/kit@2.70.3': - resolution: {integrity: sha512-UDvEYuZqAMbfB/oXIoqKvbKcb7YczK5zYrzmsGV1zRJk03jntwp8dXiYoIJotxAndsKvcPFtx9H1GRSKFdSHgg==} - engines: {node: '>=18.13'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.0.0 - '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 - typescript: ^5.3.3 || ^6.0.0 - vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - typescript: - optional: true - - '@sveltejs/load-config@0.2.3': - resolution: {integrity: sha512-VT3qmUb8pRV2QrZjd8iAmtg8lf4W0TIjZbvXtz5MKei/q96teWZgGJyyidJzOjzZzvdq616eSRVeMYIQChUTAQ==} - engines: {node: '>= 18.0.0'} - - '@sveltejs/vite-plugin-svelte@7.3.0': - resolution: {integrity: sha512-QbRoJyD92e9R0ufeQIWRHrCC0ObcqSv/aBDdrQMoU+sypav3cDx5wytdQ6GLdXjEMO6xjrXGzfkUygng8JMv0A==} - engines: {node: ^20.19 || ^22.12 || >=24} - peerDependencies: - svelte: ^5.46.4 - vite: ^8.0.0-beta.7 || ^8.0.0 - - '@tauri-apps/api@2.11.1': - resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} - - '@tauri-apps/cli-darwin-arm64@2.11.4': - resolution: {integrity: sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@tauri-apps/cli-darwin-x64@2.11.4': - resolution: {integrity: sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': - resolution: {integrity: sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - - '@tauri-apps/cli-linux-arm64-gnu@2.11.4': - resolution: {integrity: sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@tauri-apps/cli-linux-arm64-musl@2.11.4': - resolution: {integrity: sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': - resolution: {integrity: sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==} - engines: {node: '>= 10'} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@tauri-apps/cli-linux-x64-gnu@2.11.4': - resolution: {integrity: sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@tauri-apps/cli-linux-x64-musl@2.11.4': - resolution: {integrity: sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@tauri-apps/cli-win32-arm64-msvc@2.11.4': - resolution: {integrity: sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@tauri-apps/cli-win32-ia32-msvc@2.11.4': - resolution: {integrity: sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@tauri-apps/cli-win32-x64-msvc@2.11.4': - resolution: {integrity: sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@tauri-apps/cli@2.11.4': - resolution: {integrity: sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==} - engines: {node: '>= 10'} - hasBin: true - - '@tauri-apps/plugin-dialog@2.7.3': - resolution: {integrity: sha512-CRgE+7TP4tvq9MjBU6f04NLTFIqVMLKHk3hAqlhil00ngK9ACTrXPH3oHpKMProxILodd3YjBoKbMwSI4IEcfA==} - - '@tauri-apps/plugin-fs@2.5.2': - resolution: {integrity: sha512-XXvMSnFiob+G1H+YHCDf+bzWVumseQuEIhzpbOJzevUfL4k0U+sTApZWJHpoLiamggnVPJm5dJ5sDsniRyWxlg==} - - '@tauri-apps/plugin-opener@2.5.5': - resolution: {integrity: sha512-xvzGai5aQds8j8R8RsUK/lW6pGG50YgOYIPLzvkqmkwAj7dfySOD7sGtejRzvVdMmv1EQfKVEFh1MvmDp8QR0g==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - - '@types/localforage@0.0.34': - resolution: {integrity: sha512-tJxahnjm9dEI1X+hQSC5f2BSd/coZaqbIl1m3TCl0q9SVuC52XcXfV0XmoCU1+PmjyucuVITwoTnN8OlTbEXXA==} - deprecated: This is a stub types definition for localforage (https://github.com/localForage/localForage). localforage provides its own type definitions, so you don't need @types/localforage installed! - - '@types/node@22.20.2': - resolution: {integrity: sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw==} - - '@types/whatwg-mimetype@3.0.2': - resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - - '@vitest/mocker@5.0.0': - resolution: {integrity: sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/spy@5.0.0': - resolution: {integrity: sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==} - - '@xmldom/xmldom@0.8.15': - resolution: {integrity: sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==} - engines: {node: '>=10.0.0'} - - acorn@8.18.0: - resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} - engines: {node: '>=0.4.0'} - hasBin: true - - aria-query@5.3.1: - resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} - engines: {node: '>= 0.4'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - - buffer-image-size@0.6.4: - resolution: {integrity: sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ==} - engines: {node: '>=4.0'} - - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - - core-js@3.50.0: - resolution: {integrity: sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - d@1.0.2: - resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} - engines: {node: '>=0.12'} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - devalue@5.9.4: - resolution: {integrity: sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} - - epubjs@0.3.93: - resolution: {integrity: sha512-c06pNSdBxcXv3dZSbXAVLE1/pmleRhOT6mXNZo6INKmvuKpYB65MwU/lO7830czCtjIiK9i+KR+3S+p0wtljrw==} - - es-module-lexer@2.3.2: - resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - - es5-ext@0.10.64: - resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} - engines: {node: '>=0.10'} - - es6-iterator@2.0.3: - resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} - - es6-symbol@3.1.4: - resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} - engines: {node: '>=0.12'} - - esm-env@1.2.2: - resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - - esniff@2.0.1: - resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} - engines: {node: '>=0.10'} - - esrap@2.3.7: - resolution: {integrity: sha512-n2nf7fZR3c9yXf0BPEuHuXqT+KW0SJVj4cN5FMEkpCZ3scLjOQWpiccyCxVzCC2q1wubTghuEGzngJY/7Ah0Ow==} - peerDependencies: - '@typescript-eslint/types': ^8.2.0 - peerDependenciesMeta: - '@typescript-eslint/types': - optional: true - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - event-emitter@0.3.5: - resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - - expect-type@1.4.0: - resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} - engines: {node: '>=12.0.0'} - - ext@1.7.0: - resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - happy-dom@20.14.3: - resolution: {integrity: sha512-0KMb/Eh8rsd+aMNkOk5h8LkAjo9Na1aksnEGmsuZf+GXl6UkAdIi0VbUHU7d59lXhNmawODgwwuCMiZpCxwwCw==} - engines: {node: '>=20.0.0'} - - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - is-reference@3.0.3: - resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - jszip@3.10.2: - resolution: {integrity: sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - lie@3.1.1: - resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} - - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - - lightningcss-android-arm64@1.33.0: - resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - - lightningcss-darwin-arm64@1.33.0: - resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - - lightningcss-darwin-x64@1.33.0: - resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - - lightningcss-freebsd-x64@1.33.0: - resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - - lightningcss-linux-arm-gnueabihf@1.33.0: - resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - - lightningcss-linux-arm64-gnu@1.33.0: - resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - lightningcss-linux-arm64-musl@1.33.0: - resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - - lightningcss-linux-x64-gnu@1.33.0: - resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - - lightningcss-linux-x64-musl@1.33.0: - resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - - lightningcss-win32-arm64-msvc@1.33.0: - resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - - lightningcss-win32-x64-msvc@1.33.0: - resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - - lightningcss@1.33.0: - resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} - engines: {node: '>= 12.0.0'} - - localforage@1.10.0: - resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} - - locate-character@3.0.0: - resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} - - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - magic-string@1.3.1: - resolution: {integrity: sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==} - - marks-pane@1.0.9: - resolution: {integrity: sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg==} - - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - - nanoid@3.3.18: - resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - next-tick@1.1.0: - resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - - obug@2.2.1: - resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} - engines: {node: '>=12.20.0'} - - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - path-webpack@0.0.3: - resolution: {integrity: sha512-AmeDxedoo5svf7aB3FYqSAKqMxys014lVKBzy1o/5vv9CtU7U4wgGWL1dA2o6MOzcD53ScN4Jmiq6VbtLz1vIQ==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@4.0.7: - resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} - engines: {node: '>=12'} - - postcss@8.5.28: - resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} - engines: {node: ^10 || ^12 || >=14} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - rolldown@1.2.8: - resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - - sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - set-cookie-parser@3.1.2: - resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} - - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} - engines: {node: '>=18'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@4.2.0: - resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - svelte-check@4.7.6: - resolution: {integrity: sha512-t2scM//ZuVbSY/T2w6FSBw1v9s2NEmh/g+sy1lqtosW5ylBV5AF4wFb1Ts9Kf3MbfPDUDJDZ9L436YT0SPTdvw==} - engines: {node: '>= 18.0.0'} - hasBin: true - peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 - typescript: ^5.0.0 || ^6.0.0 - - svelte@5.57.0: - resolution: {integrity: sha512-NdbDn7fl4be1ViUG0oq/lvG6OZy3oENolV2ONjiqqsfVoeAfzaQAKUcEX3MrQod/Bebv1PgwET9rfXhgn9s4Kg==} - engines: {node: '>=18'} - - tinybench@6.1.4: - resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} - engines: {node: '>=20.0.0'} - - tinyexec@1.3.0: - resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} - engines: {node: '>=18'} - - tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} - engines: {node: '>=12.0.0'} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - type@2.7.3: - resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} - - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - vite@8.2.2: - resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.4.0 || ^0.5.0 - esbuild: ^0.27.0 || ^0.28.0 - jiti: '>=1.21.0' - less: ^4.0.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - '@vitejs/devtools': - optional: true - esbuild: - optional: true - jiti: - optional: true - less: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitefu@1.1.3: - resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} - peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - vite: - optional: true - - vitest@5.0.0: - resolution: {integrity: sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==} - engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 5.0.0 - '@vitest/browser-preview': 5.0.0 - '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 - '@vitest/coverage-istanbul': 5.0.0 - '@vitest/coverage-v8': 5.0.0 - '@vitest/ui': 5.0.0 - happy-dom: '*' - jsdom: '*' - vite: ^6.4.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - ws@8.21.3: - resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - zimmerframe@1.1.5: - resolution: {integrity: sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA==} - -snapshots: - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.6.0': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.6.0 - - '@lucide/svelte@1.44.0(svelte@5.57.0)': - dependencies: - svelte: 5.57.0 - - '@oxc-project/types@0.149.0': {} - - '@polka/url@1.0.0-next.29': {} - - '@rolldown/binding-android-arm-eabi@1.2.8': - optional: true - - '@rolldown/binding-android-arm64@1.2.8': - optional: true - - '@rolldown/binding-darwin-arm64@1.2.8': - optional: true - - '@rolldown/binding-darwin-x64@1.2.8': - optional: true - - '@rolldown/binding-freebsd-x64@1.2.8': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.2.8': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.2.8': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.2.8': - optional: true - - '@rolldown/binding-linux-ppc64-gnu@1.2.8': - optional: true - - '@rolldown/binding-linux-s390x-gnu@1.2.8': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.2.8': - optional: true - - '@rolldown/binding-linux-x64-musl@1.2.8': - optional: true - - '@rolldown/binding-openharmony-arm64@1.2.8': - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.2.8': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.2.8': - optional: true - - '@rolldown/pluginutils@1.0.1': {} - - '@standard-schema/spec@1.1.0': {} - - '@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)': - dependencies: - acorn: 8.18.0 - - '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)))(svelte@5.57.0)(typescript@6.0.3)(vite@8.2.2(@types/node@22.20.2)))': - dependencies: - '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)))(svelte@5.57.0)(typescript@6.0.3)(vite@8.2.2(@types/node@22.20.2)) - - '@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)))(svelte@5.57.0)(typescript@6.0.3)(vite@8.2.2(@types/node@22.20.2))': - dependencies: - '@standard-schema/spec': 1.1.0 - '@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0) - '@sveltejs/vite-plugin-svelte': 7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2)) - '@types/cookie': 0.6.0 - acorn: 8.18.0 - cookie: 0.6.0 - devalue: 5.9.4 - esm-env: 1.2.2 - kleur: 4.1.5 - magic-string: 0.30.21 - mrmime: 2.0.1 - set-cookie-parser: 3.1.2 - sirv: 3.0.2 - svelte: 5.57.0 - vite: 8.2.2(@types/node@22.20.2) - optionalDependencies: - typescript: 6.0.3 - - '@sveltejs/load-config@0.2.3': {} - - '@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.2.2(@types/node@22.20.2))': - dependencies: - deepmerge: 4.3.1 - magic-string: 1.3.1 - obug: 2.2.1 - svelte: 5.57.0 - vite: 8.2.2(@types/node@22.20.2) - vitefu: 1.1.3(vite@8.2.2(@types/node@22.20.2)) - - '@tauri-apps/api@2.11.1': {} - - '@tauri-apps/cli-darwin-arm64@2.11.4': - optional: true - - '@tauri-apps/cli-darwin-x64@2.11.4': - optional: true - - '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': - optional: true - - '@tauri-apps/cli-linux-arm64-gnu@2.11.4': - optional: true - - '@tauri-apps/cli-linux-arm64-musl@2.11.4': - optional: true - - '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': - optional: true - - '@tauri-apps/cli-linux-x64-gnu@2.11.4': - optional: true - - '@tauri-apps/cli-linux-x64-musl@2.11.4': - optional: true - - '@tauri-apps/cli-win32-arm64-msvc@2.11.4': - optional: true - - '@tauri-apps/cli-win32-ia32-msvc@2.11.4': - optional: true - - '@tauri-apps/cli-win32-x64-msvc@2.11.4': - optional: true - - '@tauri-apps/cli@2.11.4': - optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.11.4 - '@tauri-apps/cli-darwin-x64': 2.11.4 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.11.4 - '@tauri-apps/cli-linux-arm64-gnu': 2.11.4 - '@tauri-apps/cli-linux-arm64-musl': 2.11.4 - '@tauri-apps/cli-linux-riscv64-gnu': 2.11.4 - '@tauri-apps/cli-linux-x64-gnu': 2.11.4 - '@tauri-apps/cli-linux-x64-musl': 2.11.4 - '@tauri-apps/cli-win32-arm64-msvc': 2.11.4 - '@tauri-apps/cli-win32-ia32-msvc': 2.11.4 - '@tauri-apps/cli-win32-x64-msvc': 2.11.4 - - '@tauri-apps/plugin-dialog@2.7.3': - dependencies: - '@tauri-apps/api': 2.11.1 - - '@tauri-apps/plugin-fs@2.5.2': - dependencies: - '@tauri-apps/api': 2.11.1 - - '@tauri-apps/plugin-opener@2.5.5': - dependencies: - '@tauri-apps/api': 2.11.1 - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/cookie@0.6.0': {} - - '@types/deep-eql@4.0.2': {} - - '@types/estree@1.0.9': {} - - '@types/localforage@0.0.34': - dependencies: - localforage: 1.10.0 - - '@types/node@22.20.2': - dependencies: - undici-types: 6.21.0 - - '@types/whatwg-mimetype@3.0.2': {} - - '@types/ws@8.18.1': - dependencies: - '@types/node': 22.20.2 - - '@vitest/mocker@5.0.0(vite@8.2.2(@types/node@22.20.2))': - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - '@vitest/spy': 5.0.0 - estree-walker: 3.0.3 - magic-string: 1.3.1 - optionalDependencies: - vite: 8.2.2(@types/node@22.20.2) - - '@vitest/spy@5.0.0': {} - - '@xmldom/xmldom@0.8.15': {} - - acorn@8.18.0: {} - - aria-query@5.3.1: {} - - assertion-error@2.0.1: {} - - axobject-query@4.1.0: {} - - buffer-image-size@0.6.4: - dependencies: - '@types/node': 22.20.2 - - chai@6.2.2: {} - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - clsx@2.1.1: {} - - cookie@0.6.0: {} - - core-js@3.50.0: {} - - core-util-is@1.0.3: {} - - d@1.0.2: - dependencies: - es5-ext: 0.10.64 - type: 2.7.3 - - deepmerge@4.3.1: {} - - detect-libc@2.1.2: {} - - devalue@5.9.4: {} - - entities@7.0.1: {} - - epubjs@0.3.93: - dependencies: - '@types/localforage': 0.0.34 - '@xmldom/xmldom': 0.8.15 - core-js: 3.50.0 - event-emitter: 0.3.5 - jszip: 3.10.2 - localforage: 1.10.0 - lodash: 4.18.1 - marks-pane: 1.0.9 - path-webpack: 0.0.3 - - es-module-lexer@2.3.2: {} - - es5-ext@0.10.64: - dependencies: - es6-iterator: 2.0.3 - es6-symbol: 3.1.4 - esniff: 2.0.1 - next-tick: 1.1.0 - - es6-iterator@2.0.3: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - es6-symbol: 3.1.4 - - es6-symbol@3.1.4: - dependencies: - d: 1.0.2 - ext: 1.7.0 - - esm-env@1.2.2: {} - - esniff@2.0.1: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - event-emitter: 0.3.5 - type: 2.7.3 - - esrap@2.3.7: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.9 - - event-emitter@0.3.5: - dependencies: - d: 1.0.2 - es5-ext: 0.10.64 - - expect-type@1.4.0: {} - - ext@1.7.0: - dependencies: - type: 2.7.3 - - fdir@6.5.0(picomatch@4.0.7): - optionalDependencies: - picomatch: 4.0.7 - - fsevents@2.3.3: - optional: true - - happy-dom@20.14.3: - dependencies: - '@types/node': 22.20.2 - '@types/whatwg-mimetype': 3.0.2 - '@types/ws': 8.18.1 - buffer-image-size: 0.6.4 - entities: 7.0.1 - whatwg-mimetype: 3.0.0 - ws: 8.21.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - immediate@3.0.6: {} - - inherits@2.0.4: {} - - is-reference@3.0.3: - dependencies: - '@types/estree': 1.0.9 - - isarray@1.0.0: {} - - jszip@3.10.2: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - - kleur@4.1.5: {} - - lie@3.1.1: - dependencies: - immediate: 3.0.6 - - lie@3.3.0: - dependencies: - immediate: 3.0.6 - - lightningcss-android-arm64@1.33.0: - optional: true - - lightningcss-darwin-arm64@1.33.0: - optional: true - - lightningcss-darwin-x64@1.33.0: - optional: true - - lightningcss-freebsd-x64@1.33.0: - optional: true - - lightningcss-linux-arm-gnueabihf@1.33.0: - optional: true - - lightningcss-linux-arm64-gnu@1.33.0: - optional: true - - lightningcss-linux-arm64-musl@1.33.0: - optional: true - - lightningcss-linux-x64-gnu@1.33.0: - optional: true - - lightningcss-linux-x64-musl@1.33.0: - optional: true - - lightningcss-win32-arm64-msvc@1.33.0: - optional: true - - lightningcss-win32-x64-msvc@1.33.0: - optional: true - - lightningcss@1.33.0: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.33.0 - lightningcss-darwin-arm64: 1.33.0 - lightningcss-darwin-x64: 1.33.0 - lightningcss-freebsd-x64: 1.33.0 - lightningcss-linux-arm-gnueabihf: 1.33.0 - lightningcss-linux-arm64-gnu: 1.33.0 - lightningcss-linux-arm64-musl: 1.33.0 - lightningcss-linux-x64-gnu: 1.33.0 - lightningcss-linux-x64-musl: 1.33.0 - lightningcss-win32-arm64-msvc: 1.33.0 - lightningcss-win32-x64-msvc: 1.33.0 - - localforage@1.10.0: - dependencies: - lie: 3.1.1 - - locate-character@3.0.0: {} - - lodash@4.18.1: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - - magic-string@1.3.1: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 - - marks-pane@1.0.9: {} - - mri@1.2.0: {} - - mrmime@2.0.1: {} - - nanoid@3.3.18: {} - - next-tick@1.1.0: {} - - obug@2.2.1: {} - - pako@1.0.11: {} - - path-webpack@0.0.3: {} - - picocolors@1.1.1: {} - - picomatch@4.0.7: {} - - postcss@8.5.28: - dependencies: - nanoid: 3.3.18 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - process-nextick-args@2.0.1: {} - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readdirp@4.1.2: {} - - rolldown@1.2.8: - dependencies: - '@oxc-project/types': 0.149.0 - '@rolldown/pluginutils': 1.0.1 - optionalDependencies: - '@rolldown/binding-android-arm-eabi': 1.2.8 - '@rolldown/binding-android-arm64': 1.2.8 - '@rolldown/binding-darwin-arm64': 1.2.8 - '@rolldown/binding-darwin-x64': 1.2.8 - '@rolldown/binding-freebsd-x64': 1.2.8 - '@rolldown/binding-linux-arm-gnueabihf': 1.2.8 - '@rolldown/binding-linux-arm64-gnu': 1.2.8 - '@rolldown/binding-linux-arm64-musl': 1.2.8 - '@rolldown/binding-linux-ppc64-gnu': 1.2.8 - '@rolldown/binding-linux-s390x-gnu': 1.2.8 - '@rolldown/binding-linux-x64-gnu': 1.2.8 - '@rolldown/binding-linux-x64-musl': 1.2.8 - '@rolldown/binding-openharmony-arm64': 1.2.8 - '@rolldown/binding-win32-arm64-msvc': 1.2.8 - '@rolldown/binding-win32-x64-msvc': 1.2.8 - - sade@1.8.1: - dependencies: - mri: 1.2.0 - - safe-buffer@5.1.2: {} - - set-cookie-parser@3.1.2: {} - - setimmediate@1.0.5: {} - - siginfo@2.0.0: {} - - sirv@3.0.2: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - - source-map-js@1.2.1: {} - - stackback@0.0.2: {} - - std-env@4.2.0: {} - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - svelte-check@4.7.6(picomatch@4.0.7)(svelte@5.57.0)(typescript@6.0.3): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - '@sveltejs/load-config': 0.2.3 - chokidar: 4.0.3 - fdir: 6.5.0(picomatch@4.0.7) - picocolors: 1.1.1 - sade: 1.8.1 - svelte: 5.57.0 - typescript: 6.0.3 - transitivePeerDependencies: - - picomatch - - svelte@5.57.0: - dependencies: - '@jridgewell/remapping': 2.3.5 - '@jridgewell/sourcemap-codec': 1.6.0 - '@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0) - '@types/estree': 1.0.9 - acorn: 8.18.0 - aria-query: 5.3.1 - axobject-query: 4.1.0 - clsx: 2.1.1 - devalue: 5.9.4 - esm-env: 1.2.2 - esrap: 2.3.7 - is-reference: 3.0.3 - locate-character: 3.0.0 - magic-string: 0.30.21 - zimmerframe: 1.1.5 - transitivePeerDependencies: - - '@typescript-eslint/types' - - tinybench@6.1.4: {} - - tinyexec@1.3.0: {} - - tinyglobby@0.2.17: - dependencies: - fdir: 6.5.0(picomatch@4.0.7) - picomatch: 4.0.7 - - totalist@3.0.1: {} - - type@2.7.3: {} - - typescript@6.0.3: {} - - undici-types@6.21.0: {} - - util-deprecate@1.0.2: {} - - vite@8.2.2(@types/node@22.20.2): - dependencies: - lightningcss: 1.33.0 - picomatch: 4.0.7 - postcss: 8.5.28 - rolldown: 1.2.8 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 22.20.2 - fsevents: 2.3.3 - - vitefu@1.1.3(vite@8.2.2(@types/node@22.20.2)): - optionalDependencies: - vite: 8.2.2(@types/node@22.20.2) - - vitest@5.0.0(@types/node@22.20.2)(happy-dom@20.14.3)(vite@8.2.2(@types/node@22.20.2)): - dependencies: - '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.0(vite@8.2.2(@types/node@22.20.2)) - chai: 6.2.2 - es-module-lexer: 2.3.2 - expect-type: 1.4.0 - magic-string: 1.3.1 - obug: 2.2.1 - picomatch: 4.0.7 - std-env: 4.2.0 - tinybench: 6.1.4 - tinyexec: 1.3.0 - tinyglobby: 0.2.17 - vite: 8.2.2(@types/node@22.20.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.20.2 - happy-dom: 20.14.3 - transitivePeerDependencies: - - msw - - whatwg-mimetype@3.0.0: {} - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - ws@8.21.3: {} - - zimmerframe@1.1.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index 78ffc6d..0000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,13 +0,0 @@ -packages: - - . - -# pnpm 12+: approve packages that need install-time build scripts. -# See https://pnpm.io/settings/build#allowbuilds -allowBuilds: - core-js: true - es5-ext: true - esbuild: true - -# Force patched xmldom (epubjs still pins ^0.7.5; Dependabot high alerts). -overrides: - "@xmldom/xmldom": "0.8.15" diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore deleted file mode 100644 index b21bd68..0000000 --- a/src-tauri/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ - -# Generated by Tauri -# will have schema files for capabilities auto-completion -/gen/schemas diff --git a/src-tauri/build.rs b/src-tauri/build.rs deleted file mode 100644 index d860e1e..0000000 --- a/src-tauri/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - tauri_build::build() -} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json deleted file mode 100644 index f6553e6..0000000 --- a/src-tauri/capabilities/default.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "../gen/schemas/desktop-schema.json", - "identifier": "default", - "description": "Capability for Grimoire windows", - "windows": ["main", "reader-*"], - "permissions": [ - "core:default", - "core:webview:allow-create-webview-window", - "core:window:allow-set-focus", - "core:window:allow-close", - "core:window:allow-show", - "core:window:allow-set-title", - "core:window:allow-start-dragging", - "core:window:allow-minimize", - "core:window:allow-maximize", - "core:window:allow-unmaximize", - "core:window:allow-toggle-maximize", - "core:window:allow-is-maximized", - "opener:default", - "dialog:default" - ] -} diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 2ffbf24..0000000 --- a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 0281c8b180a54e0556766b8364c21b4b0856f6da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3092 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 UWE=eNr3aL}w6au#gh|-{06qZvL;wH) diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 8b73a02238124fca0619d9cc94f13de7dbe39265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3550 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 diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 49ecdae7aea7978e1adcd66cf75b441a68ded302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3439 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 Ry3_yw002ovPDHLkV1h7)w}Sux diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 28531ca44665f41a4a25c98fa1b239a932671f86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9103 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 LN>a5FMnV4vjpooq diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png deleted file mode 100644 index d9e5007217276171144386e4245b2195f2d11dba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39530 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 SrXP672ZN`ppUXO@geCyRmp>~2 diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index d51f60f5e1a77205f387809deae106f6cb7c1823..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69206 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Ⓢ#HxH?tV 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 diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 6cb3fb3c9c95178e3f7af5cbe694a2e11e940ba9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20470 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 diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index c85138e685659cbc72c4b8af18cecde8e214696f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27091 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 K6t5FC3i%&7qOKVL diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png deleted file mode 100644 index 5514c9a23965cdb721af5842ea0cb9c156bd94e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100555 zcma&NW3VW}(k-}c+qP}nwr!kk+qP|6XWO=I+t!@>-hB7RObmW>M0G{3%B<|Fj>?r; z5ejnRuuxb~00026k`f|H004mX|9K$5{`HK>F;D>jh9^pj2&%YmTy;Z6o1VImx&C(U ze!gsYnzMIiX)7a1DyGWN6MG#r9Gmh4tu-Ht{sEG?npzXc27$x@LC!!183Pa;5eS4N z!z5w8>jT_^0!uPYHr_d4J6L-BEwOSoTt_OSN-l{Ut{2 zTzS2D$-Cu0^*-h9Ad<~wv0Ny|6w*oTK>Nw|JCs?fuQ7S#_D8+;8EA~LU2u0t;tNtGGdzE7PdcptMkIDpL`Zt1WkGi9S#3zWD zp1$DGv(nF}-z&IpI^%a9Q>jY(fKv!_3S+I@w=mt0{G}N8(BSx8BBAmdmQerd{WE?A z@UP!*%I`(WGGYAp0KLx4k*=}{>{Sw+hJOEO3OcImdm})tH#x%KZKb-Nx1x;%oo)w7 zfZRsz)kF$)r=1zSZqJmy(o5<9whtY2XY}g2cc?y=SMK?+{>Q}0$j1cg*Y#@8NF;tm zuW}tm+{-seK$@1(5{%a$-@ltaDf3~TN{ee{PPJc-#edoTes6#7_~lfeUjg)T_`MMc zTr~sJqx<*@UXO;+QBm_#u3f^Y=yRGsQDJ>>SgZKow&{DUEUq#*3W5s@cZAC`FNis9 zj(k9Tu`(+&LQCBRALwS2cfTI|e#re)^vLO&$dLN@68c>34@9kZrh4UPQ}B3!@d3HH3_}rKH%|L79#$wV zrqO4ePxb}|Ur4xP=FH02y`aJ&kDnmF`w=g{SeAlw4miK#6!k>?AMD*OVVq zKW$7o6o6L+Y$Cc^m@W;seh**0QL;OJ<(39 z^DzUC9A?C_O`G8lKNE5ZYY2TkUrqGhs9BYHLCkUYQXlDu`@+HL`;3K~nH=Yi26y># z@4{@b7L+OzkHNmc21X{>0PXztqpij8qDR94ofXEI^AX0+M{f-sQI|#~=L}fFN8t=H zzWk@_`k%{bKUcpALvWA=@T~6NeaNS|dzl}**p)AXoP=m)$#CXt_s_J_tOp#g4HWJ; z=KWga95GohXe$eO#HtND<=iueak#(Q6cpUqv%!>J8y<^M{fH1@gXFx-I_ouH0RoAz zm*CjB4rd5&fcPW(j0@V~ulolsA(th;hrEw9qoSXFA3bl=-p*C9%H;km{K29lW=EKJ z5sZDMy}2H)D>djRwjH8O_*YJo0BMr_;6JT5^WmY<3%#uxvA=~6rT(v0kQ1{gIM>^1=vW7O(n zbjX5)jlXQTdxR?SzSBaqqTs+S0s0-%Oh^U1Vakc?VcZmfoq30g73s=)L+?DmsmLag zO9HUv@=P=XTn_AOs&#zuwm~jW@1q=nVmN-sm_t&Vo3&`mPT@#P@k_I-s7EQ?cU#7c z7d-3)mgjPe{_`P0>EsakmR9yB)}7u)3B_)YqJWqqattar(j2z?9#sT=;2(>e79!jJcKb(H6_q0Pkvd+90M5UdMBcvhWm`?b$S z#G$SY88&D*BW%|6Qp+=WV7y}M(XHQe75zcB2?`l;eHcVn&)rhH{!+#Zf5Pq%kz59W zAPPGu97pBfk3NQ$cOu(UHo`QL;Wv*wXToDj$LtKtF}yG{l~nyEjv{Z|Lrd&K-w^CJ9EO!QfBay-DMGJG3N#0`0%);De_ z#USHpt))M>M7ju!CSN4OkKwSZw-uPb^uVuExP@!_9`}iMqAjO?I)4zk!t%x;SOJ9G zJbmDg3bj1Gnsf#EUNC*ZNm+a=6J78tN@N{a#e4xx$$gz@Z@ioF+MH|p%)a=t>Z~Tw zW7i)!kggdT5tE4gfINAec9`Y!R4QvV5zj@uHyKn=tzWL;l=FUT()94gC}hM8#CLk$ zhUqWT=zFt!?vCE`Lg6X;qlhp{<7#vt$VOIvkZXZ`n-_8{ssV^yiZX}Xwrl=eo{C2i~wyzs&6UmCSw~m7epg9gGCdQMSu;ry= z8-rK8)Ia2MwsGrj{T6pB)Nq8>M!jO`$?MhMaj?nu=1`p1ao)$g)_z|>M<6$l`&;5AJ7D>Ndr(s}wc*hM`Dm+`+Xg_PZ&{q~c&%WmOf@susi)n^O;R(dvIw7ui8 z0lS;Ft$2Me2h?F^rn2aATd3i@5nLf91YZ$HpMxO^cgzv(f&d8oC~Z@C-;ckec$s{#b*u7$-)}$T}Pxn98ZFej# zr=e9M_US2Hor?;1dI%w2Gw~<(7I)F`?h|I(aCQ5+&^y#*dAxCl`}6fOyQ5O0K6&_~ z%d-dNbVC!NMZlBlW521KwT0U7CM@)?0@Q)|4wu#~6Kem_&keEW*a3+ihnOMZxJurf zN(9R$@0d}_o)HGsGbWghudE^Hx!Sq>p;rq;DnSk6sI7VF8WF#=y$niuMmg^noQ3bJ zKTK{EO)sx7%zon6vQ@6^sBI7it&1^M5(U(#(Ya3lz?HXk>NwTPP*=}buEdZ-#_w~L zHn<#n9DGIbKzuvu2d73v6t3)0W2ipUPxhh4uO1phxwDYaRRv8TX+U%jU3#LyGQ890 zmx_(bdg<(?&dM|eab;nL3UqKk9}uM}_yusmG0m1_8?O&qYybeZahVRR5xqKzI7etj zzD5r&|BEjc%GHQ{;$3~=XCOW(L*puO;U8a6<%Ewd2ti4=MBE2&$Tzc{li=z56wDsa zH`FpyZ+NzU`Oyv5XgHxxt<+4fa7b^ZppR&Et;7Ym290L5hSrm2T0GV7(nO=C}? zs+D8yGpGTp>Nh?U7&_dawFky1Oqr42082DKv}7_<#*Z_Y+lc30qgc2WT56-ot%3xX zv~~Wj=?8c|)U1;hY;SEFhp%0o02FD_D8@C9SH&zv6b4dHtom8_M3`&#vnfHlB>1?) z_<==fs~M0-)?QMp=$x!1rKosfPLDZ91aeF-b3iekJ)gN!kc15N62hjni*>DX0$x1y zz+1ARK`ZGo%0h8~!g3ED*)s|MgqLLktz$pRl7jQh0KZ#Fq1|x>hs(M4QiLE0I$v9V z^B#FXNtq(I9C+7M;uRx5@4g{F#J?d8CRKG~s`dTdlbVSquS0y>$pwBI5+u`a979Qr zi&v~)!oei~_iH3RD(j!zFU^qUtN^mK$fVkwTF5cGCK011V(1&)p*|Aj-20X>5N~L}%>$&g)>f;sY_@ zQ?!K(tt@}Ds2?l0aAat}+?2Soye@{3_MIZmrs!&fj8Ygyqg>7)q1+~WUnbi}#{xE5 z)IE8Em(U62Y1})Gqkqzu*`?Ks{sZWJ?J+*a2fi$$O%R3PqD+!c3pHQ&B^Z^~{*?j< zHKtRvP1C`?k8cXu6EFObP=i%e6LkpQ41LyTzKrdvo&HDfX(47yOz**i?Uo&X8i;BB zm5fRrqRB>Z5;VTRL+*B@V==O67geGSh{ zWCfTAPJh{oJBvKHMONjAxFYKA5ZJOzB8^~gHs2LjyqyBjnqrO5JO|1h_;hc?y4Ryb z-CV4#JTK)Rz|gg+2A$n1D}ax7BL)~3RnxFo{roxcio7?TiF5BMN6gJT9kcW7H8#W` z+F%e;dKGmM3Ym>$kVxt4JcKv|cN?iaO;&3*?l4$J!lv+zmFul8`QoBnVV2K| z^sB;7;<$6sfQCHDt1=9&rAj~;pAsC@KoN^o=fSMMVS)#U=qErNK=;fl^ahuyifF_I znFMJuOvtRx+}=lclPZKE4aDaF^gI%76#XvpE&Z%^aT5;LQ+#VsuY|*0OqMCn2Z1Fo ziUpC5^wfl#OYD#Hk`c-BrWkNEFArz)8jNRRZbex0WE2i2iofOvmx7^AD{r!}XU)%z za$i1dKGd`5WueGFd=x)$Ka&;h5^R~o*H;dihQN1qtE|ILhjD(UZigZ&tAMMF*eLc6 znNLYQ=6@+ZUNV|+&gHo0yB1TLyDiLNH(-uL&x-jQ4K(La&-`=c{fiez@^375+C>)* z49sTQZu#C;&{)ZM3qTl-%jFb(f5ye-3|RTpZwQ?6rH43XK)lwVhCmuIrae~usYGI6y&#>H`ddk>SYM262wPNQsv;S-1< z@A<}U1Ry>ALL0yL%}-rF;$8o)KGyTS_$W;L*x!JSGJsD*KJUK~ILs*MVeux7(CXXk z++G4t5HT^zmspflYh*I3Gh-#I6=yn*nAf1#6(f|4LtI%9rVBmO%X%dTyS-}t4g|v3 zXcMc#zyQd28L%JgthWaW?FpX`z8^ckqQUDu?Lc0C1 zQhz;c?jXK9?mNZ_xo!A1tDs4&7fuyVd%4#qZ)MiEc)~R46tOteoHeZbxT9xw8JMa5 z3ME?-XhqPDhwl4j2%n^MGx-FCRy+hU)eA9EonCUkH6gW!b@6XXxbbaux}9*o>G{WA zijUCQd%N23`^h2kjQuJ;Hk@>Lf+kVg!3C~mf*Fd?02nk7M1rrXZK%w&uETN5$#_HH z@$V=xY?cdJK|M9rRBk)N*#0?Q$;2;FI89$Lku@50=Qeh(IB2>XQlcH1>sP^i0lW{Y zF`z^LwRu39@R{1Yui=FzO;I@-e+XP@818*!#PX11zI@o91(j54=JG$EAM}MbnlTtf zUyOTpq?ief)d)5l#CGeDHEq_LK+PhmACxjz+Q57Az793#U?sj-mMzJB0M0K&xk~|X zEhet_;kq$;ps`Mrfg4}?Tf1Mm{ijf#Uc{pSA@_8?B}ER5wm)=aP37KRLj1nOjI>oY z=VO7*O8=?d7Hj!1#_DrJYL$>wpHEQ}kx~Svdh|EC0G&yYGuL_zZOHr*il1k6!gcNA z(MxyAP}>=!48Jef_~=!+^~V?ZL~hkY{vHQA2fi&_zDQ^YOK66rTfhXLEdZHVYN4g| zR=8;qV;mvBh#Q`kO2F5=8{J)T%YWq{_ObbHvm&aZae`wy8#SohM%k%!KNMp$F>)xf zCBt(cqpWb96Y`EI6F80oAcv!nuAys84Nlu*?Iufe#M}v}x*W!i?W3O8-Iq_cHqsW8 zabOgbe7$&lfY=nX$M$GsG*|TCN)NXm&I+5m$yB^Ps!4V{T1k|c{|?qM6*P<z?RAT0%Yv2yE*_dF|KYrIE^to-;=kYJQNfLl4 zsWEJ3E{i>lFx`BgSpn3tZ__)Qtm>wQpR%X|$ zA&ZH83s)P;X5)*TaB4K7l-~>1KN7Z$JIHJmzB|(PWglTl51|vFp|r9wV)>Is0{pDA zqA5XB7o~fdS}o843)YcACC?v59(UgWzw=mF`ds3Zgq|?+3K`f~i)fT-+id#9Q;#IH z-wsaK)O9XO*hf*iw6f!h8SDuu#q-r1jJQ?;tvk98FZa0YmiQNrP@NSOwK*XIjr$vv zt(bE6vsn`?B;dkvRbS*gx)}@C7RWtf$uRo`!AG;mXy|yZw_dT$Qj2BCyJ-yC2$m9$ zHbM#3W>nxtpafTpBv>{fhQ&z6U$trjEz~O1qbCJ715A_liWIoCQ8*b^*g1_FuBNV= z#Y#E};3b}h&+aOyGP?i=bxLoFKvdhT(PyF1(yB=<)Xn?zijq|Nk>Ff8RnejCDacTj z{Zh^%BU5^)rw3gLYXQwa?NoKj0rK5vEw~Z!x_fZ#n%VVjlthiwEF|mMD;DQ@5#;KGsim{wA zj_feg+UZW^1)8GFmTS+%GUX+fV@Anf(g)2zi4vpD(JO+=5g@6_8z#mb@lTYcr4bA5 zd}w@)yt7DyI0!av=RPbkvMyK6kEcQisn;8`VSriAFd1hGYg+|Qd~#!yk-uDsmM=gi z*r)PVR~;QiaiXulPpjv=uD;+>(5L5Ob%K0KT&TYp&iFT@cHDAG+~LUhkMdJ$gC*yq zdI+Q~^Cd#x+CeQ0HH@9s9G*e=f$7Z8MzL0)w;>trjHoi1{{j;@Q?Io+#f=6Gr7qVi;P7o|-qQZWe zSPKOPBl#^^bSA^BT~1|ITHyXZhd23^m04K<1%sg&!AdT$d9ekztk7*u8KumFr@`Ny3Olv9bXeiF?JFDxAxO-rcb~oo;%t2M!mn1@r z6gAE{gSTLrG0vN4A+Y85LdY7pTd~552sbfZeVNJyk1)wDdS88QcjM*#=}{g09AF+* zrI$!tt*a&DrLPERjtgzhR@G&8|FUU91Ma)2b;oQ>hq9CpR0!N*nf7|AC`r~k-f9m&jeK=!oe*mfa*W>-Vh@7nzh?4TuH^YdCa%(oxC`Dxm`v- zH9H@AaASo~=Pcv|aYwwM|5xAD+p(boU{15ww-a7rR|edk)6Q9g*Rc_g_hoF0CpL@X za`3ayK@N>?As{Unf9ook{pO{rxXoj?kO4>Ezq|)MxLa+wMS#yLTcfhge(TyOuTk|i zNx-HaOGIu_r>#mXrq3Q9yWDU@czTqi;YMn#wYq zIIqVE$Bx|+FaR{%88=8`G8YeC)$0hz9a>#=zji}t=uCTmRv1L>GQ58HBlM6@d z@5-ZPl3Yg&<=Afp2K$&8xQye1V>GMmE_eDti9-GCo&>eK{v~ySMN8zeMBC-=E zdod{r_4Yi<2`0e+c_$InaAVOW^rW+5K~KsJA+svSzmV8X1Gk^GltoVq#%=2iNh#rn z*`LR?%8p$wul!}_vpxv6|Hf%6zaklUjKT*7zh``6Qz>NllXj*(x)vJ92v(z5HVLZ; zI^eWiXb&~NfdUc)!J9E7nU-~pl?)AA%z4CywU0x9b&)v)1y3 zQLun*w{i&~#Vu_WT$YEJYPV9(81!00KCt?hMMhSD+^3}whpd*gEXD}v zd-xd7>;SMm6{{oU(rEECB%Kj|yqy8k;NLkx(__RD^RAoL28_He#KNp~qSurgU^(<5 zu{>-3glim7>+O<)o#125r^{Jtsz1Pir?vi|&@ZCfsuOL&o$(I z@mf~(x{OQF9znhL=VChfB5Bx=DKeCWFheDJ1ILrAizO=0!>Xc}MZaQt+13|!T}o8Z z6%^5k~F~Q5mRVNj{6_)K#>HCmtehY<1F8k963@JfM>LA;){fIUZSV*`jn>;SProAp5D=WR*FkpI_%%iI} zInUtik-jDyo|snfj^2-x&r4PB4RVAg+MVVYtXk|%;sbo`3Ps5ll_7R#if(x;T)nHIaJ9dX?3UZaZ6 zxrH+=F-fyK?!~^6jx;c&sCoX-aK5`d5XEOH?)sbBcg_pvZ}U8zNPAp2?6KS%^)sOOPfn)Yflzi5hFH?NJgSFiKZox!AK$< zm`d=A@HijuSVGvT6Xc@}rI|jc6V(pa$!!)8_hr~__lGWux;mnLU#lJGi2yop6Gmx( zB!E(!d%F zMd9nJ`*wMJ?CR=w6{ji%;(wcMd4`zW8m+GiDAMKQ=2`A_W122t)uu&|jEE$j-uXqH z&rQh*r<=;Gy+jiR;i;~xw-z2u5;_YsWI7&mphZ%SZuv9Ww`iVYa00!IR%Bg0coh&{ zEJkPb=@i>Fr?8k$0!d_Ku6f-#!Xu%gkUK>=4^Qy~A>tno$I>Cs&vtw8Y%8U!=ix>x zrow@1@ivl_nzkyuy(gc1jg~y9eF+lN>wT?u3q6~Qv!v7Xu|`b$Jp)BSVR^F^cTY!j z=+FKz__tx;BWKt%25wS>x7rL#>-I%zcG!1vV@<=>7B-(^V@ONkZ<)fk62tMijzepO zE-N=1n0o9|t|giVUI|Sg885FNj>|yT(EM~c4R$0M^Z5gb|2Tt;VN*P2+Yi!65kaI- zOo$Z{F$$$ZVNrA5*0h!uhV=MMaVHU?odqG%stz^X5@(np&()6r^?x}J z&j?NgdXqyCK%-q$#I=Am0}vzo?q0p1pc}++)=IUOn@lyj@_njK_ttd+aJju_xW3D6 zc<%}XABSi&9$e6Xz5;edj%!QC0?bVHm?UsBhFq^|Rs#vysiV}7SEE_1wU?MdV zp8hZGN(vi_Zq}<7` z^SISZiIj5-SO|qGDE_~^ea~yQO8sw(t(+VsI1eKcq)c~7AKJGyF1ek~JT5kapu{a+ z-)gJv`idGEZ05}o6AyZ~8bz50gn!VrmQyd&yEm)4E%t^{J!h7$d1JFt9X|iSVjKY1 zqc{^;P*v(e0JBGx1+e{*iQ{PctHcuCY}IkxoaBp;L=_{zRHwNZT7ZY2l}Cms`;RJZ z^~R4%vUhs4*>K)OiaT3Xl$hCnZ&U;qM+0R9kykmTtsrrU&JhZ6Xz}x-d z+xhwfi|A3|L!r8+swZbH12n9UR%6lfW7L97MW__Z+|8>vysbpLU~!z?=NU?%-2 z_xdg^4U?c4brWMpO4mM;0<# z6M7wZTn^Z`@OiF2ArUQDP~_m$y{T=r7Y`hAEix95d- zwLp?gP;xuv-Ibcopq)$mx^;pqgE=;7w9{>bb4lP9HZLoAoZS&iL~~)p64UZrBCJR! zB8NFAKsK|3Hmq3fnsmzCv}s22SM(u**P%=H&F=i;jZ4^72Sm#a+2B)y0=jE`D5TfK<3}R|TT%KTFDT9(ggP5HL7)r>@I<5i@8q$RE#qyqJbrumxfcYi9 z*y~z|S!jrrjuZ+^sLtl=#FV0Kls?tPw=@7F`M>O$*L-TjGD%;?+pdXtgL8 zSfUpRpp=0t@dS6HPDV_gmzZD~H!y03IGfanYuvPEw-O;ar_|~py(Z^b2?77Zq>U3V z${V@MV~liV2oFgKvLinD-1YJ-QXK4ahhqthqJsd(lJ4M$c^{R>^F<8M$k+c>rk7(` zlCNAVz341y4lWw3&4X}@{>K;p20E;%M-b)9(HH8qMEg6{V6SB=r0;!p9CgVMCbr~% zIXnol@;Vis03gr;poc*X?_FExTn%mpNhM5YQz{{?W?|*Y)2Q;q%ghypo}tOe;ZQ;_ zN_-cj!PVjVx;NG3pMYs?UpX4QpNWA(ZgE%n-afmnG~W=|DVn{_E4RFq;bpazn@4W3 zY(Tklf_Y>r26J2_SMc%yeI~WX5)vgfm0fl}v!{u6Ak>ZGku39f{BjI$IJ(7WdLk_0 zaR2G=9||2sOUkib6iMwrc1h4w*zIWzWb5C;>LyxB5X((S0EQq@J}X&(6xlwV8vo##y!W#$&7hoNKns(B%f14z)n|-OzvsgWU0$7&fd#4!J2MZ%> zm9|!ZF!r8G!<3@oxM2^JI_JkI4QhN~MuyS9Rd6q}1Uff}h zs)QKuJ`sGP-Df!j70>w|vHMwXE%d7J zlnATo+En^Z@$fN)483!H*n$>P#@meV?Q9kV8T9DLy zw7I&1{Q={`!$8;K=*iyOaupzvf0jC4Xr8C>G=o#cR&f&Atob(5c@1#B3}&UiQLBnc zCoqm8 z);~t7E5*9`L(&w{XuG7dBH8@Un>&2k(%66F3-LxuMW2Y!sn^<&x=8$eh=#m9B!^1; z9veQUh7^Uplk+KD4-jI9C#2jc#gggSNMbN53E*wG%zE7Vby?UUr}=Fgdj`8Thl2Y| za{`nnR|E$*3k%Z*YO<9a?7NyGn@wZ_TgP~>VgEwu_UmxERC95(t02R)w7Aaw+-OrT zDbS!SRrF_!_*Pegb(e)Dv^ay3)sHCCGtkxs=l`>=7IFAg7@mW+j`e75hqz!pTidh(k?v#_*jApZo|C zfKAVMq=qDkbqsC-uBE%a%|yVrO;)S)&cdNhJIv#QOb_j8?`of1b_Zh_FMmhFAtpc{ z72t7-4j{mg#Qc@t17*_>QHlpha<4TUR&XLPg#erGzqnLacShjyj;;uhQI2WMIEP_n zuuYfJv0104=kX%a&2*|jM4!O)eLYsUtF*qZk9|zX^?tu2v-^g&f+&ld*PfdAj2zX; zVq^Rg1ZoQO;Qp2hr^xoCbaRIV+28OiZrQQHwq#fG^h|&<91cof4223&)_v}h(A2!{ z)qDsv!BP}6m_WP%GVbIcg2Q5h)>W2gAS#B_H;9c3H8rN4@+P_D>sSOaP@t-7P+l7i zlN?hVxfPrc7sVaId~h5<=4=9 zQ?sLW6PDw9r}~kVrt%-=B$gy5_m+(Z!P)9W)iy}^7D`9JC9%#hDe{QOYJ;pp`oRC5 z#)(L5A>_({nH!U`)hqk#Ksyu~Et)Qvpdek<6@G;t)SwIF2{Kv5>DV7pgYZcNF1E=> zIy4y(J2*~QB^zhK1m}&h;O|^APs>q#N$Mg%eg!#NB`d`oIRFP^a`Ps59ei&bg^ZIf zFfL31adLfWJjtKxkFm{6f%vcQtnL|W`c(HujGD0O;Y0v>k1=6gRztcz}67kp7*i^oy^MGH#J zxRp|Tp~Np%tjI@tIiX@&$Z2rYDu<(kq53~ z63J`}a|S+feNkObT2|0RA(>U+J2~h`TFt_%YyExkVp~i5%k!%MwB01q(wXd(1E)Ux zFZB-N`DXhqUVZHk+xea4h%I>7n3bLD-eD>X$b}(Ye`kjOOnz<0ac$prwF}XR39G(< z>*-)NYPfwXE8I{%oQOx!&A2`K*vLP#>YQ=8;Hj{T#^^91n1a0g z(J^mr;VY!(eFA1IcGDt8lH;DliAhT<``WibMBP=-icDY1Hsu2ZH>tWH4kCxF-YP&z zvMa|`vQ~i7EIHwoz>loq+hG@L7X-HqDKM`&J6%8ZD!_+c@?a%T?HWM6&L{DUtd(@6 zj8bNebyTOIJ4}4)9`bqDX74uJwgT?6a>&*OF>t<$FLeYMg$xbC6a{IPE)IKSXDM(t z@;8}(zuB~AEvAeq5E}ALPv>|**nc*~*)x>3gw-~`o zJvu@;BTP_{F;}l!-6M@fkc}ps2%D-mlrdVvk>a2Pl)N{e?$vhF(ZWnalfnFTkBiCi zdHRU=;kh1&$Y9x4xRywAmJ6(t??K(+L{mHaq;u=+c7+}g3r3?#& z+NzM%usjKa1a*p{sE6U+#UU?(H9Zr7@TT=bt!D)`1nZ|*yqF|{V%xX6sEt~nl1U`V z#RWp|v;E|lKn|>fxBQjIzr3C*-I-gbDQgnA{M;Ha*;W#;*O4SR$rYTQt@8$Zw2Pfp zZDgh7UvU6Dss(I@jToe!6`&q6F6^dCG(CAAQa7A!-Jy>@>AU5?XT__(Y{bbX${{te z)OV7nICwtdo|j@IWiLkx7xi&5&Dcxwp3AJVw1}_!Ww#5~0j0np!5+@=mgjYii$iUP z>)oK3vxRXwSxl@6OS6^D2xj5kHHwE(xpAx7VG#CMSRtkxy8uCTmuI(fk$i~G1mH6M zgkW~cKmHPNP-HoWEBBrl!0y=Vwl;DW-tqgE+7mT_HgDl+|fCkWe>F7Gsx1&`~N&vEN%> z8aAd1FxE2`glmAoadKFWS9V}q85+RKgRk{ScII}MiVfyj!E84XnGOEDMXdaEqj@#! z1y2YLx;R`jE+Df?+ypTt2tFF*FV=<3lqitbqJg;03&UoGw33X(y0H0aYp$$Ik;@6` zIRL3Yt^)uy^*KPDU5#1#2FM+fYz!C>Ta^vTf~+x&Z!t(2o%x zZt6av_qejQbs)j#EKQo$gx*c5Y@1rFvPB^%hP~MjswF3?BavV$VapLn5_3FK#f~At ziKToP{)8jj=EDInmTyR7aMki$PEd-OM4x?lYMtKgV^PF>9$M5Ll8>~q@ucqCdiSgh zJ(U~(%8+fe7)IWBr!DI7^-f5$w=%teoH<= z|7lqalGiU+r||!|i(UQA>vf&fxg@Kty|$$|8HFCwk@3o<+!Q!U6gCLEY2A85Oh8Wf z$64gQ@dDN1d;NF=L*LxWCh2xGofP1wv%8i^#zA~Au`QZ8lwx7*lDl)Jp3qOPdc){d zgD9R{@f2t(60G9lX_r5*atWIHgY6VC8Rnz_^Hrg8d*74iUEx`ioFaHMh%GDlo{!Fg z;M0Qt)GU_+vBpGLp%;~L7Idb?5u$DqG5i&72xSBG&8E`-x?0#1&(pX1P$Uz0a+06U zNG+)OUDfx=%e0CqTF^V#2o2*j&Yg%`6giBKn$R0u1oKufcC>e0om7D_F{QC`WZ_Qs8YFK7w|a&@5uMPlEROP*oo zLwHm-kFqRNvo!4_nG%DfqCFhpC~8$$Fg~G#BeGcHgdL(Lx%wRpbv%xReDJ8Kj96i^ zP?}*$+JAh0jyBoBsIT*V<0+hyky$y{=Q1CU9L$V;%-?3=&bKuk=)zzb3HS z!JS7J?Xi)`Og--L10)vF0tlG3#)I-dM(bxRt zwEadUzGTl3hx}_@cL=t6e5C~(WFuE|UzAw0MwwGsvGaz7XA$q*Ts{_Ow51-#@V;2l zcxB73@w6>lEcB=uLl{(I@AbOns1*_GXp510_?8R_V`(dbTJ9z`i*M|xOoGJ4(ysm#y zFCJQ8>q!q6pv#d<;iI%x&vqf`d-sztxT!iyNi6{oW6+Z7>G0332xxf;Ji1OEW1$k> zIgp8EEQ-V1D~bd8+L_$OCR*6=bZv%xOXIG^D#|aLOwC^3aB3Uns;BEy90ZcGqjha^!Kq!wBDbwpOf; zzaG6?;N`-9DuW!xHAq&~19U_`D0dEV9^07r%%Wu~v6X$fEh3jqhaP+B9T0plgbJ^R z&76!a^{kDUpcd4H_Yw3@SHOiNhh^3T(|&6>=*)kPkRz&)-R`ZAm>I0%(W#Z@Ox9U| zq~M)S9XRY{h?oJatI~?qg}2RwCL!RN5EQX8K^%0%Bb2UlzpEJ_X1lgb6)?F{%+A|I z4o1)RunpmKTw803+5L+ak{N;OjNbnI+$zqg!Z;RhFk4QF@6)Ix=|83^}>Oyz=Hhl;IXMiM*Q=5onE@8ywDNUqZtNh36Jsi+b= z`9cIMGq((!wK+c;#CA=KwNu+6`fvOK(lsW=!}Oyod-Z--_Mr4-gCZZ%=1-#+hWyOr zSIvK_H2=PU9)u=-PeIFns}`sFGEjqsL*GFaHg`7OXtU&5E&vrLV7Dszt0(xN5Ta!r zn7I|tFQ!SRh9vtMahpJ||A_j7P}s}UIu5%;Q^6_fXEv>OuBujArXv?eya9vjWHaOm zGw{V71TUHxL$l{sq7teS^+nk4QqN7*B90g6;$jSaybGy5=5bgKMY-T={gj^^!pJ^9 z;b_4tsgSX-BoN!I$lN~8W)rZcRcb{OBNvb;Md4VA-L~LD2!oFi$eC%^6RIZ6vMHv> z&QwYy{j2SSNsh^D%HPg3pdzGYaratj^TEh^12O>+FDCCKtdt2^|Ehd z!dz-apq#_WDHb`{+qjugShp?=vbO+@3-KFnThdwT1JMNj8U*@30CGT$zrZ$*A>Xb< z@^LxLa*mdWwjx9Jh6|qU+KT{$W0GWOMqTTzx^B6K?sI3tW zy>a2V$)7!!c`sk_yYJpj8oPp6IpvyObEGCGMw_e;lC&SBG`*la2T@nkK`L<)$U_H^ zG_$}`v~(4koCUk_e%=zs#w&qET98iWI1;9t6Gp^{{(nF(ObaE{L!yWU?iv&wcyGOAOl?NzwMX58plx!T9nKUmu``vdwY&*CB>ZzrwckXoa z7?_~WiKS}i>a}%SFQTwq%nL2ZQM$IrF)pJ!DK?R3p`4MGNM)1Dks(-%V*04h*91&!ut*^Q`L~FqhX-> zz4F8K>>QhOdtldsQOOJ%6NGPtX#2>URZDejy++q(rR`arUf3W|`tWvNf8SyElI(TF<9a5Jx73?V6*&aiU(WI$K4oYmTK~IMe8%j|VY@ky@4pAXXd{G4jQhvw}!W#i)4> z$K)birwsfAvRS#M8tQ#Ruu_BTn7}`#@a#79nDexB zP<*plmF?uCnoTJ7^Rtz$!>6*0RysY9>CZf3`sV#h(20LVN58HrE3*ku@p4PdqrNo8h@`+SjTeY>KBGJsCXU9`jJ|LFav6CukGtn_@HhMUaxvW)Mg zDXbV_uPTu8F&;}&-?H?|ljrAJ05ht^&_rYn`i_cY8`k%I`k3hcEwzCR)B>Yh%N}R< zNeq#dHLftpy&&cqcKh|>Pfh@FYx zk%>r*Z-+th(XEP_E!H(^YF5-;m} zRkI&g*Zc^(ib31pv5!45Pjac;%xh2@w}eQ&wOiE2rG<_A#_MZRx|#;oT95?S0(5qCiEY^7`AW zT~OvdH2LD3Rq9V7N1_bNG9?j$3Nb87(%|h6Iw2y`hOXXQk=Y?|MJe~u^vd9riP5G{ zin!PYbzZPbGG@7<(L9-GwI!U9RYT;ZMcpoBExZmcl}}DyDf;jPFk`n*X)pqBZ3@vs zL2WBQzgBIgb1whJhV}|-QxQ^*`b5AjL~R+Ol*-6{MvNFXwcJpQ3XiP|j@4k6F`tBa z(Dl>aGS3F<7LvObQ?()j9iu4}c*0CvGgAPWp7&i-Lq9%q4}bsU{@3OO{9-#m~u8gy-w&j6Hl^id+ zKJfd?-9d$Nmi!(Lj3}ZorrcWb8}05*f39iuFon5B^)Kin^7)2s;rzpQK4=dJ%m5}@ z4+6f>Hfkb;#O_t0d)SKwXV=F@K1WT-g+ntZ+=7iDghr$Q>Jy_<3s~b+uGbbUrs+*4 zM#H4gy&xZ|8D5mT=--O<(qj#iap1fY;c`dEj>t<__O1&H?>(6uBH~1%wAyOisvA1- zA%@sOp4|(x$WJt`+R$4>(i321>(vEYt1M)B?E7i=ZkYC$jU)yRy%Tt`ksTr03kWcA962IC>cit*v`)B0ZW)$CXEO2wCiVz>qyc(JHHvJ}vdVl3Biu%$^3Re`V?jZr}0klh(iU(6Q zI899npw*^NDzXs&0d)BsUeDpXH~WN20bpBpd*gscgmo{92iI4-b$n{^{WNp({nRs< z0%dmh#$8c7NBT0)>s2FPnsdeYk?XhmbG+cPY$z$@Vg!qN=*59@sACYsSr{eI6NLD! zIwj)T04`))PtBVF>P>muIFY^DJvj&FKSSG;pi>sa807FQgRk`SLC=qkMI^QaaY94} zTsggD*+{kViJ~WkloRq|nTcbH@q2o<+tg5A)G@s56W2FN~Pbpf9a+3bG?gaTP`BYn#f+1?T{xItN9}*YW-KP+)Ypu ztBADx9+C%X(czs~I?o(Ki7FP?E2a`wtu~l5JbnC>lKP?IVZRb^UKGD9NAXM}iP38( zW|z{fDCd|uDDi5#$~~%YQtsGNI|@t9J_1PxIdZCpDb@Jyum7E&IQbWul6~W)m+pQr z)N@oNh)Do@;OwR1AIDGwI4lenBq3Ecsfg`^)Zoq(g}_&CE4%aUO1Z?+zSHp_9Ffy- z$vtlM`GF!aIz^;)hUr3)*~tVELD!*J-wngmR@8#ZhfEMsMBK+n6_cpzhXG9Q;JwMAy2ifMkX4ss%W&PzByzNGz?i_mBR?8i#CItgPhTS>cV`T zF1hE&!JU;shIOPi8+JC|bSuXfD~&IF=0xw$zI_v{MQ*cMM&T44i}6x559TYJ)lA$@ zjaO(a9o`tnUr2gBdQ3<%r$88E^OW*N`Sa11N70{IH1f0N$bbg%QBpoNV&r12|2EUr z--aY<_uu)_$$yxy-1>_8ijqhWlK}Rh;+LY^>Z-RE_3@0h#woazYj;s~N0emD0$}Qd zx(X{=nfW+dkveS;s$|0_t5VGbWE-4gUUDE%8-hccE1bw?lPPxj*7x3C9lU+LV;Zv$ zg4|&g2lxFrBlQfba~V@-r4cy;*KVn4^q;>_;m2DhQRoc=IG}07wh2%x0ktD4h7pDQ8DeC?}hrKi-r%d zck!MRnTV9uvXOvZ?xnr&eH6LIv_3HLnmm2>A_YQ3)=9+%f+7|;{Pe?BM39=Rj;Af@ zu}L5ra=tQ%94)T-u}0_SJa)=)&MjHtH2~QgANWSkPb#FbU$|L5p8kW7v(iL<+($Il z0s7YtO7al%)?CAX>}-n&GWuG_AAIMHJNdwms|DSkM~|MK>ppdHzK%YzI)1PHy?0ls z9=4Nyq#(mo%)EboF3)RD(K3ulQK>6`7hg zbF(97lkd&8#e*CoiW3e$oC&r3fV=69wcPU}ukXjbIL=5A7b8Zp3{#w0M(nPGhhI7R zxjH>RLr9>s)UB%QqihcRCW0BG5;W`X%8x(SP*2R;4QzvrI31bV0U$V6>ET84cx#f6 z34({=RIN8}MkbxeXljy5_CNYuOLJ{4$|sPnxw!9&vf9t8|KA%vZPB5CQOHPezWE`d zcf_Q9KkNNpuX-xRu~^e;cTl2G9$B)(&puYS!eV~ba5}TH(h1`a+j_3*wlz+oqiUa_ zWYM`wTT=v!qnxarh)~*o^0d?ZvFGO#XX%j@MKc2ohM7D7y!g^ZnzJ?t(mo-n+hDyLGqUqjNp6i1BNN7VZRIbZc8~ z*8S9pOs%_019dN*7@?d`)DLBh?QQ4A#y&RJX0Ft0R_F2ai{`0g_2W-oSkxh}*73tE z!}it<-W~s2uP^(5{H$%h~KtLW;jsPomo>}#uoyrxe`9S$ckqrRB^><=ua&5B{Cf@D%YeE8(ekG!Ch zQX?4A>-m=GE5(lJn1gV@h#Wd!HdYtRP@lXN*3_(ZzGXl18LqX?bcjHz@O?9%^u_7EUA_#r)T&OpXJ0KC(&>^Lyc608n zcIU=YBd8Mre)LKeI)aMvsMGQ91l3GoqZ0!-u{>EvUvR$u#@+s-r|K>r(OQOsRDbEO z9y7o7PH)f|WL1C_&-u9CWdUN>SCYo9^&~pc(!+(C7T5-E9AduP?bN>8%>>=wm;zn| zj$jxn&plS_|JZYL6%baGa-$@oFZ58wuUrFf&}dYP=!=c>z0*f49cq5ov#|rDjA4|k z(Yu2dwQECM%GAwc=WV^YG=-R>oZT;#_%bC)U{!38ipUr>FkOs)#ut9bA6;B@kFBiu zccjOZ5!eG|mrGu7?1ldv4&v={h-FshC_+t)QQ>Y`yd@^8{L0(SW_MS-z%;cD z5(_adkIh-`Z~cukG=?oII)S#|X!?Co*HV~ueKoCq>-}i)Pp^ja-@X>j-|giU{7l!> zDUlZfRzx|u1jxlwHg6%_#slH|2XDbiEIH2;L0S;v?xpac(%DmKg?1mS+_a z=2)5&b-x>4HS8vqtEs#siKnV&RchwRr(E;YrGaAD11c6xp~jnGtTcK-=BIm2DdJuZ z{KzQCKZa6stpJ(Ya{gBVSh#)3(n2MtiWsD?RFHPeabawFCbFFIGp#IiOr=JV*Z6b7 z@7tKp%|$$VEkHi0Ai>*I?oUuC0ZiK7o?Jv>+PmxPJ$TID9UWz&iuRB_Ht;eL9kW^F z?=@B9`_|>%GCtzKX;c9?q^yASR-Yn@)5mJ97oI)Io;cU);5vj!%J)=L10aM*8TJr; zREw#^lX5pH^)UFxiKlwT$)_u-;nc+U_~ryNLFCXY&3D>vfWyni+Z-|+xus?HS+|!A z!C^E)1AA2MsK;vm$G6%YeS}7)ipVHezVK9|{_8(;3T((MF7+PyG}lJa7<#KVK88^j zpE4&4AW5nl-hcFq=Mqr&D{$ZB5jy#{#|9g!{86`^gYj?Y zWAt-+oj6nDcRkEmRW}qxSW*AWNZ-RSY70&Kk!MtW?lc;@iE@{3qf=5U8=%pklp^YM z-C00v*i(3SH2?sno&^P$rypO?D~{d=P^CmlA+5wGFD&$rEmj+JY|HBbDco3$ZDTSf z6(hR6ROOX?`{5PoCRKc&9*kJbV`GP01fTegV%!cdc9@x_aVe5`h^EO)aCmm5EJv*L z2CvW8D@zb^_&j!xRQpP?Ye%Ao&786YOHb8xm5o!zAr7pT11}2+4vj_4mYeoNOthUg z`-@}?(%jGE+-95UF(R9#t-6y#9?J5e--G{aY#-xp3%x>^I{}LD zNv(<*_l9OKv>XR%#TbHw5o#UAZto*lhG{-PdJ4iOB4wy;Wp)mut}N543jnT54EO{m zmzPblY@548vy<8lcznw2#&D8OmTyfl(LTe~MdhWpRGErn$i2_TU`}6t(*G(3DfkN!?a^!E+gv7qGWpmr6X-E3H^a){?ZF(nK={~B zh;2|LL9mDJN6S>5x|aq0L`frSJV{XAQ8NM(^#h;^V5&ySka;Vw0~ZKq%4c^>^EbkGvpP*w%3)M0BVbv2)t##VKcFK`Avc06gl) z7yrqhW&L%iCff*$@(t}QLF!0Y^>R$ z)N4%N>o!V=H}~X)`MLR4rAoEc<#n(7_1A7u`cUX3jw=Mv^H3xCV`UQMB z8N7ctak@Tlc@bZ@+sPVl--&Y^r+1isrAe&Qgd$q4L-Yz+8Hum8{IYZXd; z{SX{`UQT45X%!fzMw4iFiNF+^z8KHVS8{N5^=bEWdIZKO0z+DZBCs^wcLY|RGo^bf zuHL_KXMo*uYy-SC`uFDYKwVx7qL1zjviEQFoKy@YobD^(q&jeZNE5Nj#H)k!pz5|d z)~Tlztuo(WMbyQ2M}34vs37(H+&@zpD3~m174mf>3j#r{rKZV;9yl>f)y9RCsg|~$ zRP38KSMPrAf?3fKW|CgtFcKqr{-eho`-eZ>B6i{6!hehd}T zDSE+2hMqP5%YW^7_;3I8=?bDc8>z`H;Dtc|%AGaO{~y2BAGFz-7Ve?JCp?BAIY!U6 z?t<-@+c_VPzPPd)I^Oqs^N?RuX2?Uc&1kH)fU2F<&%HL~8ViEQ*vJ+Hs5WX_17s42 z;IreiPb|dMPG~Q$;m#AW5yI5a-1NXc%%PnaUU_HDMQYNaJjS@}71hX)6^zf!X^8e! zD<27OD2dTQd%}&?|DV10fRgMyuf*Q}a;VsOI!w+p7z}_xAOQl5A}PuWD{)OpOIe4a zZON-WI_tGIn9E%sdyjqW_0h4kl5DNCv`DS8EQu6Ji68+IB8WT)gUOgY-7`I%D!=^R z@B6)1)m>d(-P7GO3?Qq{Y0RLfyTS|q{lEL&?_P>vfwfpu1Vyh0@YW!rar(04<++ad zqIFeZ3hdQPBWd~Kgq5leQM@$p&(I@urWT^l)$CpcCiGN@!uNJCa|&Yd>FYOtb^pXD z@FV_bxXah;l);5{42(5ZV6<|A(`O!31j>3djPCjj0TznsIorB5?@&jFeDeso7ED!eVSz!i+A(`?qHN#}4NC zqxTfl{+ya2wPDip>Hx2~ierEF8w=$d!9-rsWQ}*JzG!fH!l=7>#j~PVT2WC#+!9}E zes+%2+$6n4gmbDwVs#}ZyR#L)pD1Q{Af>J_*|>{cSHtC$5TUe3=(UYPfC}cW zsj*CF@AJ+i94jM`IAcBGMi0Q=J|Ol4j_)&A6G8RdB&9Z0Q0qPB-H4E;mCXvjsKna_ z1X;L&P_J)G*_mqPjb%L;zBI$c))*FQ124Fxvt{7b%=BUa=vW)xQ;~~fIokE3-@}Cs zE(!oZG^aH4a=lRAkY|6>fV6)m;T11!=$Y#yVw{DHZ!I!3{ zKKa4HQPb2E+YOTni@=7&D#SyM_G{M9yl+JK@ka;zTP5_@Zh6jZ*)vLw&__k#W2Q+j zSh74^)a)TeU`(V~cvK@H7ufgkF7LMBYqctzY zH~flDeJ_%b*MlW{DBiT=qCOGLf1kWms{PsXRrh8%k(ISfwi|e*{X7h3quv|RZ7o`1 zG)9|=Sh!|q13#AYsu*vM%SyzZpmSx#)W#2q?(~IuxZQ)ule=tHSQdgDJFAOzfYO?k zSo^QumWN1xfM(A)0THwwD?}NVo}9p_T~NYWb7K-l1QpS0R&ICU-eG*X%2SM*)IJkf zPjk={al9Q5^5&*@`D&_*<@-HO?)Jzalob9pWYm{cUTG}5upGP)p=^n@4<73ao;YM0 zl6|Y*oMXN1F=cH}#Fq8YFlVG1*^A{&82EwTd3usLD3lU=GV=lI+IrXa2!R-Uta736^3xHsb+S(?$}tGHye++R?AMd)*Botq~KYpfee?o}s8%pBvZ z*d(pr9wmZHt*AY)uaDoq!>nKqAzXz;Ffq%=!XU2St!-A5zD9-_^c_L$w2!N>bc8_2 z=~A|s5gVw87v-t6{lUL^2oLq3#nJB8z0>)*aOp&?BgNgk3x$G=%JCJHza|&`@rH7hKMDEJE5!-#;!O@}%_c!+BNLkPeK?^+x9&sj1;Hwd(- zvJ#rZVO?{pn%_&KJW{h@>Iad-MjaywufDZlfBj_5zfvzsTpun<41$e#^_~@d?yT%N z<3_FP;If43>xPr_YJrwP0A^Z>B)^Bx6SF4Br9a2lp<@kT8Z)L>%lk5O1u37CR820u zc&<)nysEfzINPeaQ9Va8dwKOrY=c3WWX{3LcFc;IZt_?`+BTqIPCBfX8XP@H&ftO) z4;5r=j-rsk=_qRvrP^ow)gaSHR2Z z0$xIDGP`3$D?NS~`)A~*ph(#=Ytj$E)zSFaP6I(49w zk@I6a22NM2jkBMhZrnRoX*_XHR@=ALvND#Y(&n4MEM8-^Y=KsY`HT6Ab6>gW zgqKT(7$`eT$uunn;NoitF4C$)5VmFDt06^sggP%P+gWOV6C96hvJu7*6{Yqj- zIsvT5c=Z76Z9EvpH#efSf)--k+RU^#qU|7kTN+f~Ch#J+2F&9qP4HUkd8L*+$h*?Y zPu!E!iB=4g2ojdeTq%mGJg~*dJ$Ze4ju*y81iq3+i<9A9&w0=A^)RajMWP&sbce%A zsr>BB1XdJ@+}iP6k~HP?X;u4rrI3}2K}Quvi~5#Kkk3`BWt$IXL}4|`uIbT-evp;R zLED%!U&FNOt4~!MW0}(I!7Y)rZ&Wk(Zp&pe7#HpqfJvIbxr%3>o2g8$n5VEJObP=LszYsz;*@uI*yR-6elYFO||>Bv&?3LNu`A2=#jo<31Q zO17YP7M`d_^EEdtL)c*hNKDQ*ubxc-N@BLd)f?r|**>VMLAZQ8qUHRL*V1XLLi>m? zzWe*O&I%hw1JP{^x_gga5A1vU!8$v;>k!d1-gT8*(q1pua)@wXwcZY6C9NDUR9@8S zH#_S;o?0#05AV$|s=Nri+Chax4lp*#-;rK%9c=ccY~e~pZ?ME3Y596a{S(AsSUZdb9tNSuZaXSO)fhk~ zj+m7(jm*&Kz>K_O%hx8yv;W)Y8~*XLWk<*G@yaR;w4s`r{nE>oxj+1tD?C3vs?D*T zeNtwmC@Y2uY!f+;Z@a$Jo`*meBLIF@_KmQMc!TijvYqFtbe^Y~-aPUj8kh;QN|f_( zoIH55VBAeVR9H|j6X$iyze%V=h+Vdt)!H_zmI>Nq`spA#7q;|kc~sC2$=thAoe`tq z>;QWS|IRpoWF{R#CRw;T7ii^1R9P)gt(CZZw@C-q768|trbIpl?Nc0F=qV9<0QUB) z!PuRunU*~2x`G$I6KyO7)7{2SG(tmpTm$ z$*BsUwJq;u^te{$34q;>@M4QGEHP%(;QK4)3z%jf-DL>Ltno@9N)}Z~izCH|Lu4$n?+$yzT6lw?5qz!5)(+{ z$uY*yYv_^5yo+5+lR9i1&Cel*QRwF!+wcy3U{FsZ z_6w9HvfE@(l(p16`8%|&V~gIBQu{$)^g(UP_psv3ng0tVk}9vB1=S} zks6h;ck?(v~3P655nv&^f=?+(ZON3Nt}Q39OKJ20NzC`?Hmx z`r5^YAtpoZs~a|>H<(A-K<&;ndL$fGU~kB9i3!%5*E}{~4J#Df+{$TdpC#j)_*&~m zX~maG>GdJD;>uc#N$V;MdaGLxz}|*UFt$+u>zlp6f4>UhAG10F_F~smf;;w!k=0`Ad9w8 zgj4v66BVS$V(hy?g*G^BJQ(Yy6HPQba9$4(dmqFxIgXL2gi`LQwaLUbc{^WKzCrZh zPOC(H9)+){zTmAGDE3u5r>1I5l(+Z;^KPCc2kP=!u@u;E*k%lidrj;J#{{SesPgLD zty(sv4p+Le;QTl*oUPG(n|8Y)5;AH=1#IG6SMCJFa1R)wtkG@(^KC-r#H6R4zv)+C zfJ}aeWZaurONoh<)W*L^QJL6*v8HDwOaH&=U&#{HsN>Plc!<(9A*m~MPa<0>-W+Y{ zErvo*#PNf`II{J}nLZ`5xCvdPkf)wjx;Swcun<9^P1GS4 zJ+iapo)nhjus(&%OnBsV5%@@nd#|eu>Oo^nob=q-t@x3Qj|`P1SSJ9Q5i(6l753!f z$5X0rvFAjI%MvobY-d<+Xd&-beMS6Yy+1&Yt3ZRxEn^5v4pmxzTi$0p8I0!)y+iw4 zwLou8T;i{38dwr?idQ>YnAHi{S9o~vm3#kJ$?9kh0FM&v>=d*AtJb4Q1d_UleXEP$o(~&5&*K zdP6Hr&%r3A3PDX4&K#(Km@!+6jhSi`(&tNd0Gp|X7>R#`xWy{cmD6tD(Xva6V;4T1 zWo}}ZG*XbAfsDjc$v*2bh&=#%y9yE09UEUyF_g}Y@C~Fla5TXTk!>=SlHZj;%kEDN zPOL#8kAhHOtKjCEDITB_Upim&emEyPkeL}#n^|i@C1Ow8ulbq#@;B#RTo^_0qiFDy z;-iI1etCWiOj=oKt5wa?LA~Cv8n4W4Q9$u2V(NUDM$%w~nf3}+#i>>>4BH=cslo=c ztpAbV+7+Nc48@+7(FtQYVvt!b9S2}dbJ=E`Jm*b)oJ``1WT+b7QMzpz2qQsf!ss1E zk0_&ZS+-4qS`&sK_@d((z*f_bBp;L@1E|&Okpe+Q$S2HdHWI`YroAGR+*j?QZ%1-Y zmH1*Wh(P|9V`g4o=&u~iFTjkLi@p&u5NElpN78}&m~KE}c_@b#}mkx$77I?;Q?%krfU zBrW%5+aOudHhkXtX4KZ1p)>ED{xOY|mTqq;>;c%@j}hc-<3<;3-D1UL zD=8)!9q17wJotM_G7r0?I(EbPPVGZ7HpNM0$YeQ}0iM^7XEuJr# zlMfnpam8RWN$H?2-!~%cZjO#8a%}oa3}Iv5<_650rB%!MJnisPZ=}f@wo|>liOIa# z{G^{-Dw8_e)dUPwIT>=KT1p^n;r8Mv0`>QJ7uzTYwozB=RDIFP$`{>B641f-&Q|&g zlZ}jR$RVs8k&zOq`St{wBO5;bIb3VV#oan+^oUb*1P<2s`Rg6l5IUd!UUXR~J@It~!%?%>TAQL++qP7kwwS!xY@Y&OK3O_X0n%kj&I|V8cG-7S6A5`Ls zAyrPYMESmwu>W}~naWS=XQ;9}@P4J=_vC%%cesrhhK6|Q~v3<;5w+o3tAKGs0qiP}nk8uZ1 z9jIvKtca!lDG(vDCwWKqStN@n(0r-?le zElrE?ym0(TtWh}UON~O>t5mDC9CFRb+mq8)2-ld2JTOgFdl_Io02AJBX1)U3h!AJ;%Rdvb5~c;+*}hQf|S><@Vh=T<Z3q?4&! zZRDhKphI;iYSa%CS^9*@&{(r4*eRJ$sKdg%+pl@usYASbC`_G~&)20S*?ARKE_p7p z2pd*Qu-Vd)nKl3e-qO=2YOsxDa{KS#b7p`mrHn3XccPYVo<~Hh${C9HBq0TkJN%I) zcXXgY`K5w8Id40mTWtCklCh#7YesD3(alUQ;blEb%DHO{7xTWRD7iSbM`;IhXjaAg z`L|rJ-f)dXTGi?d;9&QfU_AhPJ6cJxjs84aie6yt#=YHc&{izR-N2!{5fhtIhfxEx z+H&eJp^kX;;GPV+O>(gr{FwXT{dxNvC#u!S`5@N~#FnVUF#G1^M(Nm|tmb!7B~o>? zeMlX+UYojc-Lo=?$xvgSc6<_&hcVCE>lcBhI6F`zN&6qLz=SaVtjzVVat(l%g1mPw2BEBC7*z# zil{QF3`6UOh4_2TkeQ!1#Y_fHk)WRJtRcdT7AhDyu?^E&*9lQ^3FF8+E4Aeh5?$=i zm5p7Qs=wfAF2Ky5va>4sXOOvz2rG_RH;E8Yhl4~NW=mGM3Uw%_Qpa`J9FJ#_PfnW4b@y+yAvw01ISuF#pGQX;P_jl8bwW} zBr$Gcde6nF<{b<-mpb!Z(v*Q%$NW4^PUF18x};+Z0V_+eAk1f7ZfqGWW^YC80odDa z4vcLG0FpUUc?FcP)fI_gfr!20ZHY?je@S+C`(7CM1$D?*QioW;whk%vog-?7Xh5kM z{0QNN`QCeTE`iv!xz$08sKk?3ZF6E!s|^*@e9(nTWU?aNKjs!^FW;Pvl!-Aw77mO= z;s7DE8&xZqYwR|we8&*akpCeRYcdHm{3uP{YIpuDa-E~b?C**sKeH- z*3g*yz-|j0euylZ;M0`Z(~ekw;z*7^`D!Jh6Xh-r97*#ZRzajn{LuYHm*#wUr*j6- zD3{SP4-CvZ-<;Lwq|9JZ@axq+Vb0zKgWK^wmNyO6q_aY=8wE@6BbCH7x9QY?ms#5I zK)zMQjsnQAgIm>*-KvCf8BL?Q8!4b7)nv8jC&{kA4?V`sK% zK%p7VW9a)rz35E2867iax|q6JX|sXBDR#tY|au)OcCI&5XS?HSVuyf7BP z^f+Gfx1>tc(q-$xJy!YaZ`LSURO+~HGENXyP?~CYDR7)EhJ^DbSatU*UUhSq29<_V z5`?yV4P}fi>Ug?7XBhN)U_AhP+ZABZMsGaw0F>7&acl`WmQ(ecu=KtQ)tBRQq7HM* z-VA{hWqb80-at+^y^hMM1!5$Df9znk`lXl3^dlPUZZjp^Duvn8lXm&u?HP@n&pV%s zs=Mt&+Ca&knk`3_sRM(qK9~^(R87~Y+M)$D9cf+?STuW;NtH*753G&6nT5cgDh2UW zDP*&?NWo`Aa&J;NvV=mE#6`+(dELv}9Ys=U{i1rEckb6a298_QF<+|8n9%H15vaCn zxS5UtF`@2q{mPsh>oDLfv%8ZYcIWwHxW+;W^G>6O#OZ;tH!s@;27~LxjGXPH5}SEo?8r84K!h+; zk`#%6i_~xJp`a}pwA!q+oFMi=VFnjx{BUw1kUO=K+MMXbascZ~VVWvtb!Yj_;QJ9o;tZ9k37ReSuPqlG#MQ zvHPS4U_AhP+u;}6APpDCaIcV6QSFT0U>g}t{tiFfAolQf^Q+6^n1rNWaEQEeaKg-? z??lq^Cit6z`bzGk1pN{q=iPL5p9)~~IwE-4Sy@Z73HW~`*APPOS`vYHuz6^`BXXvVy z%c+gNLZNPC`Ye+a2(XB%?^+dhYI|I4^byc~`dYHZMYl0W>1l6?Nsk#=3{Lv1B5Y z_52Wf0QUBJunlteGza6lWGpKnu{Moe=WA>gTND=O-<{nZOM4j(8udzN{7L7}(O~EE zXB!z%bd~#dWyteRr6FMYJ^0iK7jL-ETrJA3h&JgW=(slPSr=|N^*y78+BqB6si0|5 ze8l>pf}?a~?<@p?rzTzT`a+}tOf3KwQ)Jezt7uMorO%9A1t1I18Tlau)*-?eqrstU zJT|l?+%`5S4i$}XY$yjQp(tkzW*CZ)g9`@oL}2m9B7y|!<7kOTJ>V||=;Vk=>6eIW8JNK7O$g z;~Zw!vWseg-ApAAOO43x&nY?2Pn95)H~`zVS+lP8c`Od+P&H#}7}XL| z&z6bW+czStDOPCPb7*+M#lByY4_0#$;>6&kw>=LJ+Fcs*$tFBE+z~A_gkW;U<>#)I z{PS1m#p}}x(tM>R+Cc=O5u!v@C)N>`S?NNWfLStW#Uw8ftT(QC!s#jB-PSMHk8IV% zt$ngV^rGJZWZgCnGiASi>87j8lDJMIXRbecvg$)=T7$rZ2Ff8*joddb+5tQhaRk?Q zzOl(L;MH$ycc}_nD>1#r5c4(qV7M|Qoa+^Ru+%N3`)3lvUIo?zu(u^7V59HFvBrv*eEmyx#*ZC zMPS;T!emh3^T38RLl6$<&1m=5vGCx|k!bJMzIYsQ^%*rVS=>Ma!r^%u( zHbfpnmA`YMKrvz)z!}7+)1A0h45}g@-JwK}?9Ss*3 zs;dB5t51m;KI^eVxdyVnkna{J&Ks*Gm$j;}>2jF+>laGymOizDcr@`=#n)_?XHkYUw?bceoXU<&(AWtJ_^`GV&L&x&k$wPC2tBNaEOCPV3H&bM~+ zpdC$s7%kX_EUwM3v?SSBeBJx@X50f4=r}2&o~jC$KXsRXlvGRo*F6|<2<7r859fJPWl&2>YB8g&@?xPYkvj=+7qe_N% zFb-6O0Yn}AZuZ+MT3U~hKB!*`1UFQrvk%5GIA`M<`R1t%aEYnljGsJlsWH=JQF@hF z55R;Ufc9Cd(@bKt#DR!ydfT{WFpID$D-V#GE>&Oclt~k4V)VG|p`s#w?Nx}`?RdqY zYinn!tg(Y{ueySO#}n{Sd}v=zEN10A7BbczvC?`zsxC-TcyiKh9KVE$%Z_sZP%)X! zTA~S)dMlte>X+*UgN1wc4u{7M@8U=H4Dqo(P>Z~p3B74Oaw5rXYge_7AzR!0;@jDC zWJ?ml^sl6{3{ybM(8t_AD8+|H^X^9<-_kgLGjLx#IW4_(>Y9T78B$Or1|ZX^X-4D9 zbU>68$dnu9F#m;D>%lfC7a!cAvGIO|XjUl*`O?S$PY07tP0GCMP&V8)sMcRP-;gU# zlv%ABTU2~D=i@l>W2rA*lLo z1IVr4r{{*eIXpAvHD0>ph%?nVNBx!M{Vg%+ZiRvC`M%Ew`ud~y-M_c`(7{o4_o%^P z&1fW`)$kRMHp+C=!B%~*_ug3>pwVg?Q7BkY!%S7xJhDXzk8T}ue(K%ZyjL&O!e?H+ zBAz^dJ+4*iu4bAl*hRe)$ga!<`D^omw|7LXJ+f1m`*U(88T^igWdrDVIeF`_UKq@) z4FFF0EmUK~8i;FDW2^Sca8Abu)bhIy=9GL!>f1kVR&LB!!uEA4b$a1?q;MBv9BZ>x z7^qa%F}Jm;c`!)nRcJ^9aakWMF}>`kYM&_VFpgh=lIQ2oT&rB}RbM>-`~J0;UweAQ zU;X}HVI{yeQJ%5QxNe{UBQEPlq_aPK&$tol*rCG{5e^)Z1-ZNXt<$yAg_~|sTSrQ7 z3z)E|@$mtrN>x{1R_-G(YoGg~%8ckq%o1VXIH*5=!4|H}1{R54S`}B~IhX*K0JB2J z!@}-u>CgD@dhn?CzK8dzV>#E1y;(hQZ|ZKcE~TEdYimEh_rvE}7RI1>Wx+3HGh#XW z{zInw_&xX7=Wg5+JoVCL>4i7mismZwzN%X)aBLCCnw6Ax=7z6bo(t^zCbZJg?V17S z51ppW+i67t9#s`B3@e9Y{Yve{^9@oAn(Ga;Z2BI?`^NO@JN9RFoGMBv5GDpxeYmJN zGv#2JGYd|qhAOOr8LZ8Zi}1T+K|{7(0H|1lFCx(+G~^Dosv88s%kV_JhR^bqNL%=` zGpCdLSK81(Xz$Sju+MR!SCvCUhJgRQ!p-Z1kRYJQ_WA?*!kH^Yf8=QTM7o z`a*J3c(L}t0&j7U0^69u<+th4^iCASRo=`Jm`RBrqT@O`6B_!#LDfI}le-!}`tAea z(=T7)&%AUxx;a1RX=WC~&YHn_8mU5?0Ez;6?xJg)pZ4pI?ls)qBP#k&pcSKZ%(*$l zPJhZfw;08NoLYPCOtp%^bxIguYm9#+rxDjtbngzM{J<_N2Z@OiHHWN0y=nW$b>~(o zSVk8TlZ)Sx4VC*FN%`*1I4qHqn=z)r@z)^cKz3Qvie8k2hU1j0PVE;?U#(u)w3|Wi z(F3sWD;5!j2X0(t)l^G?-;unYvyD-ZB#!fv6vw6L_f`*M@1X7>1|#P$4G=TRumG*3 zJ+eP58LC)0exZ@Uu3BMix!XqTISQs!f9zl`8tGRHsJK|vrRpLpqtjDv{afc9a?L1G z)gU!(n}jqrMTkS!jlHrv)^A1ce&ikTdynnl!^mz7-6_MX2XX>jqPl3^wCa={E$H|H zCnV^)l2B=$zwz~G)rD~rjWjwQ8LXTHVlHK##C(z3Vr(s%QAQOTs=Rt#oiNnklOG;y zeCQp!!@qs*0)P62H=LPj+0%_|L54~*Zd&GphGk`kXaDB)MzD9puRglRP|>ThI@OA_ z$;cQ|_Wef-9tM=lC$88TQkHg7ib-EHronX{zGJUdJ-E%x1E%D}khv*{A&PRmU(@?C zwgUl9h7t){&w=Yn1AsaJSZ$LuBD+gD#y8J30tgf`q?V)a#ch2q z^ws^yp0)cjQvAfD_s1W8bYBcjgC5w|EVmI!RBh1!@an+TD&a!vgKrPyK z_0&b#^BVlVJ^e*plg%jZUcN4$`x27m-^4z#Go8Ab030tFCZ?+^TYHduX2GePzFP6O z4qKU>Te2Aak_iAXU>gf$YTTN^W~&YUi3G&45>~DihGZr9AFHSL2;~jq;%}EkjMh_G*lPhc+t@?9M2#1*<%Fx~{>c z#ca`ltqL0qpo4yww{uu8l6!y4TB^O8Cza{G38V7-nR=WyB$sBbFnegLn-p`jSN?qS zmQM-T!V{5Fd#D;rKnwHk*xK>rJpsI;o@BlUV8Rb}`wT-RF(=QO2JQuA(=f%lYUlZ8 zX00**5+-pHD;8EU#^Mz;sl%=>PZm;p$BaHyV%2Y+sz&J1m?UGwA{O4a%d8!RLkFqN z{En)N7C=B#bZXM6ee;~7)LdR@?{l^CA44B2FSEmU9NQcH*n94cb`3^Q5>95^dYA&& zV#|)NGWZ-Ni?OPiK2wX%f~z~nFV^|-({Gutp1PDdFs}Ol@B`a*Rh5057qea8$=eDN zdcWzgFF7Ehii{%k45I*J1u%l;32cK}g=Zn-swz=Zvqn`9`gbhptt6rez5>izAW_U| z8T1#czwzg%Yp4eGM-C6M2ln-|-D6oLYpLXtpMV)9UUjd^niZRvRB?(6Alqi?-Y@;g zHv0pQ?5qCq*Uq@ly?#DV3Rsn*gkj^D43FWMd!b-G`DP=$d@HCwe!x(Qm=4gZgz=JAHY&>x|9}E}O!r~^S%e$lN2+tkiFp{EA?GX2X zrsf`wv$@eKjCF>5ZH|mj@HU`Xz%-n+rLOFORu8~>kL5ifGRzah?(Vw@~nIEwK7M)t2{QK6%yz9ZmtReXUO%+f85k%ahX>*ya8&{p3OS z!;fwsCUbYs_GrGRW$sR3!vR1HSVXb)ai!r0(0o$ni$Z;ft;or8dX))^NABsXt&m-(S^tF9Vs5(S@b{i zrMK+g|MFXzKYRMJux&VlIk{r+j>E&^zO8vBV=9PaiAgcKO22Fg$Y497`ISrBcGGnK z$B*r)J$L{1*+2Ngx3udwZpgBp$N0GnMQlv(gA97*w`POFbTw?e`+!-7VljsVXfbLz z&Nr2X7<|qm{-*J*v(?I_8?Lo=NNv1xe^#~(xzIia(K+pbbIz~Ly47b-*YtL(x^2UR zG=dm$&e9#JIPSESiOq>lRH&3*U_x6MZSTCdranB#LuvrGvHI zr&v-rxfdi>u&d54ud^G_lrj6%gXl(ukxK;T!c2ge^v(mh(%x|+hn=$2GVAS>P3J5* zhkl8RbY_1buNG-=wnZhi&vjj&_p4t$*9H74R%lDbG93jOuB|&Pb`hFc-bTr!DN-<`60w4l^Ea`ou3inSsNi}LSBs}!$hwQ zzJqdxaGx!C|7kF8Hg7cWEcKATJPT){*g+s#-5Jl;oc$w$d}O<&ZXL+zM3quS)jBTp zX=*kOwkE5;`qPKxL%X)z`VW78qVLLdH3OIUg)clm9qry?%8wr%5+A!~L>nD6b)w~{ zU&a%`0qaUFis%W3k*~+)^V++1^feCr(qr|%eEKYV`neNo%~yR%&00y%6;Gl=qyYzf z>x-|~{i9p;GJ0c>Gl~>*vq&kDY7~^G^{xZiVAqIN8!4(5Ds?sJ;$PVsLdPB;I(OZ! zyl}1#M||uF*vhjb6m;X#rx6Q?vmc3)dR5Jqb)~>IbLXz zb9sllLu%fQ1YXqUw#Bq@Lx91_FdH@)0VpfY<*R57+45@ac5;#=D@5qNVDM%bB=xpH z4@xyBEZ?^?lZ$~OalA4IEJFIKF*e$9`*`rK*u+UYCmeBFcPu%+nM zFjA;0@%{Tv=U@HYQMs7c^3{f)P=kpkpRqa}P&hRKgktgd#Y*F;la0oO1zq>ph%Ty_ zu|oBuiYe6de-TeaH!%vaVV?o zAJ3UIwm?8e07293r1GrUq4>dvNAubKZMS~yGhZI62U4V11G%%a4Sw>AH|@W8W~%o1 zk-o+cJve6U8qbq~ujsh90W#Q7K~R^;v8wUOkL;}8w}0IG(=WdepSyM|60|1!T8-6S>OU+RD<>&-`R)MI|RbFI|z(V3>DuG0C@~I zW4zg8PRlWN&oEnv==asOx+xwW%Au3ZgV|~$0ucDBBI(%SCTtddqADc`$QXgLL%q$f zke)Iq=5@q!h#J_ih@P9>#*LSn)wY)!&EDpNb6S`u=X}Hz#&u9)`Iwc!V#z^Y!7Z;C=i@9}9l=Lwm(Od8X)jH7xL}eT;M^ zgmDy*FyjWwmeRLQ2li(^|C;#cUpcOvyS|`?a#mB#d+ zD9q*pn>-gHO(Y$&ZrYXUy7x!loUeZ2lqSxGqeam`Y^SQLG{~CJyG;e-YC^bWD=JSF zkEHd$^UJbk>4DUzU9alGtCuSN^laJPQjp`mtV;c#Ry9V)ga@9h?i?|s-TUsVzWCZH z6UA9%Sa|2UVK|X0pSWyCUw`$6dwFUB#>7fII$&W|PGX4@y|B_bJv~2(azl)~iZRj8 zq=)W1>kfSW zqzrC*IjP7Pt$`wJ$(a!avAq(&1OS+k0GNYp2t2pE7JxPBL;Ps0v)enyC(lE%8Mn%z zUap6YZNnSvBUIE?BMX{ORFZ19 zR;}JQAw>WB6Ze~zq39%;Uji>wZmL89UjE-6pI!Le^MZfH8O^I^Uyhu~lcf1&fVEMR z6xKd1PL&plAw!Mje(g$05nj76>!^a~?HD!{s-y_W5`PhV_Q0#D6GH~yx%*!Cg_m9x zJQ${vJw0t6&{5N4(a_$Sa^r8jI^|wLP#F4b!j=IO*9iex-1J=R_LlH@LtjyN#X5Fy z)Ze;wmp?gmla))e05OwvMsjMKlz^0ae%hn=Re{?*&Ymd6Z4Mr*sVc!R*iXGt2`A@* zf~u^42pS(hqJEjU0fS#vl57B&u?m23&6HyW{m1n{4*@uG!D5xRjCcUR>Hx6M^#H5~ zU_Ah9GbhJBK7jX{oOm0xJ%-Wz@exNdU$f!&m$(ziZN2b>z>31S;Wt8KWd&wL1ab{8 z2-X2$lv?aS0&Q(%h?jQO5NYU@M(B*95|hR5;FlNxW8s43RPFP}>%v?;%IQlB8P-84 z9-L|;uh{O#KJs|<^FMM(C`vbrzE?Mz{qt2;m=H9j7E0z=>6J@D{kQ-0MfI<~_Of=f z?2EEBpvguiLvIt(`{Xq|*RE}Y$@{{85S)oR?Fw=17{>X!ZV zl>tpKhWk|o)`@YnNx)hMn5FcisA#6@h<(b5tB$`=ny>F0HDnTcgi+iC2$*g-Wo_GN z(eEGL89x8=@kmg}%8PVj0U=0+ju)8O;E0ZdUE7Pc&R3BYjODiz#&1{uCIE3h7b^#H5~V9TtP zkRA-$Mr;BYZ#J%+p}}0$34&ay;fh!&5c-!ilUe)1gN%5)8--Xv1x5g7H!Co4EeetI z!10o4S#_g5JbZQrOCV_|uT=Xn;?+(Ww(f3PxX$17Dqnc5F1rv05RKTR9FrcP?YP0A zt@-jl{+ajl_uOM>kv-W*oBs48@h(+YBGBM^cBFcKHdFcCzkEaf<1as_P0qWbk{wWG zJq502wL~$=5NCw88JE3J{rnMa$7ntS!A4SfHTyUrGq%*u-HM(6=WnO{%g%Vwu<}_b z`x)nJ3pnZbTcUeraBbu1wvk?;k}#7D7;h7KmXu+iT40 z2X<}uLzWRrVar z4X=RNKOx@ma-ZxwT+D>&>-`O2lbr>C#j66C60es+BoNe8r3T*K>H$~}z`j2LEChhb zcL-pdDlnoD(+Z5G0M;1j&s2SK`SblS5k9ak02XuyFzSJUK0;i5Qh_O}0a$bR)J=T# zHkif4QoEq|{#{lXs>FQCcy&0)V-b7fnp648i8^-0h*oL3F3=BBj{)kw_(z_&zw%Fi z`oU<-n#uciDU%K&uU>U&M#iZN6AORx)V1h${`hJA+_gDTG67uZXwjy9In9?+szGhe z{>2~LDL;Pyc((2YtVJ7gvW+l@YNsZ>_;XKQ5Ej_h0Zr32cn7ZvT&y`-K!1U39&{eO z;uFIRj+rJ#nidY44UQ%>OGE)uEFE@cODe-PDl!I@iGIVj#EaKwO9w|KIo}3iz&I8- zv&x}E_t=*v7ue{cwvBrUo<*Xo7Vs^zqnL^`L_cwSVuW%_#2(`U$^y4 zj3`>gEFIgE$#G$c>r4Evu;eJ%&_DNdy7tT~=VY!G6xAwNoaEN+PUNiT z&;G<=i+|$B4`x6cN=q&lKs!ZsPfmul|NON}VoBVZ2L*-X>b$>H6Gm=#P8V?)}GiEdhgs3cn?lQ_TIsWjbtMT5d`1%1zz`<2u_ZuRzYNI1y)5c z(bbe$*HW%b&6MvQS7cLHlqhbgIYBw{nzU=r5%>95PBJ@SPz%aRw;4FNsVlmfRZBLD zpE-U#n7UQ=_e|vZV8I~Aixg$L0vS%8SJOuFvGUFb?ssmM{OIEK8RWFdLTj*>KnxYN z1~0{EFeBNJxajR5)*M)t$NN=2Uk&PIXpuGwVqhmY*azCcsq3NqaF{|%Ph9w;Qa1oI zAmYGS#Cj=2+}kANkefZQ9)R@#tOsD7mNmGsr~+fH@duXXgaw77DdXOWs*a2GUzEG; zmRA9=aIpgO)~>+t2w?|=4L&p0+y83%8dBv|f~^pF-6^~|b={ozfnHDupFY#5ymZkm z&|qxK;FZ7&fhR&=KI{C;pZ`Gly$55}DPJl=Ixnr}vBYDnl4ilK30q6Q_k~l!=l=Q` zb*=%OTy6l=j->N0z_oaM!q7p$v;WWk(Ge}5!BAL~Rvy$}fu2yfK5IArFTQq?Lt&{3yt{8E4ZuWHS8wUs+$;yO5FjBuTp{O(YgQ( zJ5|R|g8Q`fXNB?j=uT8#DR{~4;MH_k3NV~pf8(lG(A9L&O*$+9FB_>RJH|&E|K^kL zD<2q`wVc|`Jk2F_HwB`8O&sg_?d9WF6!+IY^A+W}H!n-FHKfa$X_1A=nkNZ%PHwH# z_?h>OiSN39EbCxUSrl1Q+KtN0i`@V8)hTz<-AaXFtXtnMH|nTjEVrj>bH6T)&sB}fF-vRyxv6e+!M)T4FJa4 z0SsS}jtpM#`BBaG455pSO$uOK62ADS@A5AVz7 zXy@GtUQ{{$-J6ZZsY$;8Peie`P!Mo50ABa(*;@PbCmyShs#kNKQ_hNuj`1B82&yim ze|V9;r}W2Py~uy(Po9)#8cZ>A16j^i^trl+5_-O~wGamX_@jqR?D~~lJsay75-J3QRlf=PKlCciMP8BNsS2gJ#f*I&R(~}7f;XEwho!@wjslesDanL z{!FIJ1Lu~0c=vEr>>G>EoH-@9u@vG(3B;&L1wdm2Vxpn4>Uf`Gw1HUi`7v9kj`peU z)fq3Ms$E0t05CU~)jaeyNbU=rMa1#dlF_%?;G%4hyfJprtDtxisdI&0Q^c_d zVE&o_hTfNt-M4zX{#PpwWzVQydFR1whVq?SgRG0-g&zTU*=MHwJQgw5Qk|uhS0Efa zysP}FpL@X0#~1s8z%fLzyN4lxiIE+wU9Aq+e(keQDPQ^4o3M2qG*sQRP$jL4f1>Kk zt5+I7`~ETU*ukNU>!p%;dQU_hzBO5`|H+F^$gS-;d{3cHIscsIL%009h251{r8! z_p#>Z3_-nbB2>o{9Q!l%+_0)+fiE->B+fBF*i;};pz(Z!Cv!L(aJ`fC#?}nSm z8C<+Pf_r5GVst6sb1D~Z`J%N7h`~5Z8!X6;tG6Ieif<2M&B`j!R8l0azR79=Sd0x{ z0JRDFn5zMp3;>hY17JLrK?1-u1S?>tRd3k!nfdw8)}0=J2|X*RAIwK$V5^A@%tjXJ z{-B+^jqzRlUF)#p6=^of4#Q#J4yLiYHL-il@r_#B+~&uzR{2q4Uw&+tp!(+rFr2L{51DL#zmcHlx0Rkb z>H5F-r+=pP>o-U;S4P_(ygR#KZ!25$+qayyc@SD$SB2Ofkyki~;E4`*^ z7b<<~b7xES58OATB^FLZ?%}@{D`$S_(R->VFZ?a&+PuHsaR_p=Cxss_HR}()G0RU~ zvn&7X$F~II1Db_im9P?Acw$54)}{)N?H#J=AODfYZ~f7ig&A9@V}>q+`q@^zCC2l^$ znW#&0++MS{(2DrTNYy9qDUbqNZ>r%I!3(J&;a6b|wF12GLv<$-e&}f5!lyqvXlmh& z5&W*g77aMsf;*FjAdkh&?wQa2-L>f7|K49|vo0n`vjcemxx5kJPUu6|_8z^bUxL~W z2DDMr*&%h>ICHh)oSfE)k2HA~#bJv!yniex|H8w*hBKy9e@jS(9|`vS_1|Be{~vzy zdHZ+2d^J9KQ<80IP-oVNrRIkR^ulnTR@jmcksJdDd2!D~!Dt83c9}RC4%a+UIJPw^ zeCR+}hhcITgb0QQ1e`(C({u%3`#i_yTD>WPip^X7Fw^9Zr{79Nj+HHBs-vjQVyz#W%F!u~^N2@!PEj{v1Hsk@U=X0i9%J1xuc;z~G+A`s zeNUc22bT$#`Y+A7Bq4hGv|T-U-CGG>Zp44$9et%={mC&4<6yZUW?dxP1YQvXFBaI+ zj+y`cb1#U0^u=!~f;ps;{(HTY;c^eVI5g!TeDuhO!rKBk={=$92)}i<=DP$q36ki_&Rm)u|f(wsi)7c*L6B%_$QEE-Eh2h(B) z=MF9-{SZJO#UFX+wm1tNc9Pw-hH&1CMDauWeQhKcH^`S5|Dsxefkft>wod=d>OsINNUoqD?kiW6E1p~ zN5`GauYCN2-VibsIzbG(9sMgO9P`SIU)GeR=SfF5xUg^B%t8!QAw}z2B@RB1L`(sB z8Lmk*Bw^l*2}5_KXo`7a@78O{^Z=~)Xp2zci?=4NjH@}ulT!s%YoxZ`z>F4OM+L^3 zyGz`l=IXJSi6gi7_~D$_ms7HxF&QXk;up@_l~=F0dE(+az)Se4NBhga{E;md3LuK% z;9V*UJblPC-E^kx(YgQWx1QAh{*`l*Rv3dLx@6Lj@n*cae9!lHk7&ZKiM;AHE1xt~ zS`^T$@=j07T+u*dv2)!=h(o|*P)4zYgdGnf+%UZf6E2P51 z4n2;Sl`4q>2g71{LlL)+<`OT$H5X`H03^&nPt5$#esUG-wxFj~7=k$U7cT{2!wq~{ zY*jX>mV%o3$dRoPC_!L;5N?L_A`rtlO(?+M`to%4KYVezf)@>1&M7O$d}U0)R{>&f z_E$ds0dLR@g2->wi5^ZyCepX3fAK^^ohpTOy&c4agqD_$?zHmz$MtHr{ICvVU3opA zgb)^w^SjuaqEHvG#=T}&55T@p2|>PzJ{ULJOnccHoL~Ag7%~Qp=ym@oc3WO$wP&Wh ztJUHxntiVrb6z%jBKh_OCh6m0QG5NGTYdhbn`@KGCp|9$ub+6Nuk?!_9*XENk|=G``6gi&WU+g$K45B8X(8@?ZTNd>h6#dsoKb(~t}z6n43%O88c+XsDS9NP87?Vco; zgsq`qe(CiZFEye%fnn>g@ihBQhN4v;>sQO&a>N#meFXv%!CMcjz}q9qH_1*cpFR+R zUJtAXVBf!#;Kqd<<_ZK{W5FRGHn$NfprP#5;^WtNEwRf_@szlNje8ebrq!n)#o^&? zW(L&(>vWZWYuYP=Iz%KHTeRgNx+(2>6&C)xAK79;q@Yr@$-0A=U}X9m&tKHa|L(W{ zIy+qrxSkuyfc{hNP$UzJn9H8n(qHFo4G(&6C~bmMQ>X`JR9`$^aqh@rLzB4wLkH54 z20|#%Uqp16>00uTlxMp@+Rv}_dGp)PO0;8YDutYP(lJ``HJplUwhp%)uDUebu z*hp|Ja=%Q!5&3+B(p`{r(rRKXS}*Z8wVUE_FfUghIgr(9{b^A$akKK8DTnp1yxE|5 z60Ow_lM=OIB)oHfrukXpuflob#)0c)h9!lAI4EADD8yRm41EHensKW;#|N@^XX6aD?YHsT{Ad__ZWFt^V7wiAWvF#O}&oSZZV}!Eye= z3?RZo+NQ`BTRxKDO8d5tQ0$AxlkAr}dk|P<4M}+YvYr3bA4~=dRli}b0%8fB=v1?h z?v=8i{OJ1|2Cw)+Hm?yh3`YvFQ| z*0X`Rt1y!cI`UZPm0&#p>pd24ihv-AwNZ%cxIbw1Qa3z%LIcpu-e{{Q9{TDwquF;M};d~Rsy3vW8K+G=Z-hGde{rR7Gzo$kE4zMZ-(Iyb19@x2?K@PNY z4ab--Qzl~5G*ndc5AMx4NOg|71Y^>*S-Zojkxh(lCDV?*%Bu%py&l+d>}YnMxUeqO z1-WudiWF{0%#oIB;%aXk8{c}TwH5yBKYl3Z7cz3Ly(hs^eum`o?rb&Asf(`in9BBJ zVcVcq`_#v_N+wdcNHV^1a2h?72;h|;D}VjX(EE+w|Dql!`B>DmcMZG}k3clQMh0O@ zXh`Z(k1BooYjds+R&#}KRU*0oeVEg^w`U+$l6)&#j%Ce#`CO%RZ63;W#WHT^{?Va? zL^`x#yl^QV{p~MJH4y9=Lc-r1nBd75QEA9krpmBhYD(kNb{Ufn4ioc8r)cH%Mz#>O zK-Z>A9x@H0&wG@gMZ@T$^^M{jRxgOYX8d(8itW}BC zXLBEZsHpvyKm0@}d9yY?G_egW5R>4KnL#jSrBj(m*W3PaBYSW{uXr7z4idZq|1)=9 zPi0(#}LQ+;bX$hFjDDg6U?1x+x&=)F-DJW+^I`S5`Ha$O9dg$Ulu6+0@msL*s~E0%JmC_y2KPwR_! zbNsdiV&`tS`TzM(CMiG8rT4p%8l^cgNbIYVg@5q)Ht_?G-W$ix4CM7<+{8E<1XsUy z%2sBp;c`)jkPOz#NH?(Hn^;{g>Y&+4x^aZ~&M1BMo!X`L9zxHFL-_uaa4g>j(nh@5 zr()}PEpZd?D_$+clCrbf9TN7A>a~MgO{%<>i8ctZF3BG zJ9|g1V%14QBD?jxwCr&0%^QaQ>!0~E)nz$V)+~#!k-4?WYM9)Fs`-o#)mW5m;Z!Qw zj#&j}84w(hl1*uMe@H7ZK^V#;f&=L2IGMN#RYwr6PtU=55se@o3!7B+!CY2iPaa>; z&z!qf+B;?j09Y6AN4x?a@cOKS3N}zL-l+1VXPC@n2|`xm2EO7M)X2)RX|*)5D~9RQ zYz=BnVR;Ui#vIYm6r5YnxZBryi%xvwij)68{=ccpr~ctMJhIqg%u5G|v8aBf_>VsF z!2FHrIpMWSrG{)2vs7)0k3i}}*rAq_#l>u@gkyI0EfniFS` zQ^3;=7sJuu`F7g-PVSL!SIyRY^eV9L_d<-NU(L-1BT4t|SMn&njp%-*tq$oBR+Ko+ zut5xla;K^AqAJRUWL}BMX1uv7CY6_z$l{%^oobL@pw?QblD?N43-3RgufOj|AxqME zsf&?c;;61${#0$i`M-Yeul16zAfW2*gI9Ye3kx`5nZcL5AxYsUK?!Fi^6st{R)WxP z@wzCc5;kyiLb>L#O3hV7Ns;bol878Ubk&@8v;AK`8%kd|-st=C30FNguW5lXk`>ha z|IglgfJt(m=YjQCuIfB_Hs^$0ghe7q5Tpo_B1O^+$`)n&vBk%fi_+}?|VZ4=X524#}@1OH7Y_$|TaG1YOg!8@E2#$}PlBoC_AEWd9b!$D(28P8)1tF+qj(W zg72%CqW^>M9wC#hd^1-+o29id03-ESswZM&9;~4>F!Cb6D=8?CTN( z(Yyw86)lt3_P-XovS=AZ91Pp_z`uy#7APvWs!QvxjwD$DjFn1ip$uQ*QdP$Pakk|| zQ~<7V8z#Du#8}Sig#K*B+~8Cu47!TkCOumBrUq{>QWcWr!DjIeo#Pb5Oqu=hkw)dW zpPaFQ^-2Gkq$K#hqwkqa^pC&)p@c0lS5hwGchN1 z-xpSy=k|~W*QD&anaDoAq`n-~WCH;E9zi2^t2Z-ei)NwJu4C$r{NU&#G&x++C#w~g zcoS<;#`1-H`PBiHnEkbalH_bf-j$|{_7)E=&xNv-*(xk7{j{hcEX{jc2l#TTI-ML;&I@mZuhAimt?LcOgoV#0TdPg-;4uKO@A*P( z;d2?J-4?V;R@*Zo8YNIv$utzDWTd`QcEd_fFohw_R-?!Kl||@cAHqaBPTC5?+A*) zwve)>RDtDjZ&$qCR0iapYXw@MSG$O{ND&Y|o0ptoQ6Tv?NtMFW&D;sy7U+(^owstgSJdAjSDx$T@ z1r5Pfad0Q=8FV}0LZL(eFp<2KQfUIB#6UVJh-{@gGZqYm%ZnY+TqF+zVcYR2m&@dL z2D~Vk6J!_W{*OPhG1|4kD6Hh5Pvjl5Jkfgc%^>`fXHHCNda=A91W#Q!lHmLHn{llm zo82bSwDrKBy(6bAs@T;|akmyk=BdBtjd~e3y_Twj#^C_KzK417H~mAb%Pcp_hz2W1 zHl%thID6x}v{Y7!ImcRYYNggB%e1c@vIYk@iZ7h9%xQp;(nD>IK`nT6k7-O+rFIZ6 zkU#{YQD2)6lz;K1(@mm;<2hKrj%7LvX`HJ5)$iZMKl7nSBDZm^o%+kIGl)?-30|y( z1nWY=G_kvB2sezlY|v5|>Ag6gwoCD+IZNbNoPDYi$UVMXl-T4bWNMMe?^q%f2!&8b zgqtUdRIu{S0`3HY+w#Ork+4kuoC26NQj{7CZ-eJLI50t4mQ69y;(9N-bTD-N6^5mhNh6U5*@)FG#bsIqH zc=ep+Uav=`d^WZ)lYOg$xro(>0!$s?*8soIVmnlCMejT1wE^OFbp$KQWg0Eo5H_9eA2!GFlty#^e$&`=xdCSnGWJOFxS-@MzhkyEq?u|e3zWcaWziPwez+bOz zb;k75q^X%kY&$_~QBa-HEjQLwPfD%!*fOXmplEqaJv+xjXpON9ib90W=mYzQ(c*%x zD)RmH-u_d?lmxE7d!s4r*jQ1pamXD^cyer7gf0wXsy-K$HV&rb*`pOj>L@Ex!VHji zzF{X#J5VV*OyKJSU?PNAo@=)%Y~KD~esX)^(6-V@8=Al6f^|BT39hmfw8b5Bzxp4Z zD_y)gQ`9vjJ?5+>EeN%O*!s}H0$PJK^ULRaxm8)qiL7UjS&(b6-uF&y4w7+5H#n($ zeHT)Odp+%V2m>dM0e}r2UC70R1&v7>&bvw$Qe3GRPD*))nP3P2VK=nZULZ~}zA}NJ z-CLQ_4j6;;3Svi=_|>Imx7a!FLCfcn-6rN?Nlp~V{DX{Qsrbbwuf<<}>QW0fPRUB- zN9t_}?UTbl{e$-;sKgS#aTW8t#J&lKtku)5Wl3o{zC7Qw0x`EdWci{$F|3(}>?ibC zZj+m?kkX5nFa%6^MDG{fIC0ysJN({5!vgo(9$EUW?|qO2Qq+z<_~2%#0Co<|f#lRz zk_E3_mE}~n(ATgy3Ws%H94)E%nPf4x82@&4&Sm&{s+iU#!-~!!u&Y%O8__TR&AW|5 zyGF|GzJm44_7=wwy2{R(U;XVDi*KC1STuE2=rH}0Y?I`C^zI@fa!{B{Hacxn+%x{S zkG5mSPfV#tTSogfJa_A@79Qh#N9lEEU9I=QV*p^nJKH}|TyzL!Y&EMI61PrHmYc&P z#R?7hvB;t66q_PjTPuVKe8m}@h}CV&3?R1>VaBuQ2VvBWwx^0Pm5kpJNKKN3XEt08dNVxoBa6_})m z@ZaTUE=+}}LKaj6#F87U>e56ZL{@m@a8p1yVPJFiU#sWq?i)zGw9}FE-#zsYvYhTC+Sq#@K1qTjRPF|MKS!>j!rX7ura1Srxq09n=5oZ@*f4 z>DbwF!O#}`5_zf}O#BbtW%wIva>?%@eh@?Sr8Aa&WiBjt&tSV}Q8@MTTR64uM6D(4 zMq-q~V*p_9;6hB~+4{-*R9?x3jkWrg%|m5@XCK}$PNSm8il~moi~RnQRi1l+Fw)PZ z9&#Z)Cz15wX)*1oqb;>=MN3H?*}+O5xT{#$Hlj4VE)c_}Ut?NH`#-;YmOuH@bh~KC zd?gU0sKcmzcI0z^Wta5XPko=ut!daLx)$k%Zga;h#atCHPt6xu#`R3!4+*bfG_3BO zfE5;;{IZH~>9LWgmmP}Q*G?*kmQ2V^m-#`=M{B13xlbL?3cP_TS#qOsdl ziUX&`&}z)KwhcweuYLYOec#raX}K$d7j*|iZP(1N{mvVu7vDUy1ibQ%PyC+Urn`Sq zuXTf0$7l1@Rj>W}d8dpG6XX43TcI@*jJMGFEvpPMRL$(ExWMj|GJNnD0N8gKcJW2Q zFcVV|XOGz)efTRC-P*LF3ja7k?zqh&fxKe4kw>PV^DnIY>(?%Vt0dFs{K ztP<<9GG&DxoXgvnhkxJ!!}uRQ|AX$Ro^Y=<+Xjfm0DwS$zqe?GC@7k$p1SJuD^pfL z3wx6LT)?VH*fCKsc2wOY3e4RqobsGTZ|(-l6~L(o63LhuimYU zsm&m?=NnWNq%Fl8cI%jsw;sd5ns4r?1>yhnHx8=@whxsZKMnm#v$C$Yne_*+Dw;xh zUc9e;ln}$z>%PsRSSx8N+RH*mh(OibizjEo7FjpSihA<^#`Eyc#B0q>S>Jzenf>zT z?k^6NHN*CzRlq9=6LZhpKmSiB^`~DvUg-rd7kAFSNu}|=eP#*cx`nx1M}OD^Y2(S) zS}MGj4Uw%PBf$%Gla5wRlU^qRVqSsKeJJq(ehmO@;78x)i9Kt zMZp%_*1agM<$_Nx+}J8A3$$GE!1yQ7k}YL{ry{Nf>XcJ z!g=v-mfVpU@k&b56Qv_Zr{RIcc*l?fzJY?FtLl4pBUjp=N94V<4Es};5iD2d>vHLL zUeW?tOhPd$<2YPJ*t-r*mVW7{9yZ^9Ku=`hCRjVguvXR+EaaMt9lnp}?l;p1NJ^Na zxd0xGtwPHO2(YR7P{Sh9-Y}$Taqd=;d)dL%S5hadvU}=!kdU`s?&yx`fuhEU*LwD~ zYtd*~4fby>X^;T)9VO7=M}eabnV$Uahaa?>?Lazx`ZNSZdIUVW>52k*(1~re{KxK_ z#3>ZzgfDsvj@MN2_=Q<#&M)KaN^(DNr@+qBvm#N^oA+#p@jgVNtn)C8S*@%o?>;oC z?%7m?2*5(jbIWrbn~FLc?h~-&g(p?yhj>3t9=3z3;=1qJZ6<%~6MMCfKf1k)EHK5- zN>;AQX=&~K+wZ9EixKMZScDL-~I3&^)v6^Sw^)=#)W;e-8zJgNZ&p4>%V{8eCmZ` zeZgzfkktCby+s|L4=s&~$&$T!x*_1)19$~fjj*E1Oj!N$LVd1E5$;3%M(+js0jHp= zltjQOO&i1!Gr#rI*VYPfJkcoZbZxs#Lsc!jfdU<~ZMoL;^|{}&0%5juA_hyLFvtTF z-q|J|EbDrZ69-$=WGRx1uq6U7wewx*Ac6{)Bum2>*&1q+RlrFkG4tx*(_tpUsRKTY zVsp?~Frhm^`E?=Lp~%ZnCieP~Bbl^EtxN-1VV>tFom)1e(o)1Q3rPR+Gu zt658p^;BY5qytAUh$lz>+s{5&-@bV~{++Kp30DfErJ2{V4`^Mp3(T)tR0@ZSPw2v+v4>(c-RMt4^37wQZ4FR#*5;nEOHkLcxvL|@n_Fnfw_gL zV)vjd*fp&|hlY0=wC88-eUn1+cYpM7Vei&zO4i7TVu z-ZvXHPu7UZFDzjL@=V-I5i^YwBR~AumgbI4KN9@EzxQ7e_;{_Qm&Xl!gU)%fCN^Rs znd%=sebImT;5g=YOwA3334nydyK1HT&s?2*e#WwOqX=+}c<*k2XSrO`SKd%-(`~W& zS04y~GD5+FB8jRKfMN<{8%B%L)=8ASXpv!qVJ_;4?Hl!KL4rx#Zp1lR-nl*cpH*E)Epc(e&ydkSAx!~)Xg^R+y?|+f8}sd zHWbMSyF3nA+F20d=T6wxsVi=ktjN}KxH!j%nGv)#y*0kxGB8$HTH+0suR#m(9o|Bu zHB>_j5g$wvq9*VWjEOLpG8*!4A@kmm(GnRmr0os@Gki}{w@6Q$ZBWt{VjMehPYV&d z#;8^{&_Wb72a4m2jK@|WQNRX2N`$W0Ufl*2ftMGjJvLrd+7+x(alGhJM=lG)C0U=S zO3vwNWNg3=PtIo>c(V#xiqgwRPbV!`74E<1pe@Gp3XBFNp$}u6oUTHzu5TJ)?2(5a zv`LqI=FAy%2IW8oOM~;(9m1)|QB+mBa=qnm9oJ%17CHe}$Lb2!M&jmSnZ0^?+OmWy zT+&yj6(W3*ovTawOP8gvpmKX-B@t0QQ4lGkVuu!Ks>;yfr9X=R5fO{Ji1V7#@KJ%J zryr9yMl44w;O?IuRjl89Uejl7(lDG4M(CW%aEb%*{~{dJNj=vy<7fkAKhI7(kd&=yE4gj90+=Gv~{+xx0E`2`;vN8F3jpR~>+fd(wt}D;^umaH6dJt7 zmI}vutX!P&*?3K{Dg{|z24Y27-&~X3bJIcOE`k^uUn06sy8On8)6u1ATe$b`!%jhQ zr5N4~tUri>Sl}v#(9j>ff4@62x-mI@>a5sowF1f(W7Kn8WeuTkzziLFFvz>Uc2@Up&>c6Qyd1E5v4WW@2_Asz)wJDr}yxi{M_(NRkp<2LVhX z5iw}-r_)Sd^m}sBAkZR8JaqwQEarXlwCw(WFKFz!3v!7-oy5v3gBN55NQB&?UaWV-oaT?b#lhgSB{MYZ*A3QKt zL|ap&^CGT51B6(E7pt4=FI_JCzw+<@qIz|D-ZTxhmu)C)9R_&ahb3Q?jHsI~pKbzJ z{N{P5@xp0`Dlcj6BTXI|Yn*@4LsM!HSMp;N6tENdWKD6(eL$>MkhE>1N^pJ#%m$wmHJs%PL`gFW%9)E- z!#7S{75DDD*98zl!`#sOX{%Tk6D7vpY~jH6YI6AQ`<&^yCcAX$0%{y7L`tTz+FSxd z7-eeS4HPu-9=vN@!|7lhkfB9((q6czC@)-+ z;&bzo-}Er1i&KRGEWTj2m|$d;2p$5cW4L#tM#E@Lt`c9B zfmaxW-Vi_A{QTn;b8^@$=3a^UOi~mqJM{m_7hiW|$vNTQe+)tz&H{Bo51i!XkC$Z|Zb<89bYmES$CG(LVW zri-$uM?G3EfOvA^veSI_gk1p;EfAH~+N}V9rQM1(0+Y(U}=1~ah1;}%4f>6f%tgP=vvwi}w8GMVm{kP`&SYt?rW?HMyL<7=-ph3`LH zZ4H%`a_>wGm00nI9xOS3^s?2MYQ|M)oyZxU?3pC9RI8l0IN$!q|MEYx&;R&Gf=3VR znfDr}t1J^%^;v|8YJa`7xzN!5;pg7l__OcqjQ`-zo>i~aueR0VsEYQa0h|85Lw&ta zDg5WBrrP)J9kmbd9W8Z4xv5Dpf*Bt>G&&SS)3g8a#mlooVFMbA)CNqJ&%_p_1*w2o zYJl?o%sEMT=B%8M*}AE6kJ^DL$YAzohj7OTn32l0$hj8yj22|t-c0x^evT+CgTQku z@rBms-d{AgjhD(Utfm%4*(6++lP`SvsB>mI5lcgbLO4Bz++F|Ie|lN^^4DJ&M!Khn*X?*4(*GEVln-rG>yO`M zmT~En-pWf$y*1s|w~t#CjUuy79?V*I;$Y*F1IMlCviT}eMr#C5h0Y+)g=qN(Z$RQ0 zRABGe3XJEKlebWT!K4p6>;4tkNQo`95b>J;B!If@iHlk+s=)fqz?QTSLFkTUwz4Zd z{U0sNw7I6-oOXyT7zE|~bw8P?sc79uTBk+Vq1Z$0tn8Uk_`DU{)9pB|#Ig|;)WX=y zTj+0o=f&5tbspje_wKaBXx^atQQw&zBT+J#h{0Up;Eqc2(BXSs*H75xix;5L@&Z{k z$fil|twcmxiDM}_dFGn`?z=}>2{e=_&8p9QD2d^{cT=%YQ!V$+3p8yjtFk1^8Cir0 zt5sywS_Ckqqa`rURR>m7cED6O5<#dSzJ?&u42{5R^(}jf2PqRXaGTbIc)|Mo2TJ;$ zO_d7jhz0PX)?pc~!zYehYX8QcUX0A@Sdqqk%jR4nnEcc(Y|gklM|u3SpWdzfgfz@W>A3*gv^8D>t>XdsY3*JxX??%V>f!GRZRr4`5MisSSZ|z zCl*h$$`<#vtS7w}@8TQA);de);R-ul(k7(tI#_ z9W5iilIS_(vdIFd_@#06(#YTav4g@t`a7TT?%%U9idxq^->RoMW8KEUB&^m9y?CxJ zEC0uTd;@~(Bn{t8QvXJ(3VCGvkwcZ(&poc_qx?)A@gX)s;AJl; zV)m<;6F`%d%r2Km2msE|Gl%UfjRVCC?$3W@xUg$Oxsp|0!UA{|!5%q!zHa|tUp^Hm z#ZfHIqPA!}n*tXMpd+_6KhrF;dH-*JY_s^!{?0>%#~;{8Hcpb46E#@BOPSs+kzkg_ z>n~ic)&Id4o*GK6p+MH)JR`?#^k4x!IKuH~CZY<_l(eygR zhDW^F#OU?q13T!X~V@qaWU{;gl^+pvKEjqU6H=B;9OSuzmM&d|T zC2?Q|7NP=E#8uqM#gy#8VGzu= zVybsdihOG7syo$)0v=my$Ke_Yz57Hs)R7-VIt6pvd~k5p%~4 zhRia3xGYiSU&imVsGn+st*?>``ipXapRPW(5bW} zRX5d!9VAb`dJUnBk+W;-xK-3KR|$P-MN28XZ8TtIYAf#9WQdPFbjaJhaZ}Q4G}z4b zYiP|z9&D{hi$;dXOwkSX%`@|n=U5)}VML;Va|ReJiU`x>gh(D@1$w9lc7WU=HT-MS zb9U2*A0CMF)&O9g-8Ja+0g=X5 z0j_kt16V}|w;Ij&94Hs?8cQhCJ3utH5U5<8Oy*R3LOPAw8Eav27B~@yZmiiw#1R|3A5AF zkqxUSoZbW(VTxiDflf~->BO- zR+w@r#D2%y7Q2E;Z?qb+h4!A1R*uk7_<_&e+1B>LHRn_JaOuMO0hd^?cX2o9zvy!2R^QN2H(m)Sd!>t57l(1r}n| z=39YbxKMRr2O)>d*gX5?BSG-bfA{&3i`S-$M3L|Go>?AOfa)~7XS3G&$lV3Api4%c zK+tWryW-}B7^+26(zlLj!AvXkT6RL${6-KKBHeq<^!Y_+8Og^CjPBvvt-xTgtVAY^ zp*tH$0GK-fumOO*lL0Jmx3C~>A!YzZLS}Rk`vVyMm=3?OsD{@YZOY%(Sa-PLIssTp zr-u>zO1_9JpMBuyf=Bk3+V|}&l(9Z49n!{fL)~;)Ct7(z>A>CO4x-stK={}@sV0_} zSYRlsR8@+(n?32pFtbt{ZHIsR<;J)3FCd-l4tjCQ8%78EcGqLC&Jw3~=Of{1S?;2yMRw5sZKKVm-RwinR z65->nEk*O*Es{8{wIOV)+ik~noiIVL4d!SJ121$#7tDLv(l5GI09Y|ufQ3GSaQwDi z;Ipkm!?W(E?&k6*AE=i0Y%G@u*@7r@*hoPm9~zYkig5gVv-OL=@e;c@8;Yt~^Q~q> zkRq#fa1#qY`}hX=r#`;BNUcIBq7fkzvv>ix*b4mAB%REe#nI;3#z^Cz|G^3UE8l!= z6ryrjQb_wX*G3>Z$ChTY&rV1yqD@(yJuv7qS@xnXIn9HQ<-G4uCEPfo4N=RG z3;n^1Vlq%sx=+2?_Ejuy@7`Y`Lsz}iX3VQSw1_0*qxTfm4K=Uz^a&UL4*NklUA&3J zfmp9%iN}%qC*ORPy?pe7^rIhn!1~~Q8(k@!slYi`p**p^OLUIqzS~6mscsycFoQ!s zwA-3)-4(rbVkY_aODE%#7p|%a3(Y5m<0={m6?T$mYV=6K{v~e#mAbz_stpUE8l!!v}M~W zDvgYAk|#XVbwrDhBR#qG!2_l=R*|bzb!BtAg)lpTybK#wWIc1V6^~U_{=lw60r%^Y zr05Qa5%5un>U;01h$X{nymHnl0{oN}+J`XgVkQ38d>;Fj&GIsJJs*%b1^_mA5c|4| zwU;xC*=nCj%!E8z6I7Wk{fM%4`YfkAX;}2(%fmXObw5A;4C=OMKYmX&u9f6UNAJZ} zh+kN?CU` zGXn+(-Y_d8+H~7-{^jp{U3mJXt?`e1=&reYwhDUe&4V~XB5kSvhB&Q&Qa1~g`Im== zWGVT9`zzk#hu`I1p5xxDr>?s%9lfX=J$1?ag@5*}@?#&`=6&Kln_JL24RstZI*Tv? zS&-_IBz5~lN!>Xq6u%GUGa^>PChSHWKatyBgz(FRJQ1k?N=#Ms5hLS4N>Hkp@;DNq zi4P)CHA2r9f>^64a&UcTQNP zMgGQb9WE0`PkQ2%yv#B$Tvf+oh*f~2oR*5LVW{CYiTKKWWxvc063qAecR>tXq; zN9y)__Lm!*N40W9StnhsM_L>)7c2eXgQeibb8hp6vp$)?>PjYiC7qV2sAdsPvcU_- zulPsLTou3XUHkn{Keof)T=NToGiRpSF|i+zb=;5B)|lwo4QPty&8;OR-uXz$|M zhG6FR#_GV8Ra}aq`BHJCb!$+pqNa%2;%9he${r9#`_MHI9QW~-uoqgofHFyDP-B?`O5T-teA%<*fgS_uW(f*rR*eHKktkomrR*M{;VB*ayhE zt1?svQfSvrk=4y{T@p6mr-vW8f3Gv&Z~<_Rw3=N+H&9WfMqfM+dF)T&==wRf`hEk) z#Yd{T`QQK8!3x-rVlEgK(ArI-Ojd6b_5zn0NTm(dxmnZy!za$_PrYz*1Ys|Vk@{Fk z9Y>A4RhnK0@1gCQ`_N7UJPuJK)}g?JWi7*&9ksuGtesq$3#*FEScfv7-aq(vWPDZs z{H3P#{<|w}Fc252?xjQ~9-p}6#&}&CyDi)+i|s?foy$D~VVS(JSYy){CcXxSB)jBP~tW&2(%+%3^={j8jEL z)_P=rIY<|`D1+~exR6n`C#y>NGY^%6H!peZ=g)dd%S{Su?j=IvXFwWgdKU%w`P}eC!dNpk=E{SVddSX&787=py0U`$sM&?w?I`mYD zEUYy^v&(Irt%pX8HUmRtO($@nXp=5Ws1-;VOUW52vryH2N#E$4n>YNgK6z37#tX-X z8!cNU55tZLFa0OxZYdxl#hZp?>(M<1+gMYoMAq@crGAFpEyHKgGDNGi)XDPVeP*Cd zCn^5cxRSY9xKAH#`zJ2Dv>RU_687^?%y;Mv)m-JPFVx+K_7v*7CLzW}3u$3ql2>DR zBK5v)hPq=+ah^NlHI7~hbZRSRZAluBfx-c$G9nYTi`Qm^FMR1b|H@Ot?&A;dbKif@ z&iclRt8jm&fXFLN$k~Z(MOtXKI#H@93KwA|L*KXR5oUDHRoa6+_a7(rA}67?mnfsw zsTn2u>VLhUefyQuLj+t@;&ehMdq^5WS!ecxdyVj}&AJH$SA%G=>gvAsxZd88)0X?j zMYqVZmf?z7URj$l-He!Dd$}RL`#`C&Yr-f~4rpgHbNZSeqRm|C4YWiRjxbQB@ms8; zM1CWnVrvRSWm4u8wqzrd4A#2=fDInKfRW_t*5zdaOLL`Qff&b(t%yWy#lPY=KM6}0 zEQuMwCO76w`u{Zh(T`nn8jtKNCzR*pda?b@H(KG9xy5AN8|`ox7esF0XI1=c`@!8M zrC>;fbn)biEh}1Ji@I!n=wQ(}v^lh%J>~i5W&)~Q(*-mKGBUO$sAbpZ>gI2KD_= zv*+XZYcHHPUOs-I(zYF?16;fdxahT)w&Zw8-E|jdH)ljVcnkpSdyK-dyl!Ki!3kenJlUeT9SzGPiaTM8rhIq& z14J}vRb>tqow%bf>i`ehi2A{;rUg<0`-@katm(vSiiPi8-01Cq@+e^^UY)9k_QQKh zjqMX!F;4BB7H4Brnb9FlRFu*u@0a5XGof?joabDc3yH2RD^fl!Oz8OJ6wEBgRsZx` zud3fTay;6%eS`C!LmS%=l9r`NR7(`4E z_(1420I_>KVfE~KYg_2 zw%tUt+}P~bN@5G_#q3vcg_WQ~yC;Ij*I#Q!d*==7@U8+hNs^gX16?zwygHC}jH@Li zDTk-0L;L0PF1uQf&0Zh_Td&hHlV3e?zWmCG3+3OfmA!j+Z?qpdI99)F)2J|9@H8Q| z%_#H~NRq`&;Us2>&2O8`j2yIZqKJJDU7XLwLNMmL*VcOeq!pdJ?o)VnM?V^C0KWn%_9y=l|FeqVb6xBqQ zRbePH-J+!w-bo&yXXCb+_^V1o* zfGPVoXqBB~YH<3xZ@+xOW7ir0GTiq{-s0-$nhY)Lg0|Y0{uj?3)xYuLNj^SQaS!gC za39`3Jhg4KsE$=cO_u^Sj%+Os1H5LE>yKE*tYO`I41Q;3sGW&uK4>ADo}wB7!?-bI zW-wS=OP`Kl4q5r*)R(rl5YZnEkm+YuUY9%q=PMab{6K*&)nA5~I*jaQp@VK$i%3zbee$QtQ2SjGX(EBLuqTo%QC z0(c&&nO}_bmkkpJ+5j<)Epr99 zxwhu5eDbiMsAw;_LKl@d^P5`?ckBf(nxzzU(YbF&F@R$S1rhRd*S-47XY3N?R`r9i zZi+T4yW$fsH^T#4jK%}IN+1jf8t=pUBOs#~m;)Pibr*t`&rS#KD;HdTr5@>kT$L6} ziDluBSxZdB0-BoBvg!G{`RCK~=9ABb!gwi(w{93s_HLWBc5fNBH;<0+vdIBxK9U6LE00wiVB5_rzG-w?R{Pjw#_~7|nyR=$@MmR43qY zoJ9Le?Bs%F`{`^}mhK~lr=2L8IyM~~IeIzJ6)_pD6@-bAO1O2yNU~|X(%3LsEe#bV z2QDID=|vL0d|a+Fae*N`U1a}UzU#qYW>n}hCTsnJQhw6*3!w++GP5s-V^9~*Z02979S%;ZI8S4Wh ztx4{9!G*py>m(3ENIe&CYU9_nO5IlD3kL}GG*;ONRTwjF5U;{_PU@}q9ilX2(SWOZ z=e=?d4j8*O80PUSjy>Io%FDGsiw|OuBLm8TVK9Z4sQsKjco!shfWOmP+&-o%+eVdm z-ZH#XQ-15z^+29!$8gmUQq7T+pY3$CL<<#?mnEqT8YB92=*eBIhaief$Il(T5(4;& zl)+Vkc4}m(B-Ki$HB>H!qr(-WXvz*EPFe-s0E1EkAwhyDTJX{N3&~v~?K&nPq@Wrk zQ()Kk8@A)a&^%Uc&mEd=Sk7F%?a#IBGJcI(PM}zBs8PNa3e|!rQz$gXPo_kHW@**N zJ7sSUTBPA3hY!4`V4^H}dnQ!Ab6ioXrc|aG7rk!OWr?-qy!CFXl9W`V;k^5QdZg|V zdvLYuP1j}HnC5I7(@OxVg?}SG^rlh0_VEYh))Ox^?7AHlZbZ39rRPABQCg4IAs5yM z-j>E544`WOU;`Rr_s1dN*~o!*oJ8s1UFN=B7D!lE;HI6?<^D1{ohcj5!~)auL&4~` zd(U#B6251n(R|;*ib>kBZtEc-cPFPASJhOKYx7a65n5$oOdj4>3`(GO_i=E@+D#%) zRa>N%iW0WOR5*8!kYIaNc-L-IxNn;tPBkMRK<0tLC_-SXVoirr5y}j)GbQAy=}DT0 zD5DgRHF`pW{@RM;u-%GFm+H-sCvylBj|ZEZI7z zbE61_v&?_PAlOKxOaeyu7{lJ?qh+TFydn03VO7p6hU`yLU6t+ z>yV1;v8v+Z8Oxp5>O6?mSS!lqk3LYd|LmptHs)TXm7WWK>%sd`UdP!NyJ5D?8pPz} z*VtEPF?b9B>>b{4q!l+5XdaZ&^Fo^0kgwiC2P!w(BV7zctsZ_~ST&FS&H}WE_73JJ zvC}&X>+Hn$0=ymrcu@)rCi%QuW<|>|xpm<0!@3LrKcA8^aHGLEIA-Bh-?XoJ1!YL}**3FjL_MZd=`}#L44H&}amH`gt)wKcz&@n! zty__r>lmdz*>aQtRse`OkXu;S8(~rI@$#hS)l}wT6Hr-4;s(3jITd{t@zux53e~YP z0I>mpy~FD@?5TC^#9b0>VhG}3*ic?FAkNL@Xz{0vE|&TxuCo;F=Io^k#7Kl6ALWjT zz5lMOBF)QWKLefK<3R)@OI|u_dDK2!shyN$Bo6PvJ*6}!E$(5-rL88^sMj2`{p{SN zf=-)$vRw-%4a~PG#=PhY&`KwlQ3FYLF_J^oAL@5raWuggkI@(tMA^B zz>S}T(iYrTmJMR@JvMyH)LXWV>UWygMT*X&ezsjajiMbaVgmqshYOkUGrDDmXkkS# z+2Cc;F_8zHAoR8^$i+Nif9YL#yf~^b9Zu*rep~9LM!$K{F}IKF)_6@VcPgh28e_HO zZ(gvQM^0Ll{Uolmn@4*sxqn~D9V#iyT8BBUUvMJPeSU77cE|?Sbpk5wvRl@!IHh31 zu$l#D9K|bDIBXvY85mH zQ)5NSjEQq_3nF1zi?DOPsl7_{3ve;K%TCQZV^_ubP%oeeMk)%>S&W2`OU8@y6Ri)7 zUGz~MYdvCOfoS#{N7T~Zje6_YC9l@6{g+t=Gv&$GTk3~p)&vW&((9i{KX-o0YeWB4 zPSm<^1hrGLSjZvrdFTPgj4i;tc@od*>nEhG!l!w^GN6{KG8pK2}oL z^1-}T7yhy+Sxa;=$U)RaA2j!XDx@VM)>u$qI^A|Z^1u+K@~RyhBIs)4*I@FC%%{G= zh21;#jl*ht|7MfoHHfJh;x)lIiBx}%fI0S@jJlCepHEB$bKJz{`F3?fPl_lU6J#L1kM*ftDo zTW%b+J&vGXUL@yu9B)k7m7UJh@@j>qRwA8`1g$b?q|j%CSu)syO4*R4q9$6yC9zo6 zrLba(3aVL?rf^wHIn&e;86)JpucJNBsrc~OrTJJm>*@YQryO2(N~#rWWdOgVib6*WV3-;Jk}m+-d&kh1B%YH+6oz zJ>-w7uCuk!;u}p{Gvrv0lBHJVOJY%QATeJ!^>7jrBm^!B6;m>{WG|G?L~wk55GM-d zWRXU$W&6SSrd`qGLwa#RmK-9tR<@^>rZ4g}4 zxG9F2cCXsoL;^sB4&->nw$L?~%h`(JkyDm2T2-20plM%!wds=2A!cW*YPmvi`|jUY zbcf6RTZbfkZ3l||^4u7h)=RZ;{Su+2)Zmr?LDaALMg6K_nP$iJViRiu29&@jv+HztA!ICtt1aPrL=wjUWl~d4uDMsP> zQhg%$fSR=ulkV@&i5OeQwBoJ}I<*1&wh|XidyxT$Yc4kJsQJL|QUT&?=h!8;yd1pJ zwz93R6K#I=zF{A*%13qlrkRTA`;Z1RumOO5548~24+~jO0u}u!BBd-WAO!MddmFd_ zbZGU4#u}IpA#`vtNjGPn=~%=P9Trk#H>IsY!|t>WH;pLm1Dj2=f9sGiAcj>>5PYWtyBsTIb<%kBBKcFKmxEV%dF2asd)}Z!gE8X4#8q^ zi`s{$4khKy)>?pn28gATM zolz5^r%^~POqeU*ztdEw>H%7)ydd>SMd-X&Y8Ae4%C5njxiQ}gO{zcpxV6}*?5M4H zXDp+Vg@ToCsH6`Kezgq@TfnbA?Z;*LV1vg1z`pD8i>+tAa}R)Fv-op(J-zXkd1-6Rlx$LU&q|hEoZWr3up1N`wzc;h{an#@Am%D^XnDO6EwKLcu1cI=_Lcc>rMV_(V%# zNvudG{A)>rCpGZpw~=Fc=LXN%htif`Zo>T*e=7763TWk8ZGq~v4iRYSBb9l@)?pK^ z!&mB~Wc??1`4gvA#okJz@$l#@EUsAUmAJvMB>+t8bh}!GBh*5}-1hhygpwp6S`;t_)#l$cM4KIxeC;2n|`x zdwlb76(!S+!A&{Smg+7yR8&H;pU7=Hk~PF9>j7R1v~~bA8_D(SDe%!oA1y~5k0_k5 zMuG|54p^7WBD0bbg|ZSDs6<<*tsx_28>X#QN!VnxS{qEe>?N7-j+&Anb(jwfPqm)7 zwiQ-UlHTcyN|=TylF{X4Mqy zZmq@#Yx6PMdqzr7XITp`bLrrsxJcULRDG3{z}aH9^tey;MODZ}r{Xn34Q?KZknlw( z4*E9$0;4ay7Wpz3d<2|;{+gyI5HD;W!N3{!XZDcMimf&io;4&BRlLU;}Yq2OLqOgvOKEP$xH_aK}bY(FS zmfMXnad)wfpO3xVag1lujI`y+J5?cpVhF&iDnW5aq3LzA4(S~eWASvmR=DaG+G@}1 zbXtiJ-xVI*Q;dj3c#{EhE?9@u<}~i4<}05)lCg4H2;vMH9oa!@*OtCvgu^cQtO#6K znjgkl*I%<9XHfuHEX$cS^bh~=fKopIFyWo2WKI_)S@f~ZCf`8V;7R0#=%-dfOF2*? z77c|jUQ2e19Zq%!-@1fWBBZvw!Poc$Fb9l5&Gst=x!5t9T?#50>+rS4^41|*VM0X- zybY$KQ6(mJHF=02KOd-3*s)^3F948aXR#HTpwP#BIg5{6Do(Yllz+L5*ui_hcm`~w zw}O)e2eqGTvY;8m8x_MOyGk7Ea!Vp=B&q-fGBB1jv?x^o6hMj-K#KXZFMTbx45WyP za@wj129QwP2exJgbD22x_+f91CFMUaf z0bhxSPJ@AH@VBx0Kl3>hwW20R5cpcX>CMYc zf4Q(@LTkgBzSzHYNQ+7wy<8v1>blrE6c(N8g-sg@xSO76e zYS)}}5_E22Q_*1yUiV7+TwoF*Ls@G7lvZKVuF|}C$tzP8rqe5-HL1cg(iv7~lI#re z1@cEx4zw|lHZD3@(?(i>b3cx-Evp>Y{I7pt!;jPo@^>5_zWi^#bj=ZjJCt-f z0IlW*k98|JfK9{h3m#sq6AD_lYrVFZ|C}xDie^R3vlUZAS&KtEREqRG$$j%ZiovfeZF0aY+-Lp(0PJ21jrmQ!vM*Se#t#Ov|Q z-!cs>!k@>XvFFSBReYffNmUhyK|Wi^cguXU1nAmFL{t8!2r0(~NIv${AKUzEU-*+t z|KS!R_Xdwa3-P-)6pZ!cl!vs1@BOGk2j-;P{J%8P{LRe6#41P~UJk@;dH5t3K;;Vo zbRE}y-luiwyw(_zyIY6dZ;8s(GwqTAXLVPXvn{lNU#&oDzg8bYmr4-xykjgjp}V8h zgtkdsQblxTU)P+XhuM;`e6F<0hYHLaS_N'PxPrfy%EJ-ktIc5N)!Vc_-p9MjAM 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 diff --git a/src-tauri/icons/android/values/ic_launcher_background.xml b/src-tauri/icons/android/values/ic_launcher_background.xml deleted file mode 100644 index ea9c223..0000000 --- a/src-tauri/icons/android/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #fff - \ No newline at end of file diff --git a/src-tauri/package.json b/src-tauri/package.json deleted file mode 100644 index f4a8a58..0000000 --- a/src-tauri/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "src-tauri", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "MPL-2.0", - "dependencies": { - "@tauri-apps/plugin-dialog": "~2.7.3" - } -} diff --git a/src-tauri/pnpm-lock.yaml b/src-tauri/pnpm-lock.yaml deleted file mode 100644 index f24c186..0000000 --- a/src-tauri/pnpm-lock.yaml +++ /dev/null @@ -1,29 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@tauri-apps/plugin-dialog': - specifier: ~2.7.3 - version: 2.7.3 - -packages: - - '@tauri-apps/api@2.11.1': - resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} - - '@tauri-apps/plugin-dialog@2.7.3': - resolution: {integrity: sha512-CRgE+7TP4tvq9MjBU6f04NLTFIqVMLKHk3hAqlhil00ngK9ACTrXPH3oHpKMProxILodd3YjBoKbMwSI4IEcfA==} - -snapshots: - - '@tauri-apps/api@2.11.1': {} - - '@tauri-apps/plugin-dialog@2.7.3': - dependencies: - '@tauri-apps/api': 2.11.1 diff --git a/src-tauri/src/device_watch.rs b/src-tauri/src/device_watch.rs deleted file mode 100644 index eab2f26..0000000 --- a/src-tauri/src/device_watch.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::kindle::{ - classify_reader, list_ebook_readers, list_mounted_readers, EbookReaderDevice, -}; -use futures::StreamExt; -use mtp_rs::mtp::{watch_devices, HotplugEvent, MtpDeviceInfo}; -use serde::Serialize; -use std::collections::HashMap; -use std::time::{Duration, Instant}; -use tauri::{AppHandle, Emitter}; -use tokio::time::{interval, sleep}; - -pub const EREADER_PRESENCE_EVENT: &str = "ereader-presence"; - -#[derive(Debug, Clone, Serialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum PresenceChange { - Arrived, - Left, - Snapshot, -} - -#[derive(Debug, Clone, Serialize)] -pub struct EreaderPresenceEvent { - pub change: PresenceChange, - pub label: Option, - pub device_id: Option, - pub devices: Vec, -} - -pub fn start_device_watcher(app: AppHandle) { - let app_mtp = app.clone(); - tauri::async_runtime::spawn(async move { - if let Err(e) = watch_mtp_presence(app_mtp).await { - eprintln!("Grimoire MTP device watch stopped: {e}"); - } - }); - - tauri::async_runtime::spawn(async move { - watch_mount_presence(app).await; - }); -} - -async fn watch_mtp_presence(app: AppHandle) -> Result<(), String> { - let mut watch = watch_devices().map_err(|e| e.to_string())?; - // Devices already connected are reported as Arrived when the stream starts. - // Treat the first second as bootstrap so those do not toast. - let bootstrap_until = Instant::now() + Duration::from_millis(1000); - let mut emitted_bootstrap = false; - - while let Some(event) = watch.next().await { - let (arrived, info) = match event { - HotplugEvent::Arrived(info) => (true, info), - HotplugEvent::Left(info) => (false, info), - }; - - if !mtp_info_is_ereader(&info) { - continue; - } - - let in_bootstrap = Instant::now() < bootstrap_until; - if in_bootstrap { - if !emitted_bootstrap { - sleep(Duration::from_millis(450)).await; - emit_presence(&app, PresenceChange::Snapshot, None, None).await; - emitted_bootstrap = true; - } - continue; - } - - // Give the device a moment to finish enumerating before we open it. - sleep(Duration::from_millis(450)).await; - - let label = device_label_from_mtp(&info); - let change = if arrived { - PresenceChange::Arrived - } else { - PresenceChange::Left - }; - - emit_presence(&app, change, Some(label), None).await; - } - - Ok(()) -} - -async fn watch_mount_presence(app: AppHandle) { - let mut known: HashMap = HashMap::new(); - let mut primed = false; - let mut tick = interval(Duration::from_secs(2)); - - loop { - tick.tick().await; - let mounts = match list_mounted_readers() { - Ok(list) => list, - Err(_) => continue, - }; - let current: HashMap = mounts - .iter() - .map(|d| (d.id.clone(), d.product.clone())) - .collect(); - - if !primed { - let had_mounts = !current.is_empty(); - known = current; - primed = true; - if had_mounts { - emit_presence(&app, PresenceChange::Snapshot, None, None).await; - } - continue; - } - - let arrived: Vec<(String, String)> = current - .iter() - .filter(|(id, _)| !known.contains_key(*id)) - .map(|(id, product)| (id.clone(), product.clone())) - .collect(); - let left: Vec<(String, String)> = known - .iter() - .filter(|(id, _)| !current.contains_key(*id)) - .map(|(id, product)| (id.clone(), product.clone())) - .collect(); - - for (id, product) in arrived { - emit_presence(&app, PresenceChange::Arrived, Some(product), Some(id)).await; - } - - for (id, product) in left { - emit_presence(&app, PresenceChange::Left, Some(product), Some(id)).await; - } - - known = current; - } -} - -fn mtp_info_is_ereader(info: &MtpDeviceInfo) -> bool { - let manufacturer = info.manufacturer.clone().unwrap_or_default(); - let product = info.product.clone().unwrap_or_default(); - classify_reader(&manufacturer, &product).is_some() -} - -fn device_label_from_mtp(info: &MtpDeviceInfo) -> String { - info.product - .clone() - .filter(|s| !s.is_empty()) - .unwrap_or_else(|| "eReader".to_string()) -} - -async fn emit_presence( - app: &AppHandle, - change: PresenceChange, - label: Option, - device_id: Option, -) { - let devices = match list_ebook_readers().await { - Ok(list) => list, - Err(e) => { - eprintln!("Grimoire could not refresh eReaders after presence change: {e}"); - Vec::new() - } - }; - - let resolved_id = device_id.or_else(|| { - label.as_ref().and_then(|name| { - devices - .iter() - .find(|d| d.product.eq_ignore_ascii_case(name)) - .map(|d| d.id.clone()) - }) - }); - - let payload = EreaderPresenceEvent { - change, - label, - device_id: resolved_id, - devices, - }; - - if let Err(e) = app.emit(EREADER_PRESENCE_EVENT, payload) { - eprintln!("Grimoire failed to emit eReader presence event: {e}"); - } -} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs deleted file mode 100644 index ba70a15..0000000 --- a/src-tauri/src/lib.rs +++ /dev/null @@ -1,440 +0,0 @@ -mod comic; -mod config; -mod convert; -mod db; -mod device_watch; -mod error; -mod kf8_extract; -mod kindle; -mod library; -mod metadata; -mod openlibrary; -mod reader; -mod state; - -use comic::{comic_page_data_url, list_comic_image_names, ComicPage, ComicPageList}; -use config::{clear_library_path, load_config, set_library_path, AppConfig}; -use convert::{ - convert_book_format, kindle_convert_available, list_convertible_targets, resolve_kindle_file, - ConvertResult, KindleReadyFile, -}; -use device_watch::start_device_watcher; -use error::{err, AppResult}; -use kindle::{ - delete_device_books, list_ebook_readers, list_kindles, list_sync_records, record_sync, - scan_device_library, send_file_to_kindle, DeviceBookTarget, DeviceBulkResult, DeviceLibrary, - EbookReaderDevice, KindleDeviceInfo, SyncRecord, TransferResult, -}; -use library::{ - apply_remote_match, bulk_patch_ebooks, cover_data_url, create_library, delete_book, - delete_ebooks, format_file_path, get_book, import_book, list_books, open_library, set_book_cover, - update_book, BookBulkPatch, BookDetail, BookSummary, BookUpdate, BulkActionResult, - EnrichmentResult, -}; -use openlibrary::{ - cover_candidates_from_matches, download_cover_from_urls, lookup_book, lookup_book_candidates, - CoverCandidate, -}; -use reader::{ - get_reading_progress, resolve_reader_source, save_reading_progress, ReaderSource, - ReadingProgress, ReadingProgressUpdate, -}; -use state::AppState; -use std::path::{Path, PathBuf}; -use tauri::State; - -#[tauri::command] -fn get_config() -> AppResult { - load_config() -} - -#[tauri::command] -fn get_library_status(state: State<'_, AppState>) -> AppResult { - let config = load_config()?; - Ok(LibraryStatus { - open: state.is_open(), - path: config.library_path, - kindle_convert_available: kindle_convert_available(), - }) -} - -#[derive(serde::Serialize)] -struct LibraryStatus { - open: bool, - path: Option, - kindle_convert_available: bool, -} - -#[tauri::command] -fn create_or_open_library(state: State<'_, AppState>, path: String) -> AppResult { - let root = PathBuf::from(&path); - let session = if root.join("metadata.db").exists() { - open_library(&root)? - } else { - create_library(&root)? - }; - set_library_path(&root)?; - state.set_session(session); - get_library_status(state) -} - -#[tauri::command] -fn open_saved_library(state: State<'_, AppState>) -> AppResult { - let config = load_config()?; - let Some(path) = config.library_path else { - return get_library_status(state); - }; - let root = PathBuf::from(&path); - if !root.exists() { - return get_library_status(state); - } - let session = if root.join("metadata.db").exists() { - open_library(&root)? - } else { - // Saved folder exists but has no library yet; create one so startup stays automatic. - create_library(&root)? - }; - state.set_session(session); - get_library_status(state) -} - -#[tauri::command] -fn clear_saved_library(state: State<'_, AppState>) -> AppResult { - clear_library_path()?; - state.clear_session(); - get_library_status(state) -} - -#[tauri::command] -fn import_ebook(state: State<'_, AppState>, path: String) -> AppResult { - state.with_conn_mut(|conn, root| import_book(conn, root, Path::new(&path))) -} - -#[tauri::command] -async fn enrich_ebook_metadata( - state: State<'_, AppState>, - id: i64, -) -> AppResult { - let (title, authors, isbn, has_cover) = state.with_conn(|conn, root| { - let book = get_book(conn, root, id)?; - let isbn = book - .identifiers - .iter() - .find(|i| i.type_name.eq_ignore_ascii_case("isbn")) - .map(|i| i.value.clone()); - Ok((book.title, book.authors, isbn, book.has_cover)) - })?; - - let hit = lookup_book(&title, &authors, isbn.as_deref()) - .await? - .ok_or_else(|| err("No Open Library match found for this title/author/ISBN."))?; - - let cover_bytes = if !has_cover { - match download_cover_from_urls(&hit.cover_urls).await { - Ok(bytes) => Some(bytes), - Err(e) => { - eprintln!("Grimoire cover download skipped: {e}"); - None - } - } - } else { - None - }; - - state.with_conn_mut(|conn, root| { - let current = get_book(conn, root, id)?; - apply_remote_match(conn, root, id, ¤t, &hit, cover_bytes.as_deref()) - }) -} - -#[tauri::command] -async fn list_ebook_cover_candidates( - state: State<'_, AppState>, - id: i64, -) -> AppResult> { - let (title, authors, isbn) = state.with_conn(|conn, root| { - let book = get_book(conn, root, id)?; - let isbn = book - .identifiers - .iter() - .find(|i| i.type_name.eq_ignore_ascii_case("isbn")) - .map(|i| i.value.clone()); - Ok((book.title, book.authors, isbn)) - })?; - - let matches = lookup_book_candidates(&title, &authors, isbn.as_deref(), 12).await?; - let candidates = cover_candidates_from_matches(&matches); - if candidates.is_empty() { - return Err(err( - "No cover images found on Open Library for this title/author/ISBN.", - )); - } - Ok(candidates) -} - -#[tauri::command] -async fn set_ebook_cover_from_urls( - state: State<'_, AppState>, - id: i64, - urls: Vec, -) -> AppResult { - if urls.is_empty() { - return Err(err("Pick a cover first.")); - } - // Confirm the book exists before downloading. - state.with_conn(|conn, root| { - let _ = get_book(conn, root, id)?; - Ok(()) - })?; - - let bytes = download_cover_from_urls(&urls).await?; - state.with_conn_mut(|conn, root| set_book_cover(conn, root, id, &bytes)) -} - -#[tauri::command] -fn list_ebooks(state: State<'_, AppState>, query: Option) -> AppResult> { - state.with_conn(|conn, _| list_books(conn, query.as_deref())) -} - -#[tauri::command] -fn get_ebook(state: State<'_, AppState>, id: i64) -> AppResult { - state.with_conn(|conn, root| get_book(conn, root, id)) -} - -#[tauri::command] -fn update_ebook(state: State<'_, AppState>, id: i64, update: BookUpdate) -> AppResult { - state.with_conn_mut(|conn, root| update_book(conn, root, id, update)) -} - -#[tauri::command] -fn delete_ebook(state: State<'_, AppState>, id: i64) -> AppResult<()> { - state.with_conn_mut(|conn, root| delete_book(conn, root, id)) -} - -#[tauri::command] -fn delete_ebooks_batch(state: State<'_, AppState>, ids: Vec) -> AppResult { - state.with_conn_mut(|conn, root| delete_ebooks(conn, root, &ids)) -} - -#[tauri::command] -fn bulk_patch_ebooks_cmd( - state: State<'_, AppState>, - ids: Vec, - patch: BookBulkPatch, -) -> AppResult { - state.with_conn_mut(|conn, root| bulk_patch_ebooks(conn, root, &ids, &patch)) -} - -#[tauri::command] -fn get_cover(state: State<'_, AppState>, id: i64) -> AppResult> { - state.with_conn(|conn, root| cover_data_url(conn, root, id)) -} - -#[tauri::command] -fn get_reader_source( - state: State<'_, AppState>, - book_id: i64, - format: Option, -) -> AppResult { - state.with_conn_mut(|conn, root| { - resolve_reader_source(conn, root, book_id, format.as_deref()) - }) -} - -#[tauri::command] -fn list_comic_pages( - state: State<'_, AppState>, - book_id: i64, -) -> AppResult { - state.with_conn(|conn, root| { - let source = { - // Prefer CBZ then CBR if both somehow exist. - let formats: Vec = { - let mut stmt = conn.prepare("SELECT format FROM formats WHERE book = ?1")?; - let rows = stmt.query_map([book_id], |row| row.get(0))?; - rows.collect::, _>>()? - }; - let format = ["CBZ", "CBR"] - .iter() - .find(|f| formats.iter().any(|have| have == **f)) - .ok_or_else(|| err("This book has no CBZ or CBR format."))?; - let path = format_file_path(conn, root, book_id, format)?; - (*format, path) - }; - let pages = list_comic_image_names(&source.1)?; - Ok(ComicPageList { - book_id, - format: source.0.to_string(), - page_count: pages.len(), - pages, - }) - }) -} - -#[tauri::command] -fn get_comic_page( - state: State<'_, AppState>, - book_id: i64, - index: usize, -) -> AppResult { - state.with_conn(|conn, root| { - let formats: Vec = { - let mut stmt = conn.prepare("SELECT format FROM formats WHERE book = ?1")?; - let rows = stmt.query_map([book_id], |row| row.get(0))?; - rows.collect::, _>>()? - }; - let format = ["CBZ", "CBR"] - .iter() - .find(|f| formats.iter().any(|have| have == **f)) - .ok_or_else(|| err("This book has no CBZ or CBR format."))?; - let path = format_file_path(conn, root, book_id, format)?; - let (name, data_url, page_count) = comic_page_data_url(&path, index)?; - Ok(ComicPage { - book_id, - index, - page_count, - name, - data_url, - }) - }) -} - -#[tauri::command] -fn list_ebook_convert_targets( - state: State<'_, AppState>, - book_id: i64, -) -> AppResult> { - state.with_conn(|conn, _| list_convertible_targets(conn, book_id)) -} - -#[tauri::command] -fn convert_ebook_format( - state: State<'_, AppState>, - book_id: i64, - target_format: String, -) -> AppResult { - state.with_conn_mut(|conn, root| convert_book_format(conn, root, book_id, &target_format)) -} - -#[tauri::command] -fn get_ebook_reading_progress( - state: State<'_, AppState>, - book_id: i64, -) -> AppResult> { - state.with_conn(|conn, _| get_reading_progress(conn, book_id)) -} - -#[tauri::command] -fn save_ebook_reading_progress( - state: State<'_, AppState>, - book_id: i64, - update: ReadingProgressUpdate, -) -> AppResult { - state.with_conn(|conn, _| save_reading_progress(conn, book_id, update)) -} - -#[tauri::command] -fn prepare_kindle_file(state: State<'_, AppState>, book_id: i64) -> AppResult { - state.with_conn_mut(|conn, root| resolve_kindle_file(conn, root, book_id)) -} - -#[tauri::command] -async fn list_kindle_devices() -> AppResult> { - list_kindles().await -} - -#[tauri::command] -async fn list_ebook_reader_devices() -> AppResult> { - list_ebook_readers().await -} - -#[tauri::command] -async fn scan_ebook_reader(device_id: String) -> AppResult { - scan_device_library(device_id).await -} - -#[tauri::command] -async fn delete_ebook_reader_books( - device_id: String, - books: Vec, -) -> AppResult { - delete_device_books(device_id, books).await -} - -#[tauri::command] -async fn send_book_to_kindle( - state: State<'_, AppState>, - book_id: i64, - device_id: String, -) -> AppResult { - let ready = state.with_conn_mut(|conn, root| resolve_kindle_file(conn, root, book_id))?; - let local = PathBuf::from(&ready.path); - let remote_name = local - .file_name() - .map(|s| s.to_string_lossy().to_string()) - .unwrap_or_else(|| format!("book.{}", ready.format.to_ascii_lowercase())); - - let (remote_path, serial) = - send_file_to_kindle(&device_id, &local, &remote_name).await?; - - state.with_conn(|conn, _| { - record_sync(conn, book_id, &serial, &remote_path, &ready.format) - })?; - - Ok(TransferResult { - remote_path, - device_serial: serial, - format: ready.format, - }) -} - -#[tauri::command] -fn get_sync_history(state: State<'_, AppState>) -> AppResult> { - state.with_conn(|conn, _| list_sync_records(conn)) -} - -#[cfg_attr(mobile, tauri::mobile_entry_point)] -pub fn run() { - tauri::Builder::default() - .plugin(tauri_plugin_opener::init()) - .plugin(tauri_plugin_dialog::init()) - .manage(AppState::new()) - .setup(|app| { - start_device_watcher(app.handle().clone()); - Ok(()) - }) - .invoke_handler(tauri::generate_handler![ - get_config, - get_library_status, - create_or_open_library, - open_saved_library, - clear_saved_library, - import_ebook, - enrich_ebook_metadata, - list_ebook_cover_candidates, - set_ebook_cover_from_urls, - list_ebooks, - get_ebook, - update_ebook, - delete_ebook, - delete_ebooks_batch, - bulk_patch_ebooks_cmd, - get_cover, - get_reader_source, - list_comic_pages, - get_comic_page, - get_ebook_reading_progress, - save_ebook_reading_progress, - list_ebook_convert_targets, - convert_ebook_format, - prepare_kindle_file, - list_kindle_devices, - list_ebook_reader_devices, - scan_ebook_reader, - delete_ebook_reader_books, - send_book_to_kindle, - get_sync_history, - ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json deleted file mode 100644 index 9b4673a..0000000 --- a/src-tauri/tauri.conf.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "$schema": "https://schema.tauri.app/config/2", - "productName": "Grimoire", - "mainBinaryName": "Grimoire", - "version": "0.1.9", - "identifier": "com.boxingoctopus.grimoire", - "build": { - "beforeDevCommand": "pnpm dev", - "devUrl": "http://localhost:1420", - "beforeBuildCommand": "pnpm build", - "frontendDist": "../build" - }, - "app": { - "macOSPrivateApi": true, - "windows": [ - { - "title": "Grimoire", - "width": 1280, - "height": 840, - "minWidth": 900, - "minHeight": 600, - "decorations": false, - "transparent": true, - "shadow": true - } - ], - "security": { - "csp": null, - "assetProtocol": { - "enable": true, - "scope": [ - "**" - ] - } - } - }, - "bundle": { - "active": true, - "targets": "all", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "macOS": { - "bundleName": "Grimoire" - }, - "windows": { - "nsis": { - "installerHooks": "./windows/hooks.nsh", - "installMode": "currentUser" - }, - "wix": { - "fragmentPaths": [ - "./windows/fragments/library-path.wxs" - ] - } - } - } -} diff --git a/src-tauri/windows/fragments/library-path.wxs b/src-tauri/windows/fragments/library-path.wxs deleted file mode 100644 index c402d8c..0000000 --- a/src-tauri/windows/fragments/library-path.wxs +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - NOT Installed - - - diff --git a/src-tauri/windows/hooks.nsh b/src-tauri/windows/hooks.nsh deleted file mode 100644 index 335464d..0000000 --- a/src-tauri/windows/hooks.nsh +++ /dev/null @@ -1,31 +0,0 @@ -# Capture this file's directory at include time (macro expansion uses a different path). -!define GRIMOIRE_HOOK_DIR "${__FILEDIR__}" - -Var GrimoireLibraryDir -Var GrimoireLibraryArgs - -!macro NSIS_HOOK_POSTINSTALL - ; Default ebook library location for new installs. - StrCpy $GrimoireLibraryDir "$DOCUMENTS\Grimoire" - StrCpy $GrimoireLibraryArgs '-LibraryPath "$GrimoireLibraryDir"' - - ; Optional silent / scripted override: - ; Grimoire_x64-setup.exe /LIBRARYPATH=D:\Books - ${GetOptions} $CMDLINE "/LIBRARYPATH=" $0 - ${IfNot} ${Errors} - ${AndIf} $0 != "" - StrCpy $GrimoireLibraryDir $0 - StrCpy $GrimoireLibraryArgs '-LibraryPath "$GrimoireLibraryDir"' - ${ElseIf} $PassiveMode != 1 - ${AndIfNot} ${Silent} - ; Interactive installs can choose a different library folder. - StrCpy $GrimoireLibraryArgs "-Prompt" - ${EndIf} - - ; Pack and run the helper so JSON escaping stays correct for Windows paths. - SetOutPath "$PLUGINSDIR" - File "/oname=set-library-path.ps1" "${GRIMOIRE_HOOK_DIR}\set-library-path.ps1" - nsExec::ExecToLog 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$PLUGINSDIR\set-library-path.ps1" $GrimoireLibraryArgs' - Pop $0 - DetailPrint "Grimoire library path setup exit code: $0" -!macroend diff --git a/src-tauri/windows/set-library-path.ps1 b/src-tauri/windows/set-library-path.ps1 deleted file mode 100644 index 1fe1a69..0000000 --- a/src-tauri/windows/set-library-path.ps1 +++ /dev/null @@ -1,41 +0,0 @@ -param( - [Parameter(Mandatory = $false)] - [string]$LibraryPath, - - [Parameter(Mandatory = $false)] - [switch]$Prompt -) - -$ErrorActionPreference = 'Stop' - -if ($Prompt) { - Add-Type -AssemblyName System.Windows.Forms - $dialog = New-Object System.Windows.Forms.FolderBrowserDialog - $dialog.Description = 'Choose where Grimoire should store your ebook library' - $dialog.SelectedPath = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'Grimoire' - $dialog.ShowNewFolderButton = $true - if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK -and $dialog.SelectedPath) { - $LibraryPath = $dialog.SelectedPath - } -} - -if ([string]::IsNullOrWhiteSpace($LibraryPath)) { - $LibraryPath = Join-Path ([Environment]::GetFolderPath('MyDocuments')) 'Grimoire' -} - -New-Item -ItemType Directory -Force -Path $LibraryPath | Out-Null - -$cfgDir = Join-Path $env:APPDATA 'grimoire' -New-Item -ItemType Directory -Force -Path $cfgDir | Out-Null - -$cfgPath = Join-Path $cfgDir 'config.json' -if (Test-Path -LiteralPath $cfgPath) { - Write-Host "Grimoire config already exists at $cfgPath; leaving library_path unchanged." - exit 0 -} - -# UTF-8 without BOM. Windows PowerShell 5.1's Set-Content -Encoding utf8 adds a -# BOM that serde_json rejects ("expected value at line 1 column 1"). -$json = @{ library_path = $LibraryPath } | ConvertTo-Json -[System.IO.File]::WriteAllText($cfgPath, $json, [System.Text.UTF8Encoding]::new($false)) -Write-Host "Wrote initial Grimoire library path: $LibraryPath" diff --git a/src/app.css b/src/app.css deleted file mode 100644 index 76f0737..0000000 --- a/src/app.css +++ /dev/null @@ -1,458 +0,0 @@ -:root, -[data-theme='light'] { - --ink: #1c1814; - --ink-muted: #5c534a; - --paper: #f3ece0; - --paper-deep: #e6dccb; - --panel: #faf6ee; - --accent: #2f5d50; - --accent-hover: #244a40; - --danger: #8b3a2f; - --line: rgba(28, 24, 20, 0.12); - --shadow: 0 12px 40px rgba(28, 24, 20, 0.08); - --topbar-bg: rgba(250, 246, 238, 0.82); - --control-bg: #fffdf8; - --control-hover: rgba(28, 24, 20, 0.05); - --cover-from: #d9d0bf; - --cover-to: #c4b8a2; - --page-bg: - radial-gradient(circle at 12% 0%, rgba(47, 93, 80, 0.08), transparent 42%), - radial-gradient(circle at 88% 8%, rgba(139, 58, 47, 0.06), transparent 36%), - linear-gradient(180deg, #f7f1e6 0%, var(--paper) 48%, #ebe2d2 100%); - --btn-fg: #f8f5ef; - --radius: 10px; - --window-radius: 14px; - --font-display: 'Fraunces', 'Palatino Linotype', Palatino, Georgia, serif; - --font-body: 'Source Serif 4', Georgia, 'Times New Roman', serif; - --font-ui: 'Source Sans 3', 'Segoe UI', sans-serif; - --font-opendyslexic: 'OpenDyslexic', 'Source Sans 3', sans-serif; - color: var(--ink); - font-family: var(--font-body); - font-size: 16px; - line-height: 1.5; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; -} - -[data-theme='dark'] { - --ink: #efe6d8; - --ink-muted: #b0a493; - --paper: #1a1714; - --paper-deep: #12100e; - --panel: #24201c; - --accent: #7fb5a4; - --accent-hover: #9ac9ba; - --danger: #d88476; - --line: rgba(239, 230, 216, 0.12); - --shadow: 0 12px 40px rgba(0, 0, 0, 0.35); - --topbar-bg: rgba(26, 23, 20, 0.88); - --control-bg: #2c2823; - --control-hover: rgba(239, 230, 216, 0.08); - --cover-from: #3a342c; - --cover-to: #2a2520; - --page-bg: - radial-gradient(circle at 14% 0%, rgba(127, 181, 164, 0.1), transparent 40%), - radial-gradient(circle at 90% 10%, rgba(216, 132, 118, 0.08), transparent 34%), - linear-gradient(180deg, #1f1b17 0%, var(--paper) 50%, #141210 100%); - --btn-fg: #1a1714; -} - -* { - box-sizing: border-box; -} - -@font-face { - font-family: 'OpenDyslexic'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('/fonts/opendyslexic/OpenDyslexic-Regular.woff2') format('woff2'); -} - -@font-face { - font-family: 'OpenDyslexic'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url('/fonts/opendyslexic/OpenDyslexic-Italic.woff2') format('woff2'); -} - -@font-face { - font-family: 'OpenDyslexic'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('/fonts/opendyslexic/OpenDyslexic-Bold.woff2') format('woff2'); -} - -@font-face { - font-family: 'OpenDyslexic'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url('/fonts/opendyslexic/OpenDyslexic-BoldItalic.woff2') format('woff2'); -} - -html, -body { - margin: 0; - height: 100%; - overflow: hidden; - background: transparent; -} - -body { - min-height: 100%; - color: var(--ink); - transition: color 180ms ease; -} - -a { - color: inherit; - text-decoration: none; -} - -button, -input, -textarea { - font: inherit; -} - -button { - cursor: pointer; -} - -img { - max-width: 100%; - display: block; -} - -.app-shell { - height: 100vh; - display: grid; - grid-template-rows: auto 1fr; - background: var(--page-bg); - border-radius: var(--window-radius); - overflow: hidden; -} - -html.window-maximized .app-shell { - border-radius: 0; -} - -.shell-body { - min-height: 0; - overflow: auto; - overflow-x: hidden; - display: flex; - flex-direction: column; -} - -.topbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.75rem; - padding: 0.55rem 0.75rem 0.55rem 1.1rem; - border-bottom: 1px solid var(--line); - backdrop-filter: blur(8px); - background: var(--topbar-bg); - position: relative; - z-index: 20; -} - -.topbar[data-chrome='macos'] { - padding-left: 0.85rem; - gap: 0.65rem; -} - -.topbar[data-chrome='windows'] { - padding-right: 0; - padding-top: 0; - padding-bottom: 0; - min-height: 2.75rem; -} - -.topbar[data-chrome='linux'] { - padding-right: 0.35rem; -} - -.topbar-drag { - flex: 1 1 auto; - align-self: stretch; - min-width: 1rem; - cursor: default; -} - -.topbar-end { - display: flex; - align-items: center; - gap: 0.5rem; - flex: 0 0 auto; -} - -.topbar[data-chrome='windows'] .topbar-end { - padding-block: 0.55rem; -} - -.topbar[data-chrome='windows'] .brand { - padding-block: 0.55rem; -} - -.topbar .brand { - flex: 0 0 auto; -} - -.brand { - display: flex; - align-items: center; - gap: 0.5rem; - font-family: var(--font-display); - font-size: 1.55rem; - font-weight: 600; - letter-spacing: -0.02em; - filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.28)) drop-shadow(0 2px 6px rgba(0, 0, 0, 0.22)); -} - -.brand-logo { - display: block; - height: 1.85rem; - width: auto; -} - -.brand-text { - display: inline; -} - -.brand-dot { - color: var(--accent); -} - -.nav { - display: flex; - align-items: center; - gap: 0.35rem; - font-family: var(--font-ui); -} - -.nav a { - position: relative; - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 2.1rem; - min-height: 2.1rem; - padding: 0.45rem; - border-radius: 999px; - color: var(--ink-muted); - transition: background 160ms ease, color 160ms ease; -} - -.nav a:hover, -.nav a.active { - background: color-mix(in srgb, var(--accent) 16%, transparent); - color: var(--accent); -} - -.topbar [data-tooltip], -.library-menubar [data-tooltip] { - position: relative; -} - -.topbar [data-tooltip]::after, -.library-menubar [data-tooltip]::after { - content: attr(data-tooltip); - position: absolute; - top: calc(100% + 0.45rem); - left: 50%; - transform: translateX(-50%) translateY(-2px); - padding: 0.28rem 0.5rem; - border-radius: 6px; - border: 1px solid var(--line); - background: var(--panel); - color: var(--ink); - font-family: var(--font-ui); - font-size: 0.75rem; - font-weight: 600; - line-height: 1.2; - white-space: nowrap; - box-shadow: var(--shadow); - opacity: 0; - pointer-events: none; - transition: opacity 120ms ease, transform 120ms ease; - z-index: 40; -} - -.topbar [data-tooltip]:hover::after, -.topbar [data-tooltip]:focus-visible::after, -.library-menubar [data-tooltip]:hover:not(:disabled)::after, -.library-menubar [data-tooltip]:focus-visible:not(:disabled)::after { - opacity: 1; - transform: translateX(-50%) translateY(0); -} - -.page { - padding: 1.4rem clamp(1rem, 3vw, 2rem) 2.5rem; - width: min(1200px, 100%); - margin: 0 auto; -} - -.panel { - background: var(--panel); - border: 1px solid var(--line); - border-radius: var(--radius); - box-shadow: var(--shadow); -} - -.btn { - font-family: var(--font-ui); - border: 1px solid transparent; - border-radius: 8px; - padding: 0.55rem 1rem; - background: var(--accent); - color: var(--btn-fg); - transition: background 160ms ease, transform 120ms ease; - display: inline-flex; - align-items: center; - justify-content: center; - text-decoration: none; -} - -.btn:hover { - background: var(--accent-hover); -} - -.btn:active { - transform: translateY(1px); -} - -.btn.secondary { - background: transparent; - border-color: var(--line); - color: var(--ink); -} - -.btn.secondary:hover { - background: var(--control-hover); -} - -.btn.danger { - background: var(--danger); - color: #fff8f5; -} - -.btn:disabled { - opacity: 0.55; - cursor: not-allowed; -} - -.field { - display: flex; - flex-direction: column; - gap: 0.35rem; - font-family: var(--font-ui); -} - -.field input, -.field textarea { - border: 1px solid var(--line); - border-radius: 8px; - padding: 0.55rem 0.7rem; - background: var(--control-bg); - color: var(--ink); -} - -.muted { - color: var(--ink-muted); -} - -.error { - color: var(--danger); - font-family: var(--font-ui); - font-size: 0.95rem; -} - -.toolbar { - display: flex; - flex-wrap: wrap; - gap: 0.75rem; - align-items: center; - margin-bottom: 1.25rem; -} - -.search { - flex: 1 1 220px; - min-width: 180px; - font-family: var(--font-ui); - border: 1px solid var(--line); - border-radius: 8px; - padding: 0.55rem 0.8rem; - background: var(--control-bg); - color: var(--ink); -} - -.grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); - gap: 1.1rem; -} - -.cover { - position: relative; - aspect-ratio: 2 / 3; - border-radius: 6px; - background: linear-gradient(160deg, var(--cover-from), var(--cover-to)); - overflow: hidden; - box-shadow: 0 8px 18px color-mix(in srgb, var(--ink) 12%, transparent); - transition: transform 180ms ease, box-shadow 180ms ease; -} - -.cover img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.book-card:hover .cover { - transform: translateY(-3px); - box-shadow: 0 14px 28px color-mix(in srgb, var(--ink) 18%, transparent); -} - -.book-card h3 { - font-family: var(--font-display); - font-size: 1rem; - margin: 0.65rem 0 0.2rem; - line-height: 1.25; -} - -.book-card p { - margin: 0; - font-size: 0.9rem; - color: var(--ink-muted); -} - -.empty { - padding: 3rem 1.5rem; - text-align: center; -} - -.empty h2 { - font-family: var(--font-display); - margin: 0 0 0.5rem; -} - -@keyframes fade-up { - from { - opacity: 0; - transform: translateY(8px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.fade-up { - animation: fade-up 280ms ease both; -} diff --git a/src/app.html b/src/app.html deleted file mode 100644 index 53c1a8e..0000000 --- a/src/app.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - Grimoire - - %sveltekit.head% - - -
%sveltekit.body%
- - diff --git a/src/epubjs.d.ts b/src/epubjs.d.ts deleted file mode 100644 index 4ef63a6..0000000 --- a/src/epubjs.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -declare module 'epubjs' { - const ePub: (url: string | ArrayBuffer, options?: Record) => { - renderTo: ( - element: HTMLElement | string, - options?: Record - ) => { - display: (target?: string) => Promise; - prev: () => Promise; - next: () => Promise; - on: (event: string, callback: (...args: unknown[]) => void) => void; - currentLocation: () => { - start?: { cfi?: string; displayed?: { page?: number; total?: number } }; - atEnd?: boolean; - }; - destroy?: () => void; - }; - locations: { - generate: (chars: number) => Promise; - locationFromCfi: (cfi: string) => number; - length: () => number; - }; - destroy?: () => void; - }; - export default ePub; -} diff --git a/src/lib/CoverPickerModal.svelte b/src/lib/CoverPickerModal.svelte deleted file mode 100644 index 2775453..0000000 --- a/src/lib/CoverPickerModal.svelte +++ /dev/null @@ -1,248 +0,0 @@ - - -
- -

- Pick the edition that matches your book. Covers come from Open Library. -

- - {#if error} -

{error}

- {/if} - -
- {#if loading} -

Loading covers…

- {:else if candidates.length === 0 && !error} -

No cover candidates found for this book.

- {:else} -
- {#each candidates as candidate (candidate.id)} - - {/each} -
- {/if} -
- - {#snippet footer()} - - - {/snippet} -
-
- - diff --git a/src/lib/LibraryFilters.svelte b/src/lib/LibraryFilters.svelte deleted file mode 100644 index 6521993..0000000 --- a/src/lib/LibraryFilters.svelte +++ /dev/null @@ -1,332 +0,0 @@ - - - - - diff --git a/src/lib/ThemeToggle.svelte b/src/lib/ThemeToggle.svelte deleted file mode 100644 index 65d263e..0000000 --- a/src/lib/ThemeToggle.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - - - - diff --git a/src/lib/WindowControls.svelte b/src/lib/WindowControls.svelte deleted file mode 100644 index ef13bee..0000000 --- a/src/lib/WindowControls.svelte +++ /dev/null @@ -1,313 +0,0 @@ - - -{#if os === 'macos'} -
- - - -
-{:else} -
- - - -
-{/if} - - diff --git a/src/lib/api.ts b/src/lib/api.ts deleted file mode 100644 index e1bf007..0000000 --- a/src/lib/api.ts +++ /dev/null @@ -1,348 +0,0 @@ -import { invoke } from '@tauri-apps/api/core'; - -export type AppConfig = { - library_path: string | null; -}; - -export type LibraryStatus = { - open: boolean; - path: string | null; - kindle_convert_available: boolean; -}; - -export type BookSummary = { - id: number; - uuid: string; - title: string; - authors: string[]; - tags: string[]; - formats: string[]; - has_cover: boolean; - series: string | null; - series_index: number | null; - /** Null when the book has never been opened in the reader. */ - progress_percent: number | null; -}; - -export type FormatInfo = { - format: string; - filename: string; - size: number; -}; - -export type IdentifierInfo = { - type_name: string; - value: string; -}; - -export type BookDetail = { - id: number; - uuid: string; - title: string; - authors: string[]; - tags: string[]; - formats: FormatInfo[]; - has_cover: boolean; - series: string | null; - series_index: number | null; - comment: string; - identifiers: IdentifierInfo[]; - path: string; -}; - -export type BookUpdate = { - title: string; - authors: string[]; - tags: string[]; - series: string | null; - series_index: number | null; - comment: string; - identifiers?: IdentifierInfo[]; -}; - -export type ReaderFamily = - | 'kindle' - | 'kobo' - | 'pocketbook' - | 'remarkable' - | 'boox' - | 'tolino' - | 'nook' - | 'other'; - -export type ConnectionKind = 'mtp' | 'mount'; - -export type EbookReaderDevice = { - id: string; - connection: ConnectionKind; - location_id: number | null; - mount_path: string | null; - manufacturer: string; - product: string; - serial_number: string | null; - family: ReaderFamily; - family_label: string; - free_space_bytes: number | null; - display: string; - supports_send: boolean; - preferred_formats: string[]; - notes: string | null; -}; - -export type KindleDeviceInfo = { - location_id: number; - manufacturer: string; - product: string; - serial_number: string | null; - is_kindle: boolean; - free_space_bytes: number | null; - display: string; -}; - -export type TransferResult = { - remote_path: string; - device_serial: string; - format: string; -}; - -export type SyncRecord = { - book_id: number; - device_serial: string; - last_sent_at: string; - remote_path: string; - format: string; -}; - -export async function getConfig() { - return invoke('get_config'); -} - -export async function getLibraryStatus() { - return invoke('get_library_status'); -} - -export async function openSavedLibrary() { - return invoke('open_saved_library'); -} - -export async function createOrOpenLibrary(path: string) { - return invoke('create_or_open_library', { path }); -} - -export async function clearSavedLibrary() { - return invoke('clear_saved_library'); -} - -export async function listEbooks(query?: string) { - return invoke('list_ebooks', { query: query ?? null }); -} - -export async function importEbook(path: string) { - return invoke('import_ebook', { path }); -} - -export async function getEbook(id: number) { - return invoke('get_ebook', { id }); -} - -export async function updateEbook(id: number, update: BookUpdate) { - return invoke('update_ebook', { id, update }); -} - -export async function deleteEbook(id: number) { - return invoke('delete_ebook', { id }); -} - -export type BookBulkPatch = { - add_tags: string[]; - set_series: boolean; - series: string | null; - set_series_index: boolean; - series_index: number | null; - set_comment: boolean; - comment: string; -}; - -export type BulkFailure = { - id: number; - error: string; -}; - -export type BulkActionResult = { - updated: number; - failed: BulkFailure[]; -}; - -export async function deleteEbooks(ids: number[]) { - return invoke('delete_ebooks_batch', { ids }); -} - -export async function bulkPatchEbooks(ids: number[], patch: BookBulkPatch) { - return invoke('bulk_patch_ebooks_cmd', { ids, patch }); -} - -export async function getCover(id: number) { - return invoke('get_cover', { id }); -} - -export type EnrichmentResult = { - book: BookDetail; - updated_fields: string[]; - source: string; -}; - -export async function enrichEbookMetadata(id: number) { - return invoke('enrich_ebook_metadata', { id }); -} - -export type CoverCandidate = { - id: string; - volume_id: string | null; - title: string; - authors: string; - preview_url: string; - download_urls: string[]; -}; - -export async function listEbookCoverCandidates(id: number) { - return invoke('list_ebook_cover_candidates', { id }); -} - -export async function setEbookCoverFromUrls(id: number, urls: string[]) { - return invoke('set_ebook_cover_from_urls', { id, urls }); -} - -export type ReaderSource = { - book_id: number; - title: string; - authors: string[]; - format: string; - path: string; - available_formats: string[]; -}; - -export type ReadingProgress = { - book_id: number; - format: string; - location: string; - percent: number; - updated_at: string; -}; - -export type ReadingProgressUpdate = { - format: string; - location: string; - percent: number; -}; - -export async function getReaderSource(bookId: number, format?: string) { - return invoke('get_reader_source', { - bookId, - format: format ?? null - }); -} - -export async function getEbookReadingProgress(bookId: number) { - return invoke('get_ebook_reading_progress', { bookId }); -} - -export async function saveEbookReadingProgress(bookId: number, update: ReadingProgressUpdate) { - return invoke('save_ebook_reading_progress', { bookId, update }); -} - -export type ComicPageList = { - book_id: number; - format: string; - page_count: number; - pages: string[]; -}; - -export type ComicPage = { - book_id: number; - index: number; - page_count: number; - name: string; - data_url: string; -}; - -export async function listComicPages(bookId: number) { - return invoke('list_comic_pages', { bookId }); -} - -export async function getComicPage(bookId: number, index: number) { - return invoke('get_comic_page', { bookId, index }); -} - -export type ConvertResult = { - book: BookDetail; - format: string; - path: string; - converted: boolean; -}; - -export async function listEbookConvertTargets(bookId: number) { - return invoke('list_ebook_convert_targets', { bookId }); -} - -export async function convertEbookFormat(bookId: number, targetFormat: string) { - return invoke('convert_ebook_format', { bookId, targetFormat }); -} - -export async function listKindleDevices() { - return invoke('list_kindle_devices'); -} - -export async function listEbookReaders() { - return invoke('list_ebook_reader_devices'); -} - -export type DeviceBook = { - title: string; - format: string; - filename: string; - path: string; - size: number; - handle: number | null; -}; - -export type DeviceLibrary = { - device_id: string; - device_name: string; - connection: ConnectionKind; - books: DeviceBook[]; -}; - -export type DeviceBookTarget = { - path: string; - filename: string; - handle: number | null; -}; - -export type DeviceBulkFailure = { - path: string; - error: string; -}; - -export type DeviceBulkResult = { - removed: number; - failed: DeviceBulkFailure[]; -}; - -export async function scanEbookReader(deviceId: string) { - return invoke('scan_ebook_reader', { deviceId }); -} - -export async function deleteEbookReaderBooks(deviceId: string, books: DeviceBookTarget[]) { - return invoke('delete_ebook_reader_books', { deviceId, books }); -} - -export async function sendBookToKindle(bookId: number, deviceId: string) { - return invoke('send_book_to_kindle', { - bookId, - deviceId - }); -} - -export async function getSyncHistory() { - return invoke('get_sync_history'); -} diff --git a/src/lib/device-presence.svelte.ts b/src/lib/device-presence.svelte.ts deleted file mode 100644 index 6a857c1..0000000 --- a/src/lib/device-presence.svelte.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { listen, type UnlistenFn } from '@tauri-apps/api/event'; -import { listEbookReaders, type EbookReaderDevice } from '$lib/api'; - -export const EREADER_PRESENCE_EVENT = 'ereader-presence'; - -export type PresenceChange = 'arrived' | 'left' | 'snapshot'; - -export type EreaderPresenceEvent = { - change: PresenceChange; - label: string | null; - device_id: string | null; - devices: EbookReaderDevice[]; -}; - -export type DeviceNotice = { - kind: 'arrived' | 'left'; - message: string; -}; - -/** Shared connected-reader state for the whole app shell. */ -export const devicePresence = $state({ - devices: [] as EbookReaderDevice[], - notice: null as DeviceNotice | null, - listening: false -}); - -let unlisten: UnlistenFn | null = null; -let noticeTimer: ReturnType | null = null; -const listeners = new Set<(event: EreaderPresenceEvent) => void>(); - -function applyEvent(event: EreaderPresenceEvent) { - devicePresence.devices = event.devices; - - if (event.change === 'arrived' || event.change === 'left') { - const name = event.label?.trim() || 'eReader'; - const message = - event.change === 'arrived' ? `${name} connected` : `${name} disconnected`; - - if (noticeTimer) clearTimeout(noticeTimer); - devicePresence.notice = { kind: event.change, message }; - noticeTimer = setTimeout(() => { - devicePresence.notice = null; - noticeTimer = null; - }, 5000); - } - - for (const listener of listeners) { - listener(event); - } -} - -export function onEreaderPresence(listener: (event: EreaderPresenceEvent) => void) { - listeners.add(listener); - return () => listeners.delete(listener); -} - -export function dismissDeviceNotice() { - if (noticeTimer) clearTimeout(noticeTimer); - noticeTimer = null; - devicePresence.notice = null; -} - -export async function startDevicePresenceListener() { - if (devicePresence.listening) return; - devicePresence.listening = true; - - try { - unlisten = await listen(EREADER_PRESENCE_EVENT, (event) => { - applyEvent(event.payload); - }); - try { - devicePresence.devices = await listEbookReaders(); - } catch { - devicePresence.devices = []; - } - } catch (e) { - devicePresence.listening = false; - console.error('Could not listen for eReader presence', e); - } -} - -export function stopDevicePresenceListener() { - unlisten?.(); - unlisten = null; - devicePresence.listening = false; - listeners.clear(); - if (noticeTimer) clearTimeout(noticeTimer); - noticeTimer = null; -} diff --git a/src/lib/library-filters.ts b/src/lib/library-filters.ts deleted file mode 100644 index a1fe07b..0000000 --- a/src/lib/library-filters.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { BookSummary } from '$lib/api'; - -export type ReadingStatus = 'reading' | 'to-read' | 'read'; -export type FormatKind = 'novel' | 'comic'; - -export function bookStatus(b: BookSummary): ReadingStatus { - if (b.progress_percent == null) return 'to-read'; - if (b.progress_percent >= 100) return 'read'; - return 'reading'; -} - -export function bookFormatKind(b: BookSummary): FormatKind { - return b.formats.some((f) => { - const u = f.toUpperCase(); - return u === 'CBZ' || u === 'CBR'; - }) - ? 'comic' - : 'novel'; -} - -export function bookAuthorLabels(b: BookSummary): string[] { - const cleaned = b.authors.map((a) => a.trim()).filter(Boolean); - return cleaned.length ? cleaned : ['Unknown']; -} - -export function bookMatchesFilters( - b: BookSummary, - statuses: Set, - genres: Set, - formats: Set, - authors: Set -): boolean { - if (statuses.size > 0 && !statuses.has(bookStatus(b))) return false; - if (formats.size > 0 && !formats.has(bookFormatKind(b))) return false; - if (genres.size > 0 && !b.tags.some((t) => genres.has(t.trim()))) return false; - if (authors.size > 0 && !bookAuthorLabels(b).some((a) => authors.has(a))) return false; - return true; -} diff --git a/src/lib/reader-prefs.test.ts b/src/lib/reader-prefs.test.ts deleted file mode 100644 index d932e24..0000000 --- a/src/lib/reader-prefs.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { - getReaderFontOption, - getStoredReaderFont, - isReaderFontId, - openDyslexicFontFaceCss, - setStoredReaderFont -} from './reader-prefs'; - -describe('reader-prefs', () => { - beforeEach(() => { - localStorage.clear(); - }); - - it('validates font ids', () => { - expect(isReaderFontId('serif')).toBe(true); - expect(isReaderFontId('opendyslexic')).toBe(true); - expect(isReaderFontId('comic-sans')).toBe(false); - expect(isReaderFontId(null)).toBe(false); - }); - - it('defaults stored font to serif', () => { - expect(getStoredReaderFont()).toBe('serif'); - }); - - it('persists a valid reader font', () => { - expect(setStoredReaderFont('opendyslexic')).toBe('opendyslexic'); - expect(getStoredReaderFont()).toBe('opendyslexic'); - }); - - it('returns publisher option with null css family', () => { - expect(getReaderFontOption('publisher').cssFamily).toBeNull(); - expect(getReaderFontOption('serif').cssFamily).toContain('Source Serif'); - }); - - it('builds OpenDyslexic @font-face CSS from an origin', () => { - const css = openDyslexicFontFaceCss('https://example.test'); - expect(css).toContain("font-family: 'OpenDyslexic'"); - expect(css).toContain( - "url('https://example.test/fonts/opendyslexic/OpenDyslexic-Regular.woff2')" - ); - }); -}); diff --git a/src/lib/reader-prefs.ts b/src/lib/reader-prefs.ts deleted file mode 100644 index 4285675..0000000 --- a/src/lib/reader-prefs.ts +++ /dev/null @@ -1,94 +0,0 @@ -export type ReaderFontId = 'publisher' | 'serif' | 'sans' | 'opendyslexic'; - -export type ReaderFontOption = { - id: ReaderFontId; - label: string; - description: string; - /** CSS font-family stack, or null to keep publisher fonts in EPUB. */ - cssFamily: string | null; -}; - -const STORAGE_KEY = 'grimoire-reader-font'; - -export const READER_FONTS: ReaderFontOption[] = [ - { - id: 'publisher', - label: 'Publisher default', - description: "Keep the ebook's own fonts when possible.", - cssFamily: null - }, - { - id: 'serif', - label: 'Source Serif', - description: 'Grimoire’s reading serif.', - cssFamily: "'Source Serif 4', Georgia, 'Times New Roman', serif" - }, - { - id: 'sans', - label: 'Source Sans', - description: 'Clean sans-serif for long stretches of text.', - cssFamily: "'Source Sans 3', 'Segoe UI', system-ui, sans-serif" - }, - { - id: 'opendyslexic', - label: 'OpenDyslexic', - description: 'Shapes designed to help with some symptoms of dyslexia.', - cssFamily: "'OpenDyslexic', 'Source Sans 3', sans-serif" - } -]; - -export function isReaderFontId(value: string | null | undefined): value is ReaderFontId { - return READER_FONTS.some((font) => font.id === value); -} - -export function getReaderFontOption(id: ReaderFontId): ReaderFontOption { - return READER_FONTS.find((font) => font.id === id) ?? READER_FONTS[0]; -} - -export function getStoredReaderFont(): ReaderFontId { - if (typeof localStorage === 'undefined') return 'serif'; - const value = localStorage.getItem(STORAGE_KEY); - return isReaderFontId(value) ? value : 'serif'; -} - -export function setStoredReaderFont(id: ReaderFontId): ReaderFontId { - if (typeof localStorage !== 'undefined') { - localStorage.setItem(STORAGE_KEY, id); - } - return id; -} - -/** Absolute @font-face CSS for injecting into EPUB iframes. */ -export function openDyslexicFontFaceCss(origin = typeof window !== 'undefined' ? window.location.origin : '') { - const base = `${origin}/fonts/opendyslexic`; - return ` -@font-face { - font-family: 'OpenDyslexic'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('${base}/OpenDyslexic-Regular.woff2') format('woff2'); -} -@font-face { - font-family: 'OpenDyslexic'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url('${base}/OpenDyslexic-Italic.woff2') format('woff2'); -} -@font-face { - font-family: 'OpenDyslexic'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('${base}/OpenDyslexic-Bold.woff2') format('woff2'); -} -@font-face { - font-family: 'OpenDyslexic'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url('${base}/OpenDyslexic-BoldItalic.woff2') format('woff2'); -} -`.trim(); -} diff --git a/src/lib/theme.test.ts b/src/lib/theme.test.ts deleted file mode 100644 index aaa6110..0000000 --- a/src/lib/theme.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { - applyTheme, - cycleTheme, - getStoredTheme, - resolveTheme, - setThemePreference, - themeLabel -} from './theme'; - -describe('theme', () => { - beforeEach(() => { - localStorage.clear(); - document.documentElement.removeAttribute('data-theme'); - document.documentElement.style.colorScheme = ''; - }); - - it('defaults to dark when nothing is stored', () => { - expect(getStoredTheme()).toBe('dark'); - }); - - it('reads a stored preference', () => { - localStorage.setItem('grimoire-theme', 'light'); - expect(getStoredTheme()).toBe('light'); - localStorage.setItem('grimoire-theme', 'system'); - expect(getStoredTheme()).toBe('system'); - }); - - it('ignores invalid stored values', () => { - localStorage.setItem('grimoire-theme', 'neon'); - expect(getStoredTheme()).toBe('dark'); - }); - - it('resolves explicit preferences without matchMedia', () => { - expect(resolveTheme('light')).toBe('light'); - expect(resolveTheme('dark')).toBe('dark'); - }); - - it('cycles system → light → dark → system', () => { - expect(cycleTheme('system')).toBe('light'); - expect(cycleTheme('light')).toBe('dark'); - expect(cycleTheme('dark')).toBe('system'); - }); - - it('labels preferences for the UI', () => { - expect(themeLabel('light')).toBe('Light'); - expect(themeLabel('dark')).toBe('Dark'); - expect(themeLabel('system')).toBe('System'); - }); - - it('persists preference and applies dataset theme', () => { - const resolved = setThemePreference('light'); - expect(resolved).toBe('light'); - expect(localStorage.getItem('grimoire-theme')).toBe('light'); - expect(document.documentElement.dataset.theme).toBe('light'); - expect(document.documentElement.style.colorScheme).toBe('light'); - }); - - it('applyTheme updates the document without requiring storage', () => { - expect(applyTheme('dark')).toBe('dark'); - expect(document.documentElement.dataset.theme).toBe('dark'); - }); -}); diff --git a/src/lib/theme.ts b/src/lib/theme.ts deleted file mode 100644 index 2319196..0000000 --- a/src/lib/theme.ts +++ /dev/null @@ -1,45 +0,0 @@ -export type ThemePreference = 'light' | 'dark' | 'system'; -export type ResolvedTheme = 'light' | 'dark'; - -const STORAGE_KEY = 'grimoire-theme'; - -export function getStoredTheme(): ThemePreference { - if (typeof localStorage === 'undefined') return 'dark'; - const value = localStorage.getItem(STORAGE_KEY); - if (value === 'light' || value === 'dark' || value === 'system') return value; - return 'dark'; -} - -export function resolveTheme(preference: ThemePreference): ResolvedTheme { - if (preference === 'light' || preference === 'dark') return preference; - if (typeof window === 'undefined' || !window.matchMedia) return 'dark'; - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; -} - -export function applyTheme(preference: ThemePreference): ResolvedTheme { - const resolved = resolveTheme(preference); - if (typeof document !== 'undefined') { - document.documentElement.dataset.theme = resolved; - document.documentElement.style.colorScheme = resolved; - } - return resolved; -} - -export function setThemePreference(preference: ThemePreference): ResolvedTheme { - if (typeof localStorage !== 'undefined') { - localStorage.setItem(STORAGE_KEY, preference); - } - return applyTheme(preference); -} - -export function cycleTheme(current: ThemePreference): ThemePreference { - if (current === 'system') return 'light'; - if (current === 'light') return 'dark'; - return 'system'; -} - -export function themeLabel(preference: ThemePreference): string { - if (preference === 'light') return 'Light'; - if (preference === 'dark') return 'Dark'; - return 'System'; -} diff --git a/src/lib/ui/ActionButton.svelte b/src/lib/ui/ActionButton.svelte deleted file mode 100644 index 4ae3539..0000000 --- a/src/lib/ui/ActionButton.svelte +++ /dev/null @@ -1,70 +0,0 @@ - - - - - diff --git a/src/lib/ui/Button.svelte b/src/lib/ui/Button.svelte deleted file mode 100644 index ee52647..0000000 --- a/src/lib/ui/Button.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - - diff --git a/src/lib/ui/Checkbox.svelte b/src/lib/ui/Checkbox.svelte deleted file mode 100644 index 301f8c3..0000000 --- a/src/lib/ui/Checkbox.svelte +++ /dev/null @@ -1,72 +0,0 @@ - - - - - diff --git a/src/lib/ui/Dialog.svelte b/src/lib/ui/Dialog.svelte deleted file mode 100644 index 0de18d3..0000000 --- a/src/lib/ui/Dialog.svelte +++ /dev/null @@ -1,144 +0,0 @@ - - -{#if open} - -{/if} - - diff --git a/src/lib/ui/SearchField.svelte b/src/lib/ui/SearchField.svelte deleted file mode 100644 index 0683f5b..0000000 --- a/src/lib/ui/SearchField.svelte +++ /dev/null @@ -1,42 +0,0 @@ - - - { - syncValue(event); - onchange?.(event); - }} - {...rest} -/> diff --git a/src/lib/ui/TextField.svelte b/src/lib/ui/TextField.svelte deleted file mode 100644 index 5ed7c9b..0000000 --- a/src/lib/ui/TextField.svelte +++ /dev/null @@ -1,100 +0,0 @@ - - -{#if label} - -{/if} -{#if multiline} - -{:else} - { - syncValue(event); - onchange?.(event); - }} - {...rest} - /> -{/if} - - diff --git a/src/lib/view-mode.test.ts b/src/lib/view-mode.test.ts deleted file mode 100644 index 3731151..0000000 --- a/src/lib/view-mode.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { beforeEach, describe, expect, it } from 'vitest'; -import { getStoredViewMode, setStoredViewMode } from './view-mode'; - -describe('view-mode', () => { - beforeEach(() => { - localStorage.clear(); - }); - - it('defaults to grid', () => { - expect(getStoredViewMode()).toBe('grid'); - }); - - it('persists list mode', () => { - expect(setStoredViewMode('list')).toBe('list'); - expect(getStoredViewMode()).toBe('list'); - }); - - it('ignores invalid stored values', () => { - localStorage.setItem('grimoire-device-view', 'masonry'); - expect(getStoredViewMode()).toBe('grid'); - }); -}); diff --git a/src/lib/view-mode.ts b/src/lib/view-mode.ts deleted file mode 100644 index 7c40f0e..0000000 --- a/src/lib/view-mode.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type ViewMode = 'grid' | 'list'; - -const STORAGE_KEY = 'grimoire-device-view'; - -export function getStoredViewMode(): ViewMode { - if (typeof localStorage === 'undefined') return 'grid'; - const value = localStorage.getItem(STORAGE_KEY); - if (value === 'grid' || value === 'list') return value; - return 'grid'; -} - -export function setStoredViewMode(mode: ViewMode): ViewMode { - if (typeof localStorage !== 'undefined') { - localStorage.setItem(STORAGE_KEY, mode); - } - return mode; -} diff --git a/src/lib/window-chrome.ts b/src/lib/window-chrome.ts deleted file mode 100644 index fe7fa3d..0000000 --- a/src/lib/window-chrome.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type ChromeOs = 'macos' | 'windows' | 'linux'; - -/** OS for window-chrome placement and styling. Sync-safe for SSR. */ -export function detectChromeOs(): ChromeOs { - if (typeof navigator === 'undefined') return 'macos'; - - const platform = navigator.platform?.toLowerCase() ?? ''; - const ua = navigator.userAgent.toLowerCase(); - - if (platform.includes('mac') || ua.includes('mac os')) return 'macos'; - if (platform.includes('win') || ua.includes('windows')) return 'windows'; - return 'linux'; -} - -export function windowControlsOnLeft(os: ChromeOs = detectChromeOs()) { - return os === 'macos'; -} diff --git a/src/lib/windows.test.ts b/src/lib/windows.test.ts deleted file mode 100644 index d2e171e..0000000 --- a/src/lib/windows.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { describe, expect, it, vi } from 'vitest'; - -vi.mock('@tauri-apps/api/webviewWindow', () => ({ - WebviewWindow: class { - static async getByLabel() { - return null; - } - } -})); - -import { readerWindowLabel } from './windows'; - -describe('windows', () => { - it('builds a stable reader window label', () => { - expect(readerWindowLabel(42)).toBe('reader-42'); - }); -}); diff --git a/src/lib/windows.ts b/src/lib/windows.ts deleted file mode 100644 index f76fbab..0000000 --- a/src/lib/windows.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { WebviewWindow } from '@tauri-apps/api/webviewWindow'; - -export function readerWindowLabel(bookId: number) { - return `reader-${bookId}`; -} - -/** Open (or focus) the in-app reader for a book. */ -export async function openReaderWindow(bookId: number) { - const label = readerWindowLabel(bookId); - const existing = await WebviewWindow.getByLabel(label); - if (existing) { - await existing.setFocus(); - return; - } - - const reader = new WebviewWindow(label, { - url: `/read/${bookId}`, - title: 'Reading', - width: 980, - height: 860, - minWidth: 640, - minHeight: 520, - center: true, - resizable: true, - focus: true, - decorations: false, - transparent: true, - shadow: true - }); - - await new Promise((resolve, reject) => { - reader.once('tauri://created', () => resolve()); - reader.once('tauri://error', (event) => { - reject(new Error(String(event.payload ?? 'Could not open reader window'))); - }); - }); -} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte deleted file mode 100644 index 1239993..0000000 --- a/src/routes/+layout.svelte +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - - Grimoire - - -{#if isAuxWindow} - {@render children()} -{:else} -
-{/if} - - diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts deleted file mode 100644 index 9d24899..0000000 --- a/src/routes/+layout.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Tauri doesn't have a Node.js server to do proper SSR -// so we use adapter-static with a fallback to index.html to put the site in SPA mode -// See: https://svelte.dev/docs/kit/single-page-apps -// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info -export const ssr = false; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte deleted file mode 100644 index ece9f56..0000000 --- a/src/routes/+page.svelte +++ /dev/null @@ -1,1432 +0,0 @@ - - - - -
- {#if dragActive} -
-
-

Drop to import

-

- {#if status?.open} - EPUB, PDF, MOBI, AZW3, TXT, CBZ, or CBR (AZW3 is Kindle send; comics and DRM-free - MOBI can be read) - {:else} - Open a library folder first, then drop books here - {/if} -

-
-
- {/if} - - {#if loading} -

Opening library…

- {:else if !status?.open} -
-

Your library starts here

-

- If you already chose a folder in Settings it should open automatically. If you still - see this, set the library in Settings. Grimoire stores - metadata.db and your book files locally, Calibre-style. -

- -

- Open Settings -

- {#if error} -

{error}

- {/if} -
- {:else} -
-
- - -
-
- -
- {#if filtersOpen} - - {/if} -
- {#if importMessage} -

{importMessage}

- {/if} - {#if error} -

{error}

- {/if} - - {#if selectMode && selectedCount > 0} - - {/if} - - {#if books.length === 0} -
-

No books yet

-

- Import EPUB, PDF, TXT, CBZ/CBR comics, or DRM-free MOBI to read in Grimoire. AZW3 can be - imported for Kindle send; Amazon store AZW3 is usually DRM-protected and is not opened - in the reader. -

-
- {:else if filteredBooks.length === 0} -
-

No books match these filters

-

- {#if filtersActive} - Try clearing some filters, or adjust your search. - {:else} - Nothing to show. - {/if} -

-
- {:else} -
- {#each librarySections as section (section.id)} - {#if section.books.length > 0} -
-

- {section.title} - {section.books.length} -

-
- {#each section.books as book (book.id)} - {@render bookCard(book)} - {/each} -
-
- {/if} - {/each} -
- {/if} -
-
- {/if} - - {#snippet bookCard(book: BookSummary)} - {#if selectMode} - - {:else} - - {/if} - {/snippet} - - {#snippet bookCover(book: BookSummary)} -
- {#if covers[book.id]} - - {:else} -
- {book.title.slice(0, 1)} -
- {/if} - {#if book.progress_percent != null && book.progress_percent < 100} - - {Math.round(book.progress_percent)}% - - {/if} -
- {/snippet} - - (bulkEditOpen = false)} - > -

Only checked fields are applied. Tags are added (not replaced).

-
- -
-
- Apply series - -
-
- Apply series index - -
-
- Replace notes - -
- {#snippet footer()} - - - {/snippet} -
- - {#if contextMenu && !selectMode} - {@const menu = contextMenu} - - - {/if} -
- - diff --git a/src/routes/book/[id]/+page.svelte b/src/routes/book/[id]/+page.svelte deleted file mode 100644 index c15528d..0000000 --- a/src/routes/book/[id]/+page.svelte +++ /dev/null @@ -1,588 +0,0 @@ - - -
-

← Library

- - {#if error} -

{error}

- {/if} - {#if message} -

{message}

- {/if} - - {#if !book} -

Loading…

- {:else} -
-
-
- {#if cover} - - {:else} -
{book.title.slice(0, 1)}
- {/if} -
- -
- -
-
- -
-
- -
-
- -
-
-
- -
-
- -
-
-
- -
-
- -
- -
-

- {#if readableFormats.length || kindleFormats.length} - {#if readableFormats.length} - Readable: {readableFormats.join(', ')} - {/if} - {#if readableFormats.length && kindleFormats.length} - · - {/if} - {#if kindleFormats.length} - Kindle: {kindleFormats.join(', ')} - {/if} - {:else} - Formats: None - {/if} -

- {#if kindleOnly} -

- AZW3 is not opened in the in-app reader. Kindle Store books are usually - DRM-protected, so Grimoire cannot decrypt them. Use Send to device for Kindle, - or add a DRM-free EPUB, PDF, TXT, or MOBI to read here. See Settings for details. -

- {/if} - {#if otherIdentifiers.length} -

- Other identifiers: {otherIdentifiers - .map((id) => `${id.type_name}: ${id.value}`) - .join(' · ')} -

- {/if} -
- -
- {#if canRead} - - {/if} - - - - -
- {#if convertTargets.length} -
- Convert to - {#each convertTargets as target (target)} - - {/each} -
-

- Native conversion for Kindle sideload (EPUB → MOBI/AZW3) and TXT → EPUB. DRM-free - MOBI can be opened in the reader (converted to EPUB for display). Amazon store AZW3 - is usually DRM-locked and is not opened here. -

- {/if} -

Only fills empty fields and missing covers from Open Library.

- -
-

Send to device

-

- Detected eReaders appear here. Kindle USB/MTP send converts EPUB to MOBI - natively when needed. -

- {#if devices.length === 0} -

- No eReader detected yet. Plug in over USB; Grimoire detects devices automatically. - You can also refresh. -

- - {:else} -
    - {#each devices as device (device.id)} -
  • -
    - {device.product} - {device.family_label} · {device.connection === 'mtp' - ? 'USB/MTP' - : 'Mounted'} -
    - -
  • - {/each} -
- {/if} -
-
-
- - (coverPickerOpen = false)} - onapplied={onCoverApplied} - /> - {/if} -
- - diff --git a/src/routes/devices/+page.svelte b/src/routes/devices/+page.svelte deleted file mode 100644 index f66e191..0000000 --- a/src/routes/devices/+page.svelte +++ /dev/null @@ -1,306 +0,0 @@ - - -
-
-
-

Devices

-

- Detects Kindles, Kobos, BOOX, PocketBook, reMarkable, Tolino, and Nook over USB/MTP or - mounted volumes. -

-
- -
- - {#if error} -

{error}

- {/if} - -
-

Connected eReaders

- {#if loading} -

Scanning MTP devices and mounted volumes…

- {:else if devices.length === 0} -

- No eReaders found yet. Grimoire watches for USB/MTP and mounted volumes automatically; - use Refresh if a device does not appear. On macOS, quit Android File Transfer and stop - ptpcamerad if an MTP device stays locked. -

- {:else} -
    - {#each devices as device (device.id)} -
  • -
    -
    - {device.product} - {device.family_label} -
    -
    {device.display}
    -
    - {connectionLabel(device)} · {formatBytes(device.free_space_bytes)} · prefers - {device.preferred_formats.join(', ')} -
    - {#if device.notes} -
    {device.notes}
    - {/if} - {#if !device.supports_send} -
    - Detected. USB send for this family is not wired yet (Kindle MTP is supported). -
    - {/if} -
    -
    - - - {device.supports_send ? 'Send ready' : 'Detected'} - -
    -
  • - {/each} -
- {/if} - - {#if status && !status.kindle_convert_available} -

- Native Kindle conversion is unavailable in this build. EPUB-only titles need an AZW3, - MOBI, or PDF format before USB send. -

- {/if} -
- -
-

Recent transfers

- {#if !status?.open} -

Open a library to see sync history.

- {:else if history.length === 0} -

No transfers recorded yet. Send a book from its detail page.

- {:else} - - - - - - - - - - - - {#each history as row (row.book_id + row.device_serial + row.last_sent_at)} - - - - - - - - {/each} - -
BookDeviceFormatWhenPath
#{row.book_id}{row.device_serial}{row.format}{row.last_sent_at}{row.remote_path}
- {/if} -
-
- - diff --git a/src/routes/devices/library/+page.svelte b/src/routes/devices/library/+page.svelte deleted file mode 100644 index 4c9fbf8..0000000 --- a/src/routes/devices/library/+page.svelte +++ /dev/null @@ -1,750 +0,0 @@ - - - - -
-

← Devices

- -
-
-

{device?.product ?? 'Device library'}

- {#if device} -

- {device.family_label} · {device.connection === 'mtp' ? 'USB/MTP' : 'Mounted volume'} - {#if library} - · {library.books.length} - {library.books.length === 1 ? 'ebook' : 'ebooks'} - {/if} -

- {/if} -
-
- -
- setView('grid')} - quiet={false} - > - Grid - - setView('list')} - quiet={false} - > - List - -
- - -
-
- - {#if message} -

{message}

- {/if} - {#if error} -

{error}

- {#if !device} - - {/if} - {/if} - - {#if selectMode && selectedCount > 0} - - {/if} - -
- {#if loading && !library} -

Scanning the device for ebooks…

- {:else if library && library.books.length === 0} -
-

No ebooks found

-

- Grimoire looked for EPUB, PDF, AZW3, MOBI, and similar files under the device storage. -

-
- {:else if library && filteredBooks.length === 0} -

No books match "{query}".

- {:else if library && viewMode === 'grid'} -
- {#each filteredBooks as book (bookKey(book))} - {#if selectMode} - - {:else} -
openContextMenu(e, book)} - > -
-
- {book.title.slice(0, 1)} - {book.format} -
-
-

{book.title}

-

{book.format} · {formatBytes(book.size)}

-

{book.path}

-
- {/if} - {/each} -
- {:else if library} - - - - {#if selectMode} - - {/if} - - - - - - - - {#each filteredBooks as book (bookKey(book))} - {#if selectMode} - toggleSelected(bookKey(book))} - > - - - - - - - {:else} - openContextMenu(e, book)} - > - - - - - - {/if} - {/each} - -
TitleFormatSizePath
- e.stopPropagation()} - /> - {book.title}{book.format}{formatBytes(book.size)}{book.path}
{book.title}{book.format}{formatBytes(book.size)}{book.path}
- {/if} -
- - {#if contextMenu && !selectMode} - {@const menu = contextMenu} - - - {/if} -
- - diff --git a/src/routes/read/[id]/+page.svelte b/src/routes/read/[id]/+page.svelte deleted file mode 100644 index 173ab28..0000000 --- a/src/routes/read/[id]/+page.svelte +++ /dev/null @@ -1,744 +0,0 @@ - - - - {source?.title ? `${source.title} · Grimoire` : 'Reading · Grimoire'} - - - - -
-
- {#if controlsLeft} - - {/if} - -
- {#if (source?.format === 'EPUB' || (source && isComicFormat(source.format))) && ready} - - - {/if} - {Math.round(percent)}% -
- {#if !controlsLeft} - - {/if} -
- - {#if error} -
-

{error}

-
- {:else if loading} -
-

Opening book…

-
- {:else if source?.format === 'EPUB'} -
- {:else if source?.format === 'TXT'} -
-
{textContent || 'Loading text…'}
-
- {:else if source?.format === 'PDF' && pdfUrl} - -

- PDF page tracking is limited. Grimoire remembers that you opened this file. -

- {:else if source && isComicFormat(source.format)} -
- {#if comicDataUrl} - {comicPageName - {:else} -

Loading page…

- {/if} -
- {/if} -
- - diff --git a/src/routes/read/[id]/+page.ts b/src/routes/read/[id]/+page.ts deleted file mode 100644 index a3d1578..0000000 --- a/src/routes/read/[id]/+page.ts +++ /dev/null @@ -1 +0,0 @@ -export const ssr = false; diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte deleted file mode 100644 index a53d418..0000000 --- a/src/routes/settings/+page.svelte +++ /dev/null @@ -1,338 +0,0 @@ - - -
-
-
-

Settings

-

Choose a library folder to open automatically when Grimoire starts.

-
-
- - {#if error} -

{error}

- {/if} - {#if message} -

{message}

- {/if} - -
-

Library location

-

- Grimoire stores metadata.db and book files in this folder. The path is saved in - your OS config directory and loaded on every launch. -

- -
- Current path - {#if status?.path} - {status.path} -

- {status.open ? 'Open and ready' : 'Saved, but not open right now'} -

- {:else} -

No library path saved yet.

- {/if} -
- -
- - {#if status?.path} - - {/if} -
-
- -
-

Reader font

-

- Applies to EPUB and TXT reading. OpenDyslexic is bundled in Grimoire for offline use - (SIL Open Font License). -

- -
- Reader font - {#each READER_FONTS as font (font.id)} - - {/each} -
- -

- The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. -

-
- -
-

Formats and reading

-

- The in-app reader opens EPUB, PDF, TXT, - CBZ/CBR comics, and DRM-free MOBI (MOBI is - converted to EPUB for display). -

-

- AZW3 can still be imported and sent to a Kindle. It is not opened in the - Grimoire reader. Kindle Store purchases are usually DRM-protected, so - Grimoire cannot decrypt them. -

-

- For reading in Grimoire, prefer a DRM-free EPUB, PDF, TXT, CBZ/CBR, or MOBI. For Kindle - reading, keep or convert to AZW3/MOBI and use Send to device. -

-
- -
-

Metadata

-

- Missing details and covers are looked up on - Open Library. No API - key is required. -

-
-
- - diff --git a/static/favicon.svg b/static/favicon.svg deleted file mode 100644 index 6e87f22..0000000 --- a/static/favicon.svg +++ /dev/null @@ -1,1061 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/static/svelte.svg b/static/svelte.svg deleted file mode 100644 index c5e0848..0000000 --- a/static/svelte.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/static/tauri.svg b/static/tauri.svg deleted file mode 100644 index 31b62c9..0000000 --- a/static/tauri.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/static/vite.svg b/static/vite.svg deleted file mode 100644 index e7b8dfb..0000000 --- a/static/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/svelte.config.js b/svelte.config.js deleted file mode 100644 index a7830ea..0000000 --- a/svelte.config.js +++ /dev/null @@ -1,18 +0,0 @@ -// Tauri doesn't have a Node.js server to do proper SSR -// so we use adapter-static with a fallback to index.html to put the site in SPA mode -// See: https://svelte.dev/docs/kit/single-page-apps -// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info -import adapter from "@sveltejs/adapter-static"; -import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - preprocess: vitePreprocess(), - kit: { - adapter: adapter({ - fallback: "index.html", - }), - }, -}; - -export default config; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index f4d0a0e..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler" - } - // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias - // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in -} diff --git a/vite.config.js b/vite.config.js deleted file mode 100644 index 218424c..0000000 --- a/vite.config.js +++ /dev/null @@ -1,37 +0,0 @@ -import { defineConfig } from "vite"; -import { sveltekit } from "@sveltejs/kit/vite"; -// @ts-expect-error type error without @types/node package -import process from "node:process"; -const host = process.env.TAURI_DEV_HOST; - -// https://vite.dev/config/ -export default defineConfig(() => ({ - plugins: [sveltekit()], - - // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // - // 1. prevent Vite from obscuring rust errors - clearScreen: false, - // 2. tauri expects a fixed port, fail if that port is not available - server: { - port: 1420, - strictPort: true, - host: host || "127.0.0.1", - hmr: host - ? { - protocol: "ws", - host, - port: 1421, - } - : undefined, - watch: { - // 3. tell Vite to ignore watching `src-tauri` - ignored: ["**/src-tauri/**"], - }, - }, - - test: { - include: ["src/**/*.{test,spec}.{js,ts}"], - environment: "happy-dom", - }, -}));
-
- {#if controlsLeft} - - {/if} - - - Grimoire. - - - - {#if !controlsLeft} - - {/if} -
- - {#if devicePresence.notice} -
-

{devicePresence.notice.message}

- -
- {/if} - -
- {#if booting} -
-

Opening library…

-
- {:else if bootError} -
-

{bootError}

-
- {:else} - {@render children()} - {/if} -
-