diff --git a/PKGBUILD b/PKGBUILD index 834a38c..be51486 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=moku -pkgver=0.3.0 +pkgver=0.4.0 pkgrel=1 pkgdesc="Native Linux manga reader frontend for Suwayomi-Server" arch=('x86_64') @@ -33,14 +33,8 @@ prepare() { build() { cd "Moku-$pkgver" - - # Build frontend pnpm build - - # Repack dist for Tauri tar -czf packaging/frontend-dist.tar.gz -C dist . - - # Build Tauri binary TAURI_SKIP_DEVSERVER_CHECK=true cargo build \ --release \ --manifest-path src-tauri/Cargo.toml @@ -49,19 +43,15 @@ build() { package() { cd "Moku-$pkgver" - # Moku binary install -Dm755 src-tauri/target/release/moku \ "$pkgdir/usr/bin/moku" - # Bundled JRE install -dm755 "$pkgdir/usr/lib/moku/jre" tar -xf "$srcdir/jdk.tar.gz" -C "$pkgdir/usr/lib/moku/jre" --strip-components=1 - # Suwayomi server jar install -Dm644 "$srcdir/suwayomi-server.jar" \ "$pkgdir/usr/lib/moku/tachidesk/Suwayomi-Server.jar" - # tachidesk-server wrapper script install -dm755 "$pkgdir/usr/lib/moku/tachidesk/default-conf" cat > "$pkgdir/usr/lib/moku/tachidesk/default-conf/server.conf" << 'EOF' server.ip = "127.0.0.1" @@ -109,7 +99,6 @@ exec /usr/lib/moku/jre/bin/java \ -jar /usr/lib/moku/tachidesk/Suwayomi-Server.jar EOF - # Desktop entry and icons install -Dm644 packaging/dev.moku.app.desktop \ "$pkgdir/usr/share/applications/dev.moku.app.desktop" install -Dm644 src-tauri/icons/32x32.png \ diff --git a/README.md b/README.md index 02946cf..0b7a31c 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,11 @@
- +

Moku

-

A fast, minimal manga reader for Suwayomi-Server.
Built with Tauri v2 and React.

- - - - - - - - - - - - -
+

A fast, minimal manga reader for Suwayomi-Server.
Built with Tauri v2 and Svelte.

--- -## Features - -### Reader -- **Single**, **double-page**, and **longstrip** reading modes -- **Infinite longstrip** — when Auto mode is enabled, the next chapter's pages are appended directly into the scroll without any re-render or gap; the entire series flows as one seamless ribbon -- Fit modes: fit width, fit height, fit screen, and 1:1 original -- Per-series zoom control via Ctrl+scroll or a slider popover -- RTL / LTR reading direction toggle -- Configurable page gaps -- Full keyboard navigation with rebindable keybinds -- UI auto-hides after 3 seconds of inactivity; reappears on cursor movement near edges -- Chapter-relative page counter that updates live as you scroll through the infinite strip -- Auto-mark chapters as read when the last page is reached - -### Library -- Grid view of your entire manga collection with lazy-loaded cover art -- Filter tabs: **Saved**, **Downloaded**, and **All** -- Genre tag filter chips — multi-select to narrow by any combination of tags -- In-line search -- Context menu: open, add/remove from library - -### Series Detail -- Cover, author, artist, status badge, genres, and synopsis -- Read progress bar with percentage -- Continue / Start / Re-read button that picks up exactly where you left off (including mid-chapter page) -- Chapter list with scanlator, upload date, and in-progress page indicator -- **Grid view** — displays all chapters as numbered tiles; read/unread/in-progress states are visually distinct at a glance; switches between list and grid with a single click -- Sort by newest or oldest first -- Jump-to-chapter input -- Bulk download menu: from current chapter, unread only, or all -- Per-chapter context menu: mark read/unread, mark all above as read, download, delete, bulk download from here -- Collapsible source details panel with source ID, language, and source migration - -### Search -- Cross-source search running up to 3 concurrent requests -- Language filter bar (preferred language default, per-language, or all) -- Results grouped by source with skeleton loading states - -### Sources & Extensions -- Browse and search installed sources, grouped by extension with per-language expansion -- Extension manager: install, update, remove, and install from external APK URL -- Repo refresh with update count badge - -### Downloads -- Download queue with live progress - -### History -- Reading history grouped by day with relative timestamps -- Per-entry thumbnail, chapter name, and last-read page -- Full-text search across titles and chapter names -- One-click clear - ---- - ## Requirements [Suwayomi-Server](https://github.com/Suwayomi/Suwayomi-Server) must be running. By default Moku expects it at `http://127.0.0.1:4567`. @@ -114,20 +47,15 @@ pnpm install pnpm tauri:dev ``` -> `tauri:dev` uses `src-tauri/tauri.dev.conf.json` to point at the Vite dev server, keeping the release build config clean for `nix build`. - --- - ## Stack | | | |---|---| | [Tauri v2](https://tauri.app) | Native app shell | -| [React](https://react.dev) + [TypeScript](https://www.typescriptlang.org) | UI | +| [Svelte](https://svelte.dev) + [TypeScript](https://www.typescriptlang.org) | UI | | [Vite](https://vitejs.dev) | Frontend bundler | -| [Zustand](https://zustand-demo.pmnd.rs) | State management | -| [Phosphor Icons](https://phosphoricons.com) | Icon set | | [Crane](https://github.com/ipetkov/crane) | Nix Rust builds | --- @@ -140,4 +68,4 @@ Distributed under the [Apache 2.0 License](./LICENSE). ## Disclaimer -Moku does not host or distribute any content. The developers have no affiliation with any content providers accessible through connected sources. \ No newline at end of file +Moku does not host or distribute any content. The developers have no affiliation with any content providers accessible through connected sources. diff --git a/build-scripts/bump.sh b/build-scripts/bump.sh deleted file mode 100755 index cc0dd37..0000000 --- a/build-scripts/bump.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -# build-scripts/pkgbuild-bump.sh -# ───────────────────────────────────────────────────────────────────────────── -# Run this AFTER the git tag has been pushed to GitHub. -# -# Usage: -# ./build-scripts/pkgbuild-bump.sh 0.3.0 -set -euo pipefail - -RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' -info() { echo -e "${CYAN} →${RESET} $*"; } -success() { echo -e "${GREEN} ✓${RESET} $*"; } -die() { echo -e "${RED} ✗${RESET} $*" >&2; exit 1; } -section() { echo -e "\n${BOLD}── $* ──${RESET}"; } - -[[ $# -lt 1 ]] && die "Usage: $0 " -VERSION="$1" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -PKGBUILD="${REPO_ROOT}/PKGBUILD" - -command -v curl &>/dev/null || die "curl not found" -[[ -f "$PKGBUILD" ]] || die "PKGBUILD not found: $PKGBUILD" - -section "Patching PKGBUILD → ${VERSION}" - -TARBALL_URL="https://github.com/Youwes09/Moku/archive/refs/tags/v${VERSION}.tar.gz" -info "Fetching source tarball to compute sha256…" -TARBALL_SHA=$(curl -fsSL "$TARBALL_URL" | sha256sum | awk '{print $1}') - -sed -i "s/^pkgver=.*/pkgver=${VERSION}/" "$PKGBUILD" -sed -i "s/^pkgrel=.*/pkgrel=1/" "$PKGBUILD" - -# Replace only the first sha256 entry (source tarball) inside sha256sums=('...') -# The suwayomi jar and jdk hashes are pinned and stay untouched. -# Strategy: match the opening sha256sums=('' then swap just that first hash. -sed -i "s/\(sha256sums=('\)[0-9a-f]\{64\}/\1${TARBALL_SHA}/" "$PKGBUILD" - -# Verify the replacement landed -if ! grep -q "$TARBALL_SHA" "$PKGBUILD"; then - die "sha256 replacement failed — check PKGBUILD sha256sums format" -fi - -success "PKGBUILD patched (pkgver=${VERSION}, sha256=${TARBALL_SHA})" -info "PKGBUILD → ${PKGBUILD}" \ No newline at end of file diff --git a/build-scripts/release.sh b/build-scripts/release.sh deleted file mode 100755 index 5e73cd5..0000000 --- a/build-scripts/release.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env bash -# build-scripts/release.sh -# ───────────────────────────────────────────────────────────────────────────── -# Usage: -# ./build-scripts/release.sh 0.2.0 -# -# Requires: nix, flatpak-builder, appstream -set -euo pipefail - -# ── Colour helpers ───────────────────────────────────────────────────────────── -RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' -CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' -info() { echo -e "${CYAN} →${RESET} $*"; } -success() { echo -e "${GREEN} ✓${RESET} $*"; } -warn() { echo -e "${YELLOW} ⚠${RESET} $*"; } -die() { echo -e "${RED} ✗${RESET} $*" >&2; exit 1; } -section() { echo -e "\n${BOLD}── $* ──${RESET}"; } - -# ── Args ─────────────────────────────────────────────────────────────────────── -[[ $# -lt 1 ]] && die "Usage: $0 " -VERSION="$1" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -FLATPAK_MANIFEST="${REPO_ROOT}/dev.moku.app.yml" -PKGBUILD="${REPO_ROOT}/PKGBUILD" - -# ── Sanity checks ────────────────────────────────────────────────────────────── -section "Pre-flight" -command -v nix &>/dev/null || die "nix not found" -command -v curl &>/dev/null || die "curl not found" -[[ -f "$FLATPAK_MANIFEST" ]] || die "Flatpak manifest not found: $FLATPAK_MANIFEST" -[[ -f "$PKGBUILD" ]] || die "PKGBUILD not found: $PKGBUILD" -success "OK" - -# ── Bump versions ────────────────────────────────────────────────────────────── -section "Bumping version → ${VERSION}" -sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${VERSION}\"/" \ - "${REPO_ROOT}/src-tauri/tauri.conf.json" -success "tauri.conf.json → ${VERSION}" - -sed -i "0,/^version = \"[^\"]*\"/s//version = \"${VERSION}\"/" \ - "${REPO_ROOT}/src-tauri/Cargo.toml" -success "Cargo.toml → ${VERSION}" - -# flake.nix has two `version = "x.y.z";` strings inside the frontend -# derivation and fetchPnpmDeps — both need to match. -sed -i "s/version = \"[^\"]*\";/version = \"${VERSION}\";/g" \ - "${REPO_ROOT}/flake.nix" -success "flake.nix → ${VERSION}" - -# ── Build frontend ───────────────────────────────────────────────────────────── -section "Building frontend" -cd "$REPO_ROOT" -nix develop --command pnpm install --frozen-lockfile -nix develop --command pnpm build -success "Frontend built → dist/" - -# ── Flatpak ──────────────────────────────────────────────────────────────────── -section "Regenerating cargo-sources.json" -cd "$REPO_ROOT" -nix-shell \ - -p "python311.withPackages(ps: [ ps.aiohttp ps.tomlkit ])" \ - --run "python3 packaging/flatpak-cargo-generator.py src-tauri/Cargo.lock -o packaging/cargo-sources.json" -success "cargo-sources.json updated" - -section "Rebuilding frontend-dist.tar.gz" -tar -czf packaging/frontend-dist.tar.gz -C dist . -FRONTEND_SHA=$(sha256sum packaging/frontend-dist.tar.gz | awk '{print $1}') -success "frontend-dist.tar.gz rebuilt sha256: ${FRONTEND_SHA}" - -section "Patching frontend-dist sha256 in dev.moku.app.yml" -PATCH_SCRIPT=$(mktemp /tmp/patch-sha256-XXXXXX.py) -cat > "$PATCH_SCRIPT" << PYEOF -import re, sys -path = "${FLATPAK_MANIFEST}" -new_sha = "${FRONTEND_SHA}" -text = open(path).read() -pattern = r'(path:\s*packaging/frontend-dist\.tar\.gz\s*\n\s*sha256:\s*)[0-9a-f]+' -replacement = r'\g<1>' + new_sha -updated, n = re.subn(pattern, replacement, text) -if n == 0: - sys.exit("Could not find frontend-dist sha256 in dev.moku.app.yml") -open(path, 'w').write(updated) -PYEOF -nix-shell -p python3 --run "python3 '$PATCH_SCRIPT'" -rm -f "$PATCH_SCRIPT" -success "dev.moku.app.yml sha256 updated" - -section "Building Flatpak bundle" -rm -rf "${REPO_ROOT}/build-dir" "${REPO_ROOT}/repo" -nix shell nixpkgs#appstream nixpkgs#flatpak-builder --command \ - flatpak-builder \ - --repo="${REPO_ROOT}/repo" \ - --force-clean \ - "${REPO_ROOT}/build-dir" \ - "$FLATPAK_MANIFEST" - -flatpak build-bundle \ - "${REPO_ROOT}/repo" \ - "${REPO_ROOT}/moku.flatpak" \ - dev.moku.app - -rm -rf "${REPO_ROOT}/build-dir" "${REPO_ROOT}/repo" -success "moku.flatpak created" - -# ── Done ─────────────────────────────────────────────────────────────────────── -echo "" -success "v${VERSION} ready" -info "Flatpak bundle → ${REPO_ROOT}/moku.flatpak" -echo "" -warn "PKGBUILD not patched yet — tag must exist on GitHub first." -info "After pushing the tag, run:" -echo -e " ${CYAN}./build-scripts/pkgbuild-bump.sh ${VERSION}${RESET}" \ No newline at end of file diff --git a/dev.moku.app.yml b/dev.moku.app.yml index 429618d..2552130 100644 --- a/dev.moku.app.yml +++ b/dev.moku.app.yml @@ -181,7 +181,7 @@ modules: path: . - type: file path: packaging/frontend-dist.tar.gz - sha256: c9bb5ee6613b2bc61e69a92cc1ef0029da3b61138d51b01d363f8ea524e51996 + sha256: c78a3f002f898011c4e70e1af781b37dac0fd995b5623170256d88339c90ca74 - packaging/cargo-sources.json - type: inline dest: src-tauri/.cargo diff --git a/flake.lock b/flake.lock index b3e4811..09846e2 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1771438068, - "narHash": "sha256-nGBbXvEZVe/egCPVPFcu89RFtd8Rf6J+4RFoVCFec0A=", + "lastModified": 1773857772, + "narHash": "sha256-5xsK26KRHf0WytBtsBnQYC/lTWDhQuT57HJ7SzuqZcM=", "owner": "ipetkov", "repo": "crane", - "rev": "b5090e53e9d68c523a4bb9ad42b4737ee6747597", + "rev": "b556d7bbae5ff86e378451511873dfd07e4504cd", "type": "github" }, "original": { @@ -15,32 +15,16 @@ "type": "github" } }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1733328505, - "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1769996383, - "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", + "lastModified": 1772408722, + "narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", + "rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3", "type": "github" }, "original": { @@ -49,53 +33,13 @@ "type": "github" } }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nix-appimage": { - "inputs": { - "flake-compat": "flake-compat", - "flake-utils": "flake-utils", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1757920913, - "narHash": "sha256-jd0QwCVz4O1sHHkeaZILD/7D6oyalceEJ4EFnWCgm0k=", - "owner": "ralismark", - "repo": "nix-appimage", - "rev": "7946addbc0d97e358a6d7aefe5e82310f0fe6b18", - "type": "github" - }, - "original": { - "owner": "ralismark", - "repo": "nix-appimage", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1771369470, - "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", + "lastModified": 1773821835, + "narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0182a361324364ae3f436a63005877674cf45efb", + "rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0", "type": "github" }, "original": { @@ -107,11 +51,11 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1769909678, - "narHash": "sha256-cBEymOf4/o3FD5AZnzC3J9hLbiZ+QDT/KDuyHXVJOpM=", + "lastModified": 1772328832, + "narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=", "owner": "nix-community", "repo": "nixpkgs.lib", - "rev": "72716169fe93074c333e8d0173151350670b824c", + "rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742", "type": "github" }, "original": { @@ -124,7 +68,6 @@ "inputs": { "crane": "crane", "flake-parts": "flake-parts", - "nix-appimage": "nix-appimage", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" } @@ -136,11 +79,11 @@ ] }, "locked": { - "lastModified": 1771556776, - "narHash": "sha256-zKprqMQDl3xVfhSSYvgru1IGXjFdxryWk+KqK0I20Xk=", + "lastModified": 1773975983, + "narHash": "sha256-zrRVwdfhDdohANqEhzY/ydeza6EXEi8AG6cyMRNYT9Q=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8b3f46b8a6d17ab46e533a5e3d5b1cc2ff228860", + "rev": "cc80954a95f6f356c303ed9f08d0b63ca86216ac", "type": "github" }, "original": { @@ -148,21 +91,6 @@ "repo": "rust-overlay", "type": "github" } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 273d044..9a4a827 100644 --- a/flake.nix +++ b/flake.nix @@ -2,43 +2,34 @@ description = "Moku — manga reader frontend for Suwayomi"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; - crane.url = "github:ipetkov/crane"; + crane.url = "github:ipetkov/crane"; rust-overlay = { - url = "github:oxalica/rust-overlay"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - nix-appimage = { - url = "github:ralismark/nix-appimage"; + url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = - inputs@{ flake-parts, crane, rust-overlay, nix-appimage, ... }: + inputs@{ flake-parts, crane, rust-overlay, ... }: flake-parts.lib.mkFlake { inherit inputs; } { - systems = [ - "x86_64-linux" - "aarch64-linux" - ]; + systems = [ "x86_64-linux" "aarch64-linux" ]; - perSystem = - { system, pkgs, lib, ... }: + perSystem = { system, lib, ... }: let - pkgs' = import inputs.nixpkgs { + version = "0.4.0"; + + pkgs = import inputs.nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; }; - rustToolchain = pkgs'.rust-bin.stable.latest.default.override { - extensions = [ - "rust-src" - "rust-analyzer" - ]; + rustToolchain = pkgs.rust-bin.stable.latest.default.override { + extensions = [ "rust-src" "rust-analyzer" ]; }; - craneLib = (crane.mkLib pkgs').overrideToolchain rustToolchain; + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; runtimeLibs = with pkgs; [ webkitgtk_4_1 @@ -65,56 +56,44 @@ || base == "package.json" || base == "pnpm-lock.yaml" || base == "tsconfig.json" - || base == "tsconfig.node.json" - || base == "vite.config.ts" - || base == "postcss.config.js" - || base == "postcss.config.cjs" - || base == "tailwind.config.js" - || base == "tailwind.config.ts"; + || base == "vite.config.ts"; }; frontend = pkgs.stdenv.mkDerivation { - pname = "moku-frontend"; - version = "0.3.0"; - src = frontendSrc; + pname = "moku-frontend"; + inherit version; + src = frontendSrc; - nativeBuildInputs = with pkgs; [ - nodejs_22 - pnpm - pnpmConfigHook - ]; + nativeBuildInputs = with pkgs; [ nodejs_22 pnpm pnpmConfigHook ]; pnpmDeps = pkgs.fetchPnpmDeps { - pname = "moku-frontend"; - version = "0.3.0"; - src = frontendSrc; + pname = "moku-frontend"; + inherit version; + src = frontendSrc; fetcherVersion = 1; - hash = "sha256-bpGYsB534RPNNAcYR9BA61vvFpSG6Xu2hY923PakCyY="; + hash = "sha256-FsZTHeBS9qQ9KYgiwDX1vam6uJXK8OjLe5U6Jfu33lc="; }; - buildPhase = "pnpm build"; + buildPhase = "pnpm build"; installPhase = "cp -r dist $out"; }; cargoSrc = lib.cleanSourceWith { - src = ./src-tauri; + src = ./src-tauri; filter = path: type: (craneLib.filterCargoSources path type) - || (lib.hasInfix "/icons/" path) + || (lib.hasInfix "/icons/" path) || (lib.hasInfix "/capabilities/" path) || (builtins.baseNameOf path == "tauri.conf.json"); }; commonArgs = { - src = cargoSrc; - cargoToml = ./src-tauri/Cargo.toml; - cargoLock = ./src-tauri/Cargo.lock; + src = cargoSrc; + cargoToml = ./src-tauri/Cargo.toml; + cargoLock = ./src-tauri/Cargo.lock; strictDeps = true; - buildInputs = runtimeLibs; - nativeBuildInputs = with pkgs; [ - pkg-config - wrapGAppsHook3 - ]; + buildInputs = runtimeLibs; + nativeBuildInputs = with pkgs; [ pkg-config wrapGAppsHook3 ]; preBuild = '' cp -r ${frontend} ../dist ''; @@ -126,6 +105,36 @@ inherit cargoArtifacts; meta.mainProgram = "moku"; postInstall = '' + mkdir -p "$out/share/applications" + cat > "$out/share/applications/moku.desktop" << EOF +[Desktop Entry] +Version=1.0 +Type=Application +Name=Moku +Comment=Manga reader frontend for Suwayomi +Exec=$out/bin/moku +Icon=moku +Terminal=false +Categories=Graphics;Viewer; +Keywords=manga;comic;reader;suwayomi; +StartupWMClass=moku +EOF + + for size in 32x32 128x128 256x256 512x512; do + src="icons/$size.png" + [ -f "$src" ] && install -Dm644 "$src" \ + "$out/share/icons/hicolor/$size/apps/moku.png" + done + + for size in 128x128 256x256; do + src="icons/''${size}@2x.png" + [ -f "$src" ] && install -Dm644 "$src" \ + "$out/share/icons/hicolor/''${size}@2/apps/moku.png" + done + + install -Dm644 "${./src/assets/moku-icon.svg}" \ + "$out/share/icons/hicolor/scalable/apps/moku.svg" + wrapProgram $out/bin/moku \ --prefix XDG_DATA_DIRS : "${lib.makeSearchPath "share/gsettings-schemas" [ pkgs.gsettings-desktop-schemas @@ -134,72 +143,139 @@ --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath runtimeLibs}" \ --prefix PATH : "${lib.makeBinPath [ pkgs.suwayomi-server ]}" \ --set GDK_BACKEND wayland \ - --set WEBKIT_FORCE_SANDBOX 0 - - # ── Icon ───────────────────────────────────────────────────────── - # Tauri bakes several sizes into src-tauri/icons/. We prefer the - # largest PNG (512x512) for the hicolor theme, and also install the - # rounded 32x32 used as the in-app logo so small sizes look right. - # Adjust the source filenames if yours differ. - for size in 32x32 128x128 256x256 512x512; do - src="icons/$size.png" - if [ -f "$src" ]; then - install -Dm644 "$src" \ - "$out/share/icons/hicolor/$size/apps/moku.png" - fi - done - - # @2x variants that Tauri also generates - for size in 128x128 256x256; do - src="icons/''${size}@2x.png" - if [ -f "$src" ]; then - install -Dm644 "$src" \ - "$out/share/icons/hicolor/''${size}@2/apps/moku.png" - fi - done - - # Scalable SVG — src/assets/moku-icon.svg is the rounded version - # referenced in SplashScreen.tsx. Pull it straight from the source - # tree so the launcher always uses the same rounded artwork. - install -Dm644 "${./src/assets/moku-icon.svg}" \ - "$out/share/icons/hicolor/scalable/apps/moku.svg" - - # ── .desktop entry ─────────────────────────────────────────────── - install -Dm644 /dev/stdin \ - "$out/share/applications/moku.desktop" <"; exit 1; } + VERSION="$1" + REPO="$(git rev-parse --show-toplevel)" + sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" \ + "$REPO/src-tauri/tauri.conf.json" + sed -i "0,/^version = \"[^\"]*\"/s//version = \"$VERSION\"/" \ + "$REPO/src-tauri/Cargo.toml" + sed -i "s/version = \"[^\"]*\";/version = \"$VERSION\";/g" \ + "$REPO/flake.nix" + echo "Bumped to $VERSION" + ''; + }; + + flatpakScript = pkgs.writeShellApplication { + name = "moku-flatpak"; + runtimeInputs = with pkgs; [ + gnused coreutils git + nodejs_22 pnpm + appstream flatpak-builder flatpak + (python3.withPackages (ps: [ ps.aiohttp ps.tomlkit ])) + ]; + text = '' + [[ $# -lt 1 ]] && { echo "Usage: nix run .#flatpak -- "; exit 1; } + VERSION="$1" + REPO="$(git rev-parse --show-toplevel)" + MANIFEST="$REPO/dev.moku.app.yml" + + echo "── Bumping versions ──" + sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" \ + "$REPO/src-tauri/tauri.conf.json" + sed -i "0,/^version = \"[^\"]*\"/s//version = \"$VERSION\"/" \ + "$REPO/src-tauri/Cargo.toml" + sed -i "s/version = \"[^\"]*\";/version = \"$VERSION\";/g" \ + "$REPO/flake.nix" + echo "Done" + + echo "── Building frontend ──" + cd "$REPO" + pnpm install --frozen-lockfile + pnpm build + echo "Done" + + echo "── Repacking frontend-dist.tar.gz ──" + tar -czf "$REPO/packaging/frontend-dist.tar.gz" -C "$REPO/dist" . + FRONTEND_SHA=$(sha256sum "$REPO/packaging/frontend-dist.tar.gz" | awk '{print $1}') + echo "sha256: $FRONTEND_SHA" + + echo "── Patching manifest sha256 ──" + python3 - "$MANIFEST" "$FRONTEND_SHA" <<'PYEOF' + import re, sys + path, sha = sys.argv[1], sys.argv[2] + text = open(path).read() + updated, n = re.subn( + r'(path:\s*packaging/frontend-dist\.tar\.gz\s*\n\s*sha256:\s*)[0-9a-f]+', + r'\g<1>' + sha, text) + if n == 0: + sys.exit("ERROR: could not find frontend-dist sha256 in manifest") + open(path, 'w').write(updated) + PYEOF + echo "Done" + + echo "── Regenerating cargo-sources.json ──" + python3 "$REPO/packaging/flatpak-cargo-generator.py" \ + "$REPO/src-tauri/Cargo.lock" \ + -o "$REPO/packaging/cargo-sources.json" + echo "Done" + + echo "── Building flatpak ──" + rm -rf "$REPO/build-dir" "$REPO/repo" + flatpak-builder \ + --repo="$REPO/repo" \ + --force-clean \ + "$REPO/build-dir" \ + "$MANIFEST" + flatpak build-bundle "$REPO/repo" "$REPO/moku.flatpak" dev.moku.app + rm -rf "$REPO/build-dir" "$REPO/repo" + echo "moku.flatpak created" + + echo "" + echo "Done — v$VERSION" + echo " -> $REPO/moku.flatpak" + echo "" + echo "After pushing the tag, run:" + echo " nix run .#pkgbuild-bump -- $VERSION" + ''; + }; + + pkgbuildBumpScript = pkgs.writeShellApplication { + name = "moku-pkgbuild-bump"; + runtimeInputs = with pkgs; [ gnused curl coreutils git ]; + text = '' + [[ $# -lt 1 ]] && { echo "Usage: nix run .#pkgbuild-bump -- "; exit 1; } + VERSION="$1" + REPO="$(git rev-parse --show-toplevel)" + PKGBUILD="$REPO/PKGBUILD" + [[ -f "$PKGBUILD" ]] || { echo "PKGBUILD not found"; exit 1; } + + TARBALL_URL="https://github.com/Youwes09/Moku/archive/refs/tags/v$VERSION.tar.gz" + echo "Fetching tarball sha256..." + TARBALL_SHA=$(curl -fsSL "$TARBALL_URL" | sha256sum | awk '{print $1}') + + sed -i "s/^pkgver=.*/pkgver=$VERSION/" "$PKGBUILD" + sed -i "s/^pkgrel=.*/pkgrel=1/" "$PKGBUILD" + sed -i "s/\(sha256sums=('\)[0-9a-f]\{64\}/\1$TARBALL_SHA/" "$PKGBUILD" + + grep -q "$TARBALL_SHA" "$PKGBUILD" \ + || { echo "ERROR: sha256 replacement failed"; exit 1; } + + echo "PKGBUILD -> $VERSION ($TARBALL_SHA)" + ''; + }; + in { - # Expose as both a runnable app and installable packages. apps = { - default = { - type = "app"; - program = "${moku}/bin/moku"; - }; - moku = { - type = "app"; - program = "${moku}/bin/moku"; - }; + default = { type = "app"; program = "${moku}/bin/moku"; }; + moku = { type = "app"; program = "${moku}/bin/moku"; }; + bump = { type = "app"; program = "${bumpScript}/bin/moku-bump"; }; + flatpak = { type = "app"; program = "${flatpakScript}/bin/moku-flatpak"; }; + pkgbuild-bump = { type = "app"; program = "${pkgbuildBumpScript}/bin/moku-pkgbuild-bump"; }; }; packages = { inherit moku frontend; default = moku; - appimage = nix-appimage.bundlers."${system}".default moku; }; devShells.default = pkgs.mkShell { @@ -214,26 +290,16 @@ xdg-utils ]; shellHook = '' - export APPIMAGE_EXTRACT_AND_RUN=1 export NO_STRIP=true export PKG_CONFIG_PATH="${pkgs.openssl.dev}/lib/pkgconfig''${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" export XDG_DATA_DIRS="${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}''${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}" - if [ ! -e /usr/bin/xdg-open ]; then - sudo ln -sf ${pkgs.xdg-utils}/bin/xdg-open /usr/bin/xdg-open - fi - - LINUXDEPLOY="$HOME/.cache/tauri/linuxdeploy-x86_64.AppImage" - LINUXDEPLOY_REAL="$HOME/.cache/tauri/linuxdeploy-x86_64.AppImage.real" - if [ -f "$LINUXDEPLOY" ] && [ ! -f "$LINUXDEPLOY_REAL" ]; then - mv "$LINUXDEPLOY" "$LINUXDEPLOY_REAL" - printf '#!/bin/sh\nexec ${pkgs.appimage-run}/bin/appimage-run "%s" "$@"\n' "$LINUXDEPLOY_REAL" > "$LINUXDEPLOY" - chmod +x "$LINUXDEPLOY" - echo "linuxdeploy wrapped with appimage-run" - fi - - echo "Moku dev shell" - echo " pnpm install && pnpm tauri:dev" + echo "Moku dev shell — pnpm install && pnpm tauri:dev" + echo "" + echo "Release:" + echo " nix run .#bump -- bump versions only" + echo " nix run .#flatpak -- full flatpak build" + echo " nix run .#pkgbuild-bump -- patch PKGBUILD (after tag push)" ''; }; diff --git a/package.json b/package.json index cc9a23a..9c47d11 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "tauri": "tauri" + "tauri": "tauri", + "tauri:dev": "tauri dev --config src-tauri/tauri.dev.conf.json" }, "dependencies": { "@tauri-apps/api": "^2.0.0", diff --git a/packaging/cargo-sources.json b/packaging/cargo-sources.json index 110bbe9..4bd0467 100644 --- a/packaging/cargo-sources.json +++ b/packaging/cargo-sources.json @@ -12,6 +12,19 @@ "dest": "cargo/vendor/adler2-2.0.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/aes/aes-0.8.4.crate", + "sha256": "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0", + "dest": "cargo/vendor/aes-0.8.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0\", \"files\": {}}", + "dest": "cargo/vendor/aes-0.8.4", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -77,6 +90,19 @@ "dest": "cargo/vendor/anyhow-1.0.102", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/arbitrary/arbitrary-1.4.2.crate", + "sha256": "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1", + "dest": "cargo/vendor/arbitrary-1.4.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1\", \"files\": {}}", + "dest": "cargo/vendor/arbitrary-1.4.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -285,6 +311,32 @@ "dest": "cargo/vendor/bytes-1.11.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/bzip2/bzip2-0.5.2.crate", + "sha256": "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47", + "dest": "cargo/vendor/bzip2-0.5.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47\", \"files\": {}}", + "dest": "cargo/vendor/bzip2-0.5.2", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/bzip2-sys/bzip2-sys-0.1.13+1.0.8.crate", + "sha256": "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14", + "dest": "cargo/vendor/bzip2-sys-0.1.13+1.0.8" + }, + { + "type": "inline", + "contents": "{\"package\": \"225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14\", \"files\": {}}", + "dest": "cargo/vendor/bzip2-sys-0.1.13+1.0.8", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -454,6 +506,19 @@ "dest": "cargo/vendor/chrono-0.4.43", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/cipher/cipher-0.4.4.crate", + "sha256": "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad", + "dest": "cargo/vendor/cipher-0.4.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad\", \"files\": {}}", + "dest": "cargo/vendor/cipher-0.4.4", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -467,6 +532,19 @@ "dest": "cargo/vendor/combine-4.6.7", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/constant_time_eq/constant_time_eq-0.3.1.crate", + "sha256": "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6", + "dest": "cargo/vendor/constant_time_eq-0.3.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6\", \"files\": {}}", + "dest": "cargo/vendor/constant_time_eq-0.3.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -493,6 +571,19 @@ "dest": "cargo/vendor/cookie-0.18.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/core-foundation/core-foundation-0.9.4.crate", + "sha256": "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f", + "dest": "cargo/vendor/core-foundation-0.9.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f\", \"files\": {}}", + "dest": "cargo/vendor/core-foundation-0.9.4", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -558,6 +649,32 @@ "dest": "cargo/vendor/cpufeatures-0.2.17", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/crc/crc-3.4.0.crate", + "sha256": "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d", + "dest": "cargo/vendor/crc-3.4.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d\", \"files\": {}}", + "dest": "cargo/vendor/crc-3.4.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/crc-catalog/crc-catalog-2.4.0.crate", + "sha256": "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5", + "dest": "cargo/vendor/crc-catalog-2.4.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5\", \"files\": {}}", + "dest": "cargo/vendor/crc-catalog-2.4.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -584,6 +701,32 @@ "dest": "cargo/vendor/crossbeam-channel-0.5.15", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/crossbeam-deque/crossbeam-deque-0.8.6.crate", + "sha256": "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51", + "dest": "cargo/vendor/crossbeam-deque-0.8.6" + }, + { + "type": "inline", + "contents": "{\"package\": \"9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51\", \"files\": {}}", + "dest": "cargo/vendor/crossbeam-deque-0.8.6", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/crossbeam-epoch/crossbeam-epoch-0.9.18.crate", + "sha256": "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e", + "dest": "cargo/vendor/crossbeam-epoch-0.9.18" + }, + { + "type": "inline", + "contents": "{\"package\": \"5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e\", \"files\": {}}", + "dest": "cargo/vendor/crossbeam-epoch-0.9.18", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -688,6 +831,19 @@ "dest": "cargo/vendor/darling_macro-0.21.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/deflate64/deflate64-0.1.11.crate", + "sha256": "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2", + "dest": "cargo/vendor/deflate64-0.1.11" + }, + { + "type": "inline", + "contents": "{\"package\": \"807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2\", \"files\": {}}", + "dest": "cargo/vendor/deflate64-0.1.11", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -701,6 +857,19 @@ "dest": "cargo/vendor/deranged-0.5.6", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/derive_arbitrary/derive_arbitrary-1.4.2.crate", + "sha256": "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a", + "dest": "cargo/vendor/derive_arbitrary-1.4.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a\", \"files\": {}}", + "dest": "cargo/vendor/derive_arbitrary-1.4.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -909,6 +1078,19 @@ "dest": "cargo/vendor/dyn-clone-1.0.20", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/either/either-1.15.0.crate", + "sha256": "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719", + "dest": "cargo/vendor/either-1.15.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719\", \"files\": {}}", + "dest": "cargo/vendor/either-1.15.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -987,6 +1169,19 @@ "dest": "cargo/vendor/errno-0.3.14", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/fastrand/fastrand-2.3.0.crate", + "sha256": "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be", + "dest": "cargo/vendor/fastrand-2.3.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be\", \"files\": {}}", + "dest": "cargo/vendor/fastrand-2.3.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1065,6 +1260,19 @@ "dest": "cargo/vendor/foldhash-0.1.5", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/foreign-types/foreign-types-0.3.2.crate", + "sha256": "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1", + "dest": "cargo/vendor/foreign-types-0.3.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1\", \"files\": {}}", + "dest": "cargo/vendor/foreign-types-0.3.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1091,6 +1299,19 @@ "dest": "cargo/vendor/foreign-types-macros-0.2.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/foreign-types-shared/foreign-types-shared-0.1.1.crate", + "sha256": "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b", + "dest": "cargo/vendor/foreign-types-shared-0.1.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b\", \"files\": {}}", + "dest": "cargo/vendor/foreign-types-shared-0.1.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1351,6 +1572,19 @@ "dest": "cargo/vendor/generic-array-0.14.7", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/gethostname/gethostname-1.1.0.crate", + "sha256": "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8", + "dest": "cargo/vendor/gethostname-1.1.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8\", \"files\": {}}", + "dest": "cargo/vendor/gethostname-1.1.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1533,6 +1767,19 @@ "dest": "cargo/vendor/gtk3-macros-0.18.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/h2/h2-0.4.13.crate", + "sha256": "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54", + "dest": "cargo/vendor/h2-0.4.13" + }, + { + "type": "inline", + "contents": "{\"package\": \"2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54\", \"files\": {}}", + "dest": "cargo/vendor/h2-0.4.13", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1611,6 +1858,19 @@ "dest": "cargo/vendor/hex-0.4.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/hmac/hmac-0.12.1.crate", + "sha256": "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e", + "dest": "cargo/vendor/hmac-0.12.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e\", \"files\": {}}", + "dest": "cargo/vendor/hmac-0.12.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1689,6 +1949,32 @@ "dest": "cargo/vendor/hyper-1.8.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/hyper-rustls/hyper-rustls-0.27.7.crate", + "sha256": "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58", + "dest": "cargo/vendor/hyper-rustls-0.27.7" + }, + { + "type": "inline", + "contents": "{\"package\": \"e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58\", \"files\": {}}", + "dest": "cargo/vendor/hyper-rustls-0.27.7", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/hyper-tls/hyper-tls-0.6.0.crate", + "sha256": "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0", + "dest": "cargo/vendor/hyper-tls-0.6.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0\", \"files\": {}}", + "dest": "cargo/vendor/hyper-tls-0.6.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -1923,6 +2209,19 @@ "dest": "cargo/vendor/infer-0.19.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/inout/inout-0.1.4.crate", + "sha256": "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01", + "dest": "cargo/vendor/inout-0.1.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01\", \"files\": {}}", + "dest": "cargo/vendor/inout-0.1.4", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2040,6 +2339,19 @@ "dest": "cargo/vendor/jni-sys-0.3.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/jobserver/jobserver-0.1.34.crate", + "sha256": "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33", + "dest": "cargo/vendor/jobserver-0.1.34" + }, + { + "type": "inline", + "contents": "{\"package\": \"9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33\", \"files\": {}}", + "dest": "cargo/vendor/jobserver-0.1.34", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2196,6 +2508,19 @@ "dest": "cargo/vendor/libredox-0.1.12", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/linux-raw-sys/linux-raw-sys-0.12.1.crate", + "sha256": "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53", + "dest": "cargo/vendor/linux-raw-sys-0.12.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53\", \"files\": {}}", + "dest": "cargo/vendor/linux-raw-sys-0.12.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2235,6 +2560,32 @@ "dest": "cargo/vendor/log-0.4.29", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/lzma-rs/lzma-rs-0.3.0.crate", + "sha256": "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e", + "dest": "cargo/vendor/lzma-rs-0.3.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e\", \"files\": {}}", + "dest": "cargo/vendor/lzma-rs-0.3.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/lzma-sys/lzma-sys-0.1.20.crate", + "sha256": "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27", + "dest": "cargo/vendor/lzma-sys-0.1.20" + }, + { + "type": "inline", + "contents": "{\"package\": \"5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27\", \"files\": {}}", + "dest": "cargo/vendor/lzma-sys-0.1.20", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2365,6 +2716,19 @@ "dest": "cargo/vendor/muda-0.17.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/native-tls/native-tls-0.2.18.crate", + "sha256": "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2", + "dest": "cargo/vendor/native-tls-0.2.18" + }, + { + "type": "inline", + "contents": "{\"package\": \"465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2\", \"files\": {}}", + "dest": "cargo/vendor/native-tls-0.2.18", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2420,14 +2784,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/nix/nix-0.29.0.crate", - "sha256": "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46", - "dest": "cargo/vendor/nix-0.29.0" + "url": "https://static.crates.io/crates/nix/nix-0.30.1.crate", + "sha256": "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6", + "dest": "cargo/vendor/nix-0.30.1" }, { "type": "inline", - "contents": "{\"package\": \"71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46\", \"files\": {}}", - "dest": "cargo/vendor/nix-0.29.0", + "contents": "{\"package\": \"74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6\", \"files\": {}}", + "dest": "cargo/vendor/nix-0.30.1", "dest-filename": ".cargo-checksum.json" }, { @@ -2443,6 +2807,19 @@ "dest": "cargo/vendor/nodrop-0.1.14", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/ntapi/ntapi-0.4.3.crate", + "sha256": "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae", + "dest": "cargo/vendor/ntapi-0.4.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae\", \"files\": {}}", + "dest": "cargo/vendor/ntapi-0.4.3", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2586,6 +2963,19 @@ "dest": "cargo/vendor/objc2-core-image-0.3.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/objc2-core-location/objc2-core-location-0.3.2.crate", + "sha256": "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009", + "dest": "cargo/vendor/objc2-core-location-0.3.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009\", \"files\": {}}", + "dest": "cargo/vendor/objc2-core-location-0.3.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2716,6 +3106,19 @@ "dest": "cargo/vendor/objc2-ui-kit-0.3.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/objc2-user-notifications/objc2-user-notifications-0.3.2.crate", + "sha256": "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e", + "dest": "cargo/vendor/objc2-user-notifications-0.3.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e\", \"files\": {}}", + "dest": "cargo/vendor/objc2-user-notifications-0.3.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2755,6 +3158,58 @@ "dest": "cargo/vendor/open-5.3.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/openssl/openssl-0.10.76.crate", + "sha256": "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf", + "dest": "cargo/vendor/openssl-0.10.76" + }, + { + "type": "inline", + "contents": "{\"package\": \"951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf\", \"files\": {}}", + "dest": "cargo/vendor/openssl-0.10.76", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/openssl-macros/openssl-macros-0.1.1.crate", + "sha256": "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c", + "dest": "cargo/vendor/openssl-macros-0.1.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c\", \"files\": {}}", + "dest": "cargo/vendor/openssl-macros-0.1.1", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/openssl-probe/openssl-probe-0.2.1.crate", + "sha256": "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe", + "dest": "cargo/vendor/openssl-probe-0.2.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe\", \"files\": {}}", + "dest": "cargo/vendor/openssl-probe-0.2.1", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/openssl-sys/openssl-sys-0.9.112.crate", + "sha256": "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb", + "dest": "cargo/vendor/openssl-sys-0.9.112" + }, + { + "type": "inline", + "contents": "{\"package\": \"57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb\", \"files\": {}}", + "dest": "cargo/vendor/openssl-sys-0.9.112", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2768,6 +3223,19 @@ "dest": "cargo/vendor/option-ext-0.2.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/os_info/os_info-3.14.0.crate", + "sha256": "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224", + "dest": "cargo/vendor/os_info-3.14.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224\", \"files\": {}}", + "dest": "cargo/vendor/os_info-3.14.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -2846,6 +3314,19 @@ "dest": "cargo/vendor/pathdiff-0.2.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/pbkdf2/pbkdf2-0.12.2.crate", + "sha256": "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2", + "dest": "cargo/vendor/pbkdf2-0.12.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2\", \"files\": {}}", + "dest": "cargo/vendor/pbkdf2-0.12.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3405,6 +3886,32 @@ "dest": "cargo/vendor/raw-window-handle-0.6.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rayon/rayon-1.11.0.crate", + "sha256": "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f", + "dest": "cargo/vendor/rayon-1.11.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f\", \"files\": {}}", + "dest": "cargo/vendor/rayon-1.11.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rayon-core/rayon-core-1.13.0.crate", + "sha256": "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91", + "dest": "cargo/vendor/rayon-core-1.13.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91\", \"files\": {}}", + "dest": "cargo/vendor/rayon-core-1.13.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3509,6 +4016,19 @@ "dest": "cargo/vendor/regex-syntax-0.8.9", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/reqwest/reqwest-0.12.28.crate", + "sha256": "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147", + "dest": "cargo/vendor/reqwest-0.12.28" + }, + { + "type": "inline", + "contents": "{\"package\": \"eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147\", \"files\": {}}", + "dest": "cargo/vendor/reqwest-0.12.28", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3522,6 +4042,19 @@ "dest": "cargo/vendor/reqwest-0.13.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/ring/ring-0.17.14.crate", + "sha256": "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7", + "dest": "cargo/vendor/ring-0.17.14" + }, + { + "type": "inline", + "contents": "{\"package\": \"a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7\", \"files\": {}}", + "dest": "cargo/vendor/ring-0.17.14", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3535,6 +4068,58 @@ "dest": "cargo/vendor/rustc_version-0.4.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rustix/rustix-1.1.4.crate", + "sha256": "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190", + "dest": "cargo/vendor/rustix-1.1.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190\", \"files\": {}}", + "dest": "cargo/vendor/rustix-1.1.4", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rustls/rustls-0.23.37.crate", + "sha256": "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4", + "dest": "cargo/vendor/rustls-0.23.37" + }, + { + "type": "inline", + "contents": "{\"package\": \"758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4\", \"files\": {}}", + "dest": "cargo/vendor/rustls-0.23.37", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rustls-pki-types/rustls-pki-types-1.14.0.crate", + "sha256": "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd", + "dest": "cargo/vendor/rustls-pki-types-1.14.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd\", \"files\": {}}", + "dest": "cargo/vendor/rustls-pki-types-1.14.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/rustls-webpki/rustls-webpki-0.103.10.crate", + "sha256": "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef", + "dest": "cargo/vendor/rustls-webpki-0.103.10" + }, + { + "type": "inline", + "contents": "{\"package\": \"df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef\", \"files\": {}}", + "dest": "cargo/vendor/rustls-webpki-0.103.10", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3548,6 +4133,19 @@ "dest": "cargo/vendor/rustversion-1.0.22", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/ryu/ryu-1.0.23.crate", + "sha256": "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f", + "dest": "cargo/vendor/ryu-1.0.23" + }, + { + "type": "inline", + "contents": "{\"package\": \"9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f\", \"files\": {}}", + "dest": "cargo/vendor/ryu-1.0.23", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3561,6 +4159,19 @@ "dest": "cargo/vendor/same-file-1.0.6", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/schannel/schannel-0.1.29.crate", + "sha256": "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939", + "dest": "cargo/vendor/schannel-0.1.29" + }, + { + "type": "inline", + "contents": "{\"package\": \"91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939\", \"files\": {}}", + "dest": "cargo/vendor/schannel-0.1.29", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3626,6 +4237,32 @@ "dest": "cargo/vendor/scopeguard-1.2.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/security-framework/security-framework-3.7.0.crate", + "sha256": "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d", + "dest": "cargo/vendor/security-framework-3.7.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d\", \"files\": {}}", + "dest": "cargo/vendor/security-framework-3.7.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/security-framework-sys/security-framework-sys-2.17.0.crate", + "sha256": "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3", + "dest": "cargo/vendor/security-framework-sys-2.17.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3\", \"files\": {}}", + "dest": "cargo/vendor/security-framework-sys-2.17.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3769,6 +4406,19 @@ "dest": "cargo/vendor/serde_spanned-1.0.4", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/serde_urlencoded/serde_urlencoded-0.7.1.crate", + "sha256": "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd", + "dest": "cargo/vendor/serde_urlencoded-0.7.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd\", \"files\": {}}", + "dest": "cargo/vendor/serde_urlencoded-0.7.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -3834,6 +4484,19 @@ "dest": "cargo/vendor/servo_arc-0.2.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/sha1/sha1-0.10.6.crate", + "sha256": "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba", + "dest": "cargo/vendor/sha1-0.10.6" + }, + { + "type": "inline", + "contents": "{\"package\": \"e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba\", \"files\": {}}", + "dest": "cargo/vendor/sha1-0.10.6", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4081,6 +4744,19 @@ "dest": "cargo/vendor/strsim-0.11.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/subtle/subtle-2.6.1.crate", + "sha256": "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292", + "dest": "cargo/vendor/subtle-2.6.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292\", \"files\": {}}", + "dest": "cargo/vendor/subtle-2.6.1", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4146,6 +4822,58 @@ "dest": "cargo/vendor/synstructure-0.13.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/sys-locale/sys-locale-0.3.2.crate", + "sha256": "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4", + "dest": "cargo/vendor/sys-locale-0.3.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4\", \"files\": {}}", + "dest": "cargo/vendor/sys-locale-0.3.2", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/sysinfo/sysinfo-0.32.1.crate", + "sha256": "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af", + "dest": "cargo/vendor/sysinfo-0.32.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af\", \"files\": {}}", + "dest": "cargo/vendor/sysinfo-0.32.1", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/system-configuration/system-configuration-0.7.0.crate", + "sha256": "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b", + "dest": "cargo/vendor/system-configuration-0.7.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b\", \"files\": {}}", + "dest": "cargo/vendor/system-configuration-0.7.0", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/system-configuration-sys/system-configuration-sys-0.6.0.crate", + "sha256": "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4", + "dest": "cargo/vendor/system-configuration-sys-0.6.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4\", \"files\": {}}", + "dest": "cargo/vendor/system-configuration-sys-0.6.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4263,6 +4991,19 @@ "dest": "cargo/vendor/tauri-plugin-2.5.3", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/tauri-plugin-os/tauri-plugin-os-2.3.2.crate", + "sha256": "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997", + "dest": "cargo/vendor/tauri-plugin-os-2.3.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997\", \"files\": {}}", + "dest": "cargo/vendor/tauri-plugin-os-2.3.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4328,6 +5069,19 @@ "dest": "cargo/vendor/tauri-winres-0.3.5", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/tempfile/tempfile-3.27.0.crate", + "sha256": "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd", + "dest": "cargo/vendor/tempfile-3.27.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd\", \"files\": {}}", + "dest": "cargo/vendor/tempfile-3.27.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4458,6 +5212,32 @@ "dest": "cargo/vendor/tokio-1.49.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/tokio-native-tls/tokio-native-tls-0.3.1.crate", + "sha256": "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2", + "dest": "cargo/vendor/tokio-native-tls-0.3.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2\", \"files\": {}}", + "dest": "cargo/vendor/tokio-native-tls-0.3.1", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/tokio-rustls/tokio-rustls-0.26.4.crate", + "sha256": "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61", + "dest": "cargo/vendor/tokio-rustls-0.26.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61\", \"files\": {}}", + "dest": "cargo/vendor/tokio-rustls-0.26.4", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4822,6 +5602,19 @@ "dest": "cargo/vendor/unicode-xid-0.2.6", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/untrusted/untrusted-0.9.0.crate", + "sha256": "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1", + "dest": "cargo/vendor/untrusted-0.9.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1\", \"files\": {}}", + "dest": "cargo/vendor/untrusted-0.9.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -4887,6 +5680,19 @@ "dest": "cargo/vendor/uuid-1.21.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/vcpkg/vcpkg-0.2.15.crate", + "sha256": "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426", + "dest": "cargo/vendor/vcpkg-0.2.15" + }, + { + "type": "inline", + "contents": "{\"package\": \"accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426\", \"files\": {}}", + "dest": "cargo/vendor/vcpkg-0.2.15", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5108,6 +5914,19 @@ "dest": "cargo/vendor/wasm-metadata-0.244.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/wasm-streams/wasm-streams-0.4.2.crate", + "sha256": "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65", + "dest": "cargo/vendor/wasm-streams-0.4.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65\", \"files\": {}}", + "dest": "cargo/vendor/wasm-streams-0.4.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5277,6 +6096,19 @@ "dest": "cargo/vendor/window-vibrancy-0.6.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows/windows-0.57.0.crate", + "sha256": "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143", + "dest": "cargo/vendor/windows-0.57.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143\", \"files\": {}}", + "dest": "cargo/vendor/windows-0.57.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5303,6 +6135,19 @@ "dest": "cargo/vendor/windows-collections-0.2.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows-core/windows-core-0.57.0.crate", + "sha256": "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d", + "dest": "cargo/vendor/windows-core-0.57.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d\", \"files\": {}}", + "dest": "cargo/vendor/windows-core-0.57.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5342,6 +6187,19 @@ "dest": "cargo/vendor/windows-future-0.2.1", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows-implement/windows-implement-0.57.0.crate", + "sha256": "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7", + "dest": "cargo/vendor/windows-implement-0.57.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7\", \"files\": {}}", + "dest": "cargo/vendor/windows-implement-0.57.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5355,6 +6213,19 @@ "dest": "cargo/vendor/windows-implement-0.60.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows-interface/windows-interface-0.57.0.crate", + "sha256": "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7", + "dest": "cargo/vendor/windows-interface-0.57.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7\", \"files\": {}}", + "dest": "cargo/vendor/windows-interface-0.57.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5407,6 +6278,32 @@ "dest": "cargo/vendor/windows-numerics-0.2.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows-registry/windows-registry-0.6.1.crate", + "sha256": "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720", + "dest": "cargo/vendor/windows-registry-0.6.1" + }, + { + "type": "inline", + "contents": "{\"package\": \"02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720\", \"files\": {}}", + "dest": "cargo/vendor/windows-registry-0.6.1", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows-result/windows-result-0.1.2.crate", + "sha256": "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8", + "dest": "cargo/vendor/windows-result-0.1.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8\", \"files\": {}}", + "dest": "cargo/vendor/windows-result-0.1.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -5485,6 +6382,19 @@ "dest": "cargo/vendor/windows-sys-0.48.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/windows-sys/windows-sys-0.52.0.crate", + "sha256": "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d", + "dest": "cargo/vendor/windows-sys-0.52.0" + }, + { + "type": "inline", + "contents": "{\"package\": \"282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d\", \"files\": {}}", + "dest": "cargo/vendor/windows-sys-0.52.0", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -6161,6 +7071,19 @@ "dest": "cargo/vendor/x11-dl-2.21.0", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/xz2/xz2-0.1.7.crate", + "sha256": "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2", + "dest": "cargo/vendor/xz2-0.1.7" + }, + { + "type": "inline", + "contents": "{\"package\": \"388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2\", \"files\": {}}", + "dest": "cargo/vendor/xz2-0.1.7", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -6239,6 +7162,32 @@ "dest": "cargo/vendor/zerofrom-derive-0.1.6", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zeroize/zeroize-1.8.2.crate", + "sha256": "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0", + "dest": "cargo/vendor/zeroize-1.8.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0\", \"files\": {}}", + "dest": "cargo/vendor/zeroize-1.8.2", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zeroize_derive/zeroize_derive-1.4.3.crate", + "sha256": "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e", + "dest": "cargo/vendor/zeroize_derive-1.4.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e\", \"files\": {}}", + "dest": "cargo/vendor/zeroize_derive-1.4.3", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -6278,6 +7227,19 @@ "dest": "cargo/vendor/zerovec-derive-0.11.2", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zip/zip-2.4.2.crate", + "sha256": "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50", + "dest": "cargo/vendor/zip-2.4.2" + }, + { + "type": "inline", + "contents": "{\"package\": \"fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50\", \"files\": {}}", + "dest": "cargo/vendor/zip-2.4.2", + "dest-filename": ".cargo-checksum.json" + }, { "type": "archive", "archive-type": "tar-gzip", @@ -6291,6 +7253,58 @@ "dest": "cargo/vendor/zmij-1.0.21", "dest-filename": ".cargo-checksum.json" }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zopfli/zopfli-0.8.3.crate", + "sha256": "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249", + "dest": "cargo/vendor/zopfli-0.8.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249\", \"files\": {}}", + "dest": "cargo/vendor/zopfli-0.8.3", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zstd/zstd-0.13.3.crate", + "sha256": "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a", + "dest": "cargo/vendor/zstd-0.13.3" + }, + { + "type": "inline", + "contents": "{\"package\": \"e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a\", \"files\": {}}", + "dest": "cargo/vendor/zstd-0.13.3", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zstd-safe/zstd-safe-7.2.4.crate", + "sha256": "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d", + "dest": "cargo/vendor/zstd-safe-7.2.4" + }, + { + "type": "inline", + "contents": "{\"package\": \"8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d\", \"files\": {}}", + "dest": "cargo/vendor/zstd-safe-7.2.4", + "dest-filename": ".cargo-checksum.json" + }, + { + "type": "archive", + "archive-type": "tar-gzip", + "url": "https://static.crates.io/crates/zstd-sys/zstd-sys-2.0.16+zstd.1.5.7.crate", + "sha256": "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748", + "dest": "cargo/vendor/zstd-sys-2.0.16+zstd.1.5.7" + }, + { + "type": "inline", + "contents": "{\"package\": \"91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748\", \"files\": {}}", + "dest": "cargo/vendor/zstd-sys-2.0.16+zstd.1.5.7", + "dest-filename": ".cargo-checksum.json" + }, { "type": "inline", "contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n", diff --git a/packaging/frontend-dist.tar.gz b/packaging/frontend-dist.tar.gz deleted file mode 100644 index 159287a..0000000 Binary files a/packaging/frontend-dist.tar.gz and /dev/null differ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d78feb4..7422033 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -230,66 +230,79 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -367,30 +380,35 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tauri-apps/cli-linux-arm64-musl@2.10.1': resolution: {integrity: sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tauri-apps/cli-linux-riscv64-gnu@2.10.1': resolution: {integrity: sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] + libc: [glibc] '@tauri-apps/cli-linux-x64-gnu@2.10.1': resolution: {integrity: sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tauri-apps/cli-linux-x64-musl@2.10.1': resolution: {integrity: sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tauri-apps/cli-win32-arm64-msvc@2.10.1': resolution: {integrity: sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 96f7c0a..8e8f5d7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1816,7 +1816,7 @@ dependencies = [ [[package]] name = "moku" -version = "0.3.0" +version = "0.4.0" dependencies = [ "dirs 5.0.1", "serde", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 48e777b..d7814aa 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "moku" -version = "0.3.0" +version = "0.4.0" edition = "2021" [lib] name = "moku_lib" -crate-type = ["staticlib", "cdylib", "rlib"] +crate-type = ["cdylib", "rlib"] [[bin]] name = "moku" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4d8025a..75da45d 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -1,7 +1,7 @@ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", - "description": "Allow launching suwayomi-server sidecar", + "description": "Default permissions for Moku", "windows": ["main"], "permissions": [ "core:default", @@ -10,11 +10,12 @@ { "identifier": "shell:allow-spawn", "allow": [ - { - "name": "binaries/suwayomi-server", - "sidecar": true - } + { "name": "tachidesk-server" }, + { "name": "suwayomi-server" }, + { "name": "suwayomi-server-aarch64-apple-darwin" }, + { "name": "suwayomi-server-x86_64-apple-darwin" }, + { "name": "javaw" } ] } ] -} \ No newline at end of file +} diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..b2f77f2 Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..d2961aa Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..f1db733 Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..45a6b8f Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..136d4a3 Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..864fd32 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..b26b59c Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..2b67df8 Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..9e9888b Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6ae4bef..7d966d1 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -16,16 +16,20 @@ pub struct StorageInfo { path: String, } +#[derive(Serialize)] +#[serde(tag = "kind", content = "message")] +pub enum SpawnError { + NotConfigured(String), + SpawnFailed(String), +} + fn resolve_downloads_path(downloads_path: &str) -> PathBuf { if !downloads_path.trim().is_empty() { return PathBuf::from(downloads_path); } let base = std::env::var("XDG_DATA_HOME") .map(PathBuf::from) - .unwrap_or_else(|_| { - dirs::data_dir() - .unwrap_or_else(|| PathBuf::from("/")) - }); + .unwrap_or_else(|_| dirs::data_dir().unwrap_or_else(|| PathBuf::from("/"))); base.join("Tachidesk/downloads") } @@ -45,7 +49,9 @@ fn get_storage_info(downloads_path: String) -> Result { 0 }; - let stat_path = if path.exists() { path.clone() } else { + let stat_path = if path.exists() { + path.clone() + } else { dirs::home_dir().unwrap_or_else(|| PathBuf::from("/")) }; @@ -56,20 +62,14 @@ fn get_storage_info(downloads_path: String) -> Result { .max_by_key(|d| d.mount_point().as_os_str().len()) .ok_or_else(|| "Could not find disk for path".to_string())?; - let total_bytes = disk.total_space(); - let free_bytes = disk.available_space(); - Ok(StorageInfo { manga_bytes, - total_bytes, - free_bytes, + total_bytes: disk.total_space(), + free_bytes: disk.available_space(), path: path.to_string_lossy().into_owned(), }) } -/// Returns the true OS-level scale factor for the main window. -/// On Linux this bypasses WebKitGTK's unreliable devicePixelRatio. -/// On macOS the value comes directly from the native window. #[tauri::command] fn get_scale_factor(window: tauri::Window) -> f64 { window.scale_factor().unwrap_or(1.0) @@ -77,26 +77,21 @@ fn get_scale_factor(window: tauri::Window) -> f64 { fn kill_tachidesk(app: &tauri::AppHandle) { let state = app.state::(); - let mut guard = state.0.lock().unwrap(); - if let Some(child) = guard.take() { + if let Some(child) = state.0.lock().unwrap().take() { let _ = child.kill(); - println!("Killed tracked server child."); } #[cfg(target_os = "windows")] let _ = std::process::Command::new("taskkill") - .args(["/F", "/FI", "IMAGENAME eq tachidesk*"]) + .args(["/F", "/FI", "IMAGENAME eq java*"]) .status(); #[cfg(not(target_os = "windows"))] let _ = std::process::Command::new("pkill") - .arg("-f") - .arg("tachidesk") + .args(["-f", "tachidesk"]) .status(); } -/// The default server.conf we seed on first launch. -/// Mirrors the Flatpak wrapper: headless, no tray, no browser pop-up. const DEFAULT_SERVER_CONF: &str = r#"server.ip = "127.0.0.1" server.port = 4567 server.webUIEnabled = false @@ -114,9 +109,6 @@ server.maxSourcesInParallel = 6 server.extensionRepos = [] "#; -/// Ensure the Suwayomi data dir and server.conf exist, and that the three -/// keys that cause GUI/JCEF crashes are always set to safe values. -/// This mirrors the shell-script logic in the Flatpak's tachidesk-server wrapper. fn seed_server_conf(data_dir: &PathBuf) { let conf_path = data_dir.join("server.conf"); @@ -131,97 +123,73 @@ fn seed_server_conf(data_dir: &PathBuf) { return; } - // Conf already exists — patch the three critical keys in-place. let Ok(contents) = std::fs::read_to_string(&conf_path) else { return }; let patched = patch_conf_key( patch_conf_key( - patch_conf_key( - contents, - "server.webUIEnabled", - "false", - ), - "server.initialOpenInBrowserEnabled", - "false", + patch_conf_key(contents, "server.webUIEnabled", "false"), + "server.initialOpenInBrowserEnabled", "false", ), - "server.systemTrayEnabled", - "false", + "server.systemTrayEnabled", "false", ); let _ = std::fs::write(&conf_path, patched); } -/// Replace `key = ` in a HOCON/properties-style conf, or append it -/// if the key is absent. -fn patch_conf_key(mut text: String, key: &str, value: &str) -> String { +fn patch_conf_key(text: String, key: &str, value: &str) -> String { let replacement = format!("{key} = {value}"); - // Find a line that starts with the key (tolerant of surrounding whitespace) - if let Some(pos) = text.lines().position(|l| l.trim_start().starts_with(key)) { - let lines: Vec<&str> = text.lines().collect(); - // We need an owned replacement; rebuild from scratch. - let owned: Vec = lines + let lines: Vec<&str> = text.lines().collect(); + + if let Some(pos) = lines.iter().position(|l| l.trim_start().starts_with(key)) { + let mut out = lines .iter() .enumerate() - .map(|(i, l)| { - if i == pos { replacement.clone() } else { l.to_string() } - }) - .collect(); - return owned.join("\n"); + .map(|(i, l)| if i == pos { replacement.as_str() } else { l }) + .collect::>() + .join("\n"); + out.push('\n'); + return out; } - // Key absent — append. - if !text.ends_with('\n') { text.push('\n'); } - text.push_str(&replacement); - text.push('\n'); - text + + let mut out = text; + if !out.ends_with('\n') { out.push('\n'); } + out.push_str(&replacement); + out.push('\n'); + out } -/// Resolve the Suwayomi data directory. -/// -/// - Linux: $XDG_DATA_HOME/moku/tachidesk (matches Flatpak path) -/// - macOS: ~/Library/Application Support/dev.moku.app/tachidesk fn suwayomi_data_dir() -> PathBuf { + #[cfg(target_os = "windows")] + { + dirs::data_dir() + .unwrap_or_else(|| PathBuf::from("C:\\ProgramData")) + .join("moku\\tachidesk") + } #[cfg(target_os = "macos")] { dirs::data_dir() .unwrap_or_else(|| dirs::home_dir().unwrap_or_else(|| PathBuf::from("~"))) .join("dev.moku.app/tachidesk") } - #[cfg(not(target_os = "macos"))] + #[cfg(not(any(target_os = "windows", target_os = "macos")))] { let base = std::env::var("XDG_DATA_HOME") .map(PathBuf::from) - .unwrap_or_else(|_| { - dirs::data_dir().unwrap_or_else(|| PathBuf::from("/tmp")) - }); + .unwrap_or_else(|_| dirs::data_dir().unwrap_or_else(|| PathBuf::from("/tmp"))); base.join("moku/tachidesk") } } -/// Everything needed to spawn the server process. struct ServerInvocation { - /// Path to the executable (javaw.exe on Windows, the sidecar script on macOS/Linux). - bin: std::ffi::OsString, - /// Extra args prepended before the Suwayomi rootDir flag. - /// On Windows: ["-jar", ""] - /// Elsewhere: [] + bin: std::ffi::OsString, prefix_args: Vec, - /// Working directory for the child process. - /// On Windows this must be the bundle folder so javaw can find the JRE and jar. - /// Elsewhere: None (inherit). working_dir: Option, } -/// Resolve the server binary path. -/// -/// If the frontend passes a non-empty `binary` string (user override in -/// Settings) we always use that — on Linux this is the nixpkgs/Flatpak path. -/// -/// Otherwise we look for the Tauri-bundled sidecar inside the resource dir -/// and, on Windows, build the javaw + jar invocation from the suwayomi-bundle. fn resolve_server_binary( binary: &str, app: &tauri::AppHandle, -) -> Result { +) -> Result { if !binary.trim().is_empty() { return Ok(ServerInvocation { bin: std::ffi::OsString::from(binary), @@ -233,18 +201,17 @@ fn resolve_server_binary( let resource_dir = app .path() .resource_dir() - .map_err(|e| format!("Could not locate resource dir: {e}"))?; + .map_err(|e| SpawnError::SpawnFailed(format!("Could not locate resource dir: {e}")))?; - // ── Windows: invoke the bundled javaw.exe with -jar Suwayomi-Launcher.jar ── #[cfg(target_os = "windows")] { - let sidecar = resource_dir.join("suwayomi-server-x86_64-pc-windows-msvc.exe"); let bundle_dir = resource_dir.join("suwayomi-bundle"); - let jar = bundle_dir.join("Suwayomi-Launcher.jar"); + let javaw = bundle_dir.join("jre").join("bin").join("javaw.exe"); + let jar = bundle_dir.join("Suwayomi-Launcher.jar"); - if sidecar.exists() && jar.exists() { + if javaw.exists() && jar.exists() { return Ok(ServerInvocation { - bin: sidecar.into_os_string(), + bin: javaw.into_os_string(), prefix_args: vec![ "-jar".to_string(), jar.to_string_lossy().into_owned(), @@ -252,16 +219,22 @@ fn resolve_server_binary( working_dir: Some(bundle_dir), }); } + + return Err(SpawnError::NotConfigured( + "No bundled server found. Set the server path in Settings.".to_string(), + )); } - // ── macOS / Linux: sidecar script is self-contained ── + #[cfg(target_os = "macos")] let candidates = [ "suwayomi-server-aarch64-apple-darwin", "suwayomi-server-x86_64-apple-darwin", - // plain name as a dev/Linux fallback - "suwayomi-server", ]; + #[cfg(not(any(target_os = "windows", target_os = "macos")))] + let candidates = ["suwayomi-server"]; + + #[cfg(not(target_os = "windows"))] for name in &candidates { let p = resource_dir.join(name); if p.exists() { @@ -273,58 +246,83 @@ fn resolve_server_binary( } } - Err("Suwayomi server binary not found. Please set the path in Settings.".to_string()) + // Fall back to PATH — covers Nix, distro packages, and any system install. + { + #[cfg(target_os = "windows")] + let which_cmd = "where"; + #[cfg(not(target_os = "windows"))] + let which_cmd = "which"; + + for name in &["tachidesk-server", "suwayomi-server"] { + if std::process::Command::new(which_cmd) + .arg(name) + .output() + .map(|o| o.status.success()) + .unwrap_or(false) + { + return Ok(ServerInvocation { + bin: std::ffi::OsString::from(name), + prefix_args: vec![], + working_dir: None, + }); + } + } + } + + Err(SpawnError::NotConfigured( + "Server binary not found. Set the path in Settings.".to_string(), + )) } #[tauri::command] -fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), String> { - let state = app.state::(); +fn spawn_server(binary: String, app: tauri::AppHandle) -> Result<(), SpawnError> { { + let state = app.state::(); let guard = state.0.lock().unwrap(); if guard.is_some() { - println!("Server already running, skipping spawn."); return Ok(()); } } - // Seed server.conf before launching so Suwayomi starts in headless mode. let data_dir = suwayomi_data_dir(); seed_server_conf(&data_dir); let invocation = resolve_server_binary(&binary, &app)?; - let shell = app.shell(); + let bin_display = invocation.bin.clone(); let rootdir_flag = format!( "-Dsuwayomi.tachidesk.config.server.rootDir={}", data_dir.to_string_lossy() ); - // Build the full arg list: prefix_args (e.g. -jar foo.jar) + rootDir flag. - let args: Vec = invocation.prefix_args.into_iter().chain(std::iter::once(rootdir_flag)).collect(); + let args: Vec = invocation.prefix_args + .into_iter() + .chain(std::iter::once(rootdir_flag)) + .collect(); - // On Windows, set the working directory to the bundle folder so javaw.exe - // can resolve the JRE and jar relative paths correctly. - let cmd = shell + let cmd = app.shell() .command(&invocation.bin) .env("JAVA_TOOL_OPTIONS", "-Djava.awt.headless=true") .args(&args) - .current_dir(invocation.working_dir.unwrap_or_else(|| std::env::current_dir().unwrap_or_default())); + .current_dir( + invocation.working_dir + .unwrap_or_else(|| std::env::current_dir().unwrap_or_default()) + ); match cmd.spawn() { Ok((_rx, child)) => { - println!("Spawned server: {:?}", invocation.bin); - let mut guard = state.0.lock().unwrap(); - *guard = Some(child); + println!("Spawned server: {:?}", bin_display); + let state = app.state::(); + *state.0.lock().unwrap() = Some(child); Ok(()) } Err(e) => { - eprintln!("Failed to spawn {:?}: {}", invocation.bin, e); - Err(e.to_string()) + eprintln!("Failed to spawn {:?}: {}", bin_display, e); + Err(SpawnError::SpawnFailed(e.to_string())) } } } - #[tauri::command] fn kill_server(app: tauri::AppHandle) -> Result<(), String> { kill_tachidesk(&app); @@ -350,4 +348,4 @@ pub fn run() { }) .run(tauri::generate_context!()) .expect("error while running moku"); -} \ No newline at end of file +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index f66c6e2..a637166 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,12 +1,10 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Moku", - "version": "0.3.0", + "version": "0.4.0", "identifier": "dev.moku.app", "build": { "frontendDist": "../dist", - "devUrl": "http://localhost:1420", - "beforeDevCommand": "pnpm dev", "beforeBuildCommand": "pnpm build" }, "app": { @@ -28,14 +26,21 @@ }, "bundle": { "active": true, - "targets": ["appimage"], + "targets": ["appimage", "nsis", "deb"], "icon": [ "icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", - "icons/icon.ico" - ] + "icons/icon.ico", + "icons/icon.png" + ], + "windows": { + "nsis": { + "installerIcon": "icons/icon.ico", + "installMode": "currentUser" + } + } }, "plugins": { "shell": { diff --git a/src/App.svelte b/src/App.svelte index 05b9fde..caa55ee 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -16,11 +16,12 @@ const MAX_ATTEMPTS = 30; - let serverProbeOk = $state(!store.settings.autoStartServer); - let appReady = $state(!store.settings.autoStartServer); - let failed = $state(false); - let idle = $state(false); - let devSplash = $state(false); + let serverProbeOk = $state(!store.settings.autoStartServer); + let appReady = $state(!store.settings.autoStartServer); + let failed = $state(false); + let notConfigured = $state(false); + let idle = $state(false); + let devSplash = $state(false); let prevQueue: DownloadQueueItem[] = []; let idleTimer: ReturnType | null = null; @@ -68,7 +69,6 @@ const scale = store.settings.uiScale * 1.5; document.documentElement.style.zoom = `${scale}%`; document.documentElement.style.setProperty("--ui-scale", String(scale)); - // --visual-vh gives true viewport height independent of zoom document.documentElement.style.setProperty("--visual-vh", `${window.innerHeight / (scale / 100)}px`); }); @@ -90,8 +90,13 @@ (window as any).__mokuShowSplash = () => devSplash = true; if (store.settings.autoStartServer) { - invoke("spawn_server", { binary: store.settings.serverBinary }).catch(err => - console.warn("Could not start server:", err)); + invoke("spawn_server", { binary: store.settings.serverBinary }).catch((err: any) => { + if (err?.kind === "NotConfigured") { + notConfigured = true; + } else { + console.warn("Could not start server:", err); + } + }); } if (!serverProbeOk) { @@ -117,6 +122,7 @@ unlistenDownload = await listen

("download-progress", e => { setActiveDownloads(e.payload); }); return () => { + cancelled = true; if (store.settings.autoStartServer) invoke("kill_server").catch(() => {}); if (idleTimer) clearTimeout(idleTimer); if (pollInterval) clearInterval(pollInterval); @@ -125,14 +131,14 @@ }; }); - function handleRetry() { failed = false; serverProbeOk = false; } + function handleRetry() { failed = false; notConfigured = false; serverProbeOk = false; } {#if devSplash} setTimeout(() => devSplash = false, 340)} /> {:else if !appReady} - appReady = true} onRetry={handleRetry} /> diff --git a/src/assets/moku-icon-rounded.svg b/src/assets/moku-icon-rounded.svg new file mode 100644 index 0000000..a215c63 --- /dev/null +++ b/src/assets/moku-icon-rounded.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/src/assets/rounded-logo.png b/src/assets/rounded-logo.png deleted file mode 100644 index 2abb47a..0000000 Binary files a/src/assets/rounded-logo.png and /dev/null differ diff --git a/src/components/layout/SplashScreen.svelte b/src/components/layout/SplashScreen.svelte index d5570de..14cf063 100644 --- a/src/components/layout/SplashScreen.svelte +++ b/src/components/layout/SplashScreen.svelte @@ -1,21 +1,23 @@

@@ -232,21 +255,32 @@

press any key to continue

{:else} -
- {#if !failed} +
+ {#if !failed && !notConfigured} {/if} - Moku + Moku

moku

- {#if failed} -

Could not reach Suwayomi

-

Make sure tachidesk-server is on your PATH

- + {#if notConfigured} +
+

Server not configured

+

Set the server path in Settings, then retry

+
+ + +
+
+ {:else if failed} +
+

Could not reach Suwayomi

+

Make sure tachidesk-server is on your PATH

+ +
{:else}

{ringFull ? "Ready" : `Initializing server${dots}`} @@ -268,4 +302,9 @@ .hint { font-family: var(--font-ui); font-size: 10px; color: var(--text-faint); letter-spacing: 0.22em; text-transform: uppercase; margin: 0; user-select: none; animation: hintFade 3.5s ease-in-out infinite; } .title-label { font-family: var(--font-ui); font-size: 11px; font-weight: 500; letter-spacing: 0.26em; text-transform: uppercase; color: var(--text-secondary); margin: 0 0 8px; z-index: 1; user-select: none; } .retry-btn { margin-top: 4px; padding: 5px 16px; border-radius: var(--radius-md); border: 1px solid var(--border-dim); background: var(--bg-raised); color: var(--text-muted); cursor: pointer; font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.08em; } + .retry-btn:hover { border-color: var(--border-strong); color: var(--text-secondary); } + .error-box { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 14px 20px; border-radius: var(--radius-lg); background: rgba(0,0,0,0.55); border: 1px solid rgba(255,255,255,0.12); max-width: 260px; text-align: center; backdrop-filter: blur(4px); } + .error-box--danger { border-color: rgba(220,50,50,0.5); } + .error-title { font-family: var(--font-ui); font-size: 11px; font-weight: 500; color: var(--text-muted); letter-spacing: 0.1em; margin: 0; } + .error-body { font-family: var(--font-ui); font-size: 10px; color: var(--text-faint); letter-spacing: 0.05em; margin: 0; line-height: 1.6; } diff --git a/src/components/pages/Explore.svelte b/src/components/pages/Explore.svelte new file mode 100644 index 0000000..1229c65 --- /dev/null +++ b/src/components/pages/Explore.svelte @@ -0,0 +1,372 @@ + + +{#if $activeSource} + +{:else if $genreFilter} + +{:else} +

+
+
+

Explore

+
+ + +
+
+
+ +
+
+ + {#if continueReading.length > 0 || loadingLib} +
+
+ Continue Reading +
+ {#if loadingLib} +
{#each Array(8) as _}
{/each}
+ {:else} +
+ {#each continueReading.slice(0, ROW_CAP) as { manga, chapterName, progress }} + + {/each} + {#each Array(GHOST_COUNT) as _}
{/each} +
+ {/if} +
+ {/if} + + {#if recommended.length > 0 || loadingLib} +
+
+ Recommended for You +
+ {#if loadingLib} +
{#each Array(8) as _}
{/each}
+ {:else} +
+ {#each recommended.slice(0, ROW_CAP) as m} + + {/each} + {#each Array(GHOST_COUNT) as _}
{/each} +
+ {/if} +
+ {/if} + + {#if popularManga.length > 0 || loadingPopular} +
+
+ + + {sources.length === 1 ? `Popular on ${sources[0].displayName}` : sources.length > 1 ? `Popular across ${sources.length} sources` : "Popular"} + +
+ {#if loadingPopular} +
{#each Array(8) as _}
{/each}
+ {:else if sources.length === 0} +
No sources installed. Add extensions first.
+ {:else} +
+ {#each popularManga.slice(0, ROW_CAP) as m} + + {/each} + {#each Array(GHOST_COUNT) as _}
{/each} +
+ {/if} +
+ {/if} + + {#each frecencyGenres as genre} + {@const items = genreResultsMap.get(genre) ?? []} + {@const isLoading = loadingGenres && items.length === 0} + {#if isLoading || items.length > 0} +
+
+ {genre} + +
+ {#if isLoading} +
{#each Array(8) as _}
{/each}
+ {:else} +
+ {#each items.slice(0, ROW_CAP) as m} + + {/each} + {#if items.length >= ROW_CAP} + + {/if} + {#each Array(GHOST_COUNT) as _}
{/each} +
+ {/if} +
+ {/if} + {/each} + + {#if !loadingLib && !loadingPopular && !loadingGenres && continueReading.length === 0 && recommended.length === 0 && popularManga.length === 0 && frecencyGenres.every((g) => !genreResultsMap.get(g)?.length)} +
+ {#if loadError} + Could not reach Suwayomi + Make sure the server is running, then try again. + + {:else} + Nothing to explore yet + Add manga to your library or install sources to get started. + {/if} +
+ {/if} +
+
+ + {#if mode === "sources"}{/if} +
+{/if} + +{#if ctx} + ctx = null} /> +{/if} + + diff --git a/src/components/settings/Settings.svelte b/src/components/settings/Settings.svelte index 3fe9a03..9efe8a9 100644 --- a/src/components/settings/Settings.svelte +++ b/src/components/settings/Settings.svelte @@ -2,6 +2,7 @@ import { tick } from "svelte"; import { X, Book, Image, Sliders, Info, Keyboard, Gear, HardDrives, FolderSimple, Plus, Pencil, Trash, Wrench, PaintBrush } from "phosphor-svelte"; import { invoke } from "@tauri-apps/api/core"; + import { getVersion } from "@tauri-apps/api/app"; import { gql } from "../../lib/client"; import { GET_DOWNLOADS_PATH } from "../../lib/queries"; import { store, updateSettings, resetKeybinds, addFolder, removeFolder, renameFolder, toggleFolderTab, clearHistory, wipeAllData, setSettingsOpen } from "../../store/state.svelte"; @@ -195,6 +196,33 @@ let splashTriggered = $state(false); + + let appVersion = $state("…"); + let latestVersion = $state(null); + let checkingUpdate = $state(false); + let updateError = $state(null); + + $effect(() => { + if (tab === "about") { + getVersion().then(v => appVersion = v).catch(() => appVersion = "unknown"); + } + }); + + async function checkForUpdate() { + checkingUpdate = true; updateError = null; latestVersion = null; + try { + const res = await fetch("https://api.github.com/repos/Youwes09/Moku/releases/latest", { + method: "GET", + headers: { "User-Agent": "Moku" }, + }); + const data = await res.json() as { tag_name: string }; + latestVersion = data.tag_name.replace(/^v/, ""); + } catch (e) { + updateError = "Could not reach GitHub"; + } finally { + checkingUpdate = false; + } + } function triggerSplash() { splashTriggered = true; setTimeout(() => splashTriggered = false, 200); @@ -695,7 +723,41 @@

Moku

A manga reader frontend for Suwayomi / Tachidesk.

-

Built with Tauri + Svelte. Connects to tachidesk-server.

+

Built with Tauri + Svelte.

+
+
+
+

Version

+
+
+ Current version + v{appVersion} +
+ +
+ {#if updateError} +

{updateError}

+ {:else if latestVersion !== null} + {#if latestVersion === appVersion} +

✓ You are on the latest version

+ {:else} +
+

Update available — v{latestVersion}

+ + Download on GitHub → + +
+ {/if} + {/if} +
+
+

Links

+
diff --git a/src/store/state.svelte.ts b/src/store/state.svelte.ts index 5a3655e..106c0f0 100644 --- a/src/store/state.svelte.ts +++ b/src/store/state.svelte.ts @@ -132,7 +132,7 @@ export const DEFAULT_SETTINGS: Settings = { compactSidebar: false, gpuAcceleration: true, serverUrl: "http://localhost:4567", - serverBinary: "tachidesk-server", + serverBinary: "", autoStartServer: true, preferredExtensionLang: "en", keybinds: DEFAULT_KEYBINDS, @@ -151,6 +151,14 @@ export const DEFAULT_SETTINGS: Settings = { // ── Persistence ─────────────────────────────────────────────────────────────── +const STORE_VERSION = 2; + +// Fields reset to their DEFAULT_SETTINGS value on each version bump. +// Add a key here whenever its default changes meaning between releases. +const RESET_ON_UPGRADE: (keyof Settings)[] = [ + "serverBinary", +]; + function loadPersisted(): any { try { const raw = localStorage.getItem("moku-store"); @@ -167,7 +175,26 @@ function persist(patch: Record) { } catch {} } -const saved = loadPersisted(); +const saved = (() => { + const data = loadPersisted(); + if (!data) return null; + if ((data.storeVersion ?? 1) < STORE_VERSION) { + const resetPatch: Partial = {}; + for (const key of RESET_ON_UPGRADE) { + (resetPatch as any)[key] = (DEFAULT_SETTINGS as any)[key]; + } + const migrated = { + ...data, + storeVersion: STORE_VERSION, + settings: { ...data.settings, ...resetPatch }, + }; + try { + localStorage.setItem("moku-store", JSON.stringify(migrated)); + } catch {} + return migrated; + } + return data; +})(); function mergeSettings(saved: any): Settings { const userFolders: Folder[] = saved?.settings?.folders ?? []; @@ -222,6 +249,7 @@ class Store { constructor() { $effect.root(() => { + $effect(() => { persist({ storeVersion: STORE_VERSION }); }); $effect(() => { persist({ navPage: this.navPage }); }); $effect(() => { persist({ libraryFilter: this.libraryFilter }); }); $effect(() => { persist({ history: this.history }); }); diff --git a/vite.config.ts b/vite.config.ts index e78c010..555abb8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,14 +1,21 @@ import { defineConfig } from "vite"; import { svelte } from "@sveltejs/vite-plugin-svelte"; +import path from "path"; export default defineConfig({ plugins: [svelte()], clearScreen: false, + resolve: { + alias: { + $store: path.resolve("./src/store"), + $components: path.resolve("./src/components"), + }, + }, server: { port: 1420, strictPort: true, watch: { - ignored: ["**/.flatpak-builder/**", "**/src-tauri/**"], + ignored: ["**/src-tauri/**"], }, }, envPrefix: ["VITE_", "TAURI_"],