From 399c628461734a9bace8bf1163c989cd05bcdc04 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Tue, 7 Jul 2026 19:37:56 -0500 Subject: [PATCH] Initial commit: MTG personal library catalog (Tauri v2 + React/TS) Full working app with: - Rust core (mtgc-core): Scryfall ingester, nucleo fuzzy search, SQLite two-DB architecture (reference.db + library.db via ATTACH), criteria filters, deck ops - Tauri v2 shell exposing the core as typed IPC commands - React/TS frontend: card entry, library browser, deck editor - Features: fuzzy card search, color/CMC/type/commander-identity filters, tile/list view toggle, hover card previews, deck format + commander support, per-card location field, grouping by type/subtype/CMC/color, localStorage persistence for active tab/deck/grouping, Discord-inspired dark UI (#C8553D accent) Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 23 + Cargo.lock | 4876 +++++++++++++++++ Cargo.toml | 25 + README.md | 114 + crates/mtgc-core/Cargo.toml | 27 + crates/mtgc-core/src/bin/ingest.rs | 28 + crates/mtgc-core/src/db.rs | 118 + crates/mtgc-core/src/decks.rs | 287 + crates/mtgc-core/src/error.rs | 29 + crates/mtgc-core/src/filters.rs | 228 + crates/mtgc-core/src/ingest.rs | 448 ++ crates/mtgc-core/src/lib.rs | 217 + crates/mtgc-core/src/library.rs | 192 + crates/mtgc-core/src/model.rs | 165 + crates/mtgc-core/src/search.rs | 108 + .../mtgc-core/src/sql/library/0001_init.sql | 41 + .../src/sql/library/0002_location.sql | 3 + .../src/sql/library/0003_deck_format.sql | 3 + crates/mtgc-core/src/sql/reference_schema.sql | 56 + index.html | 12 + package-lock.json | 1971 +++++++ package.json | 26 + scripts/devel-setup.sh | 80 + src-tauri/Cargo.toml | 21 + src-tauri/build.rs | 3 + src-tauri/capabilities/default.json | 7 + src-tauri/icons/128x128.png | Bin 0 -> 3361 bytes src-tauri/icons/128x128@2x.png | Bin 0 -> 5948 bytes src-tauri/icons/32x32.png | Bin 0 -> 1124 bytes src-tauri/icons/64x64.png | Bin 0 -> 1925 bytes src-tauri/icons/Square107x107Logo.png | Bin 0 -> 2859 bytes src-tauri/icons/Square142x142Logo.png | Bin 0 -> 3596 bytes src-tauri/icons/Square150x150Logo.png | Bin 0 -> 3791 bytes src-tauri/icons/Square284x284Logo.png | Bin 0 -> 6744 bytes src-tauri/icons/Square30x30Logo.png | Bin 0 -> 1041 bytes src-tauri/icons/Square310x310Logo.png | Bin 0 -> 7196 bytes src-tauri/icons/Square44x44Logo.png | Bin 0 -> 1437 bytes src-tauri/icons/Square71x71Logo.png | Bin 0 -> 2050 bytes src-tauri/icons/Square89x89Logo.png | Bin 0 -> 2454 bytes src-tauri/icons/StoreLogo.png | Bin 0 -> 1532 bytes .../android/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../icons/android/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1538 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 4061 bytes .../android/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 1709 bytes .../icons/android/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1534 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2868 bytes .../android/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 1713 bytes .../android/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 2880 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 5133 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 3232 bytes .../android/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 3981 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 7491 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 4716 bytes .../android/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 5274 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 10177 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 6020 bytes .../android/values/ic_launcher_background.xml | 4 + src-tauri/icons/icon.icns | Bin 0 -> 61059 bytes src-tauri/icons/icon.ico | Bin 0 -> 12482 bytes src-tauri/icons/icon.png | Bin 0 -> 11987 bytes src-tauri/icons/ios/AppIcon-20x20@1x.png | Bin 0 -> 730 bytes src-tauri/icons/ios/AppIcon-20x20@2x-1.png | Bin 0 -> 1300 bytes src-tauri/icons/ios/AppIcon-20x20@2x.png | Bin 0 -> 1300 bytes src-tauri/icons/ios/AppIcon-20x20@3x.png | Bin 0 -> 1814 bytes src-tauri/icons/ios/AppIcon-29x29@1x.png | Bin 0 -> 1094 bytes src-tauri/icons/ios/AppIcon-29x29@2x-1.png | Bin 0 -> 1786 bytes src-tauri/icons/ios/AppIcon-29x29@2x.png | Bin 0 -> 1786 bytes src-tauri/icons/ios/AppIcon-29x29@3x.png | Bin 0 -> 2489 bytes src-tauri/icons/ios/AppIcon-40x40@1x.png | Bin 0 -> 1300 bytes src-tauri/icons/ios/AppIcon-40x40@2x-1.png | Bin 0 -> 2368 bytes src-tauri/icons/ios/AppIcon-40x40@2x.png | Bin 0 -> 2368 bytes src-tauri/icons/ios/AppIcon-40x40@3x.png | Bin 0 -> 3126 bytes src-tauri/icons/ios/AppIcon-512@2x.png | Bin 0 -> 14671 bytes src-tauri/icons/ios/AppIcon-60x60@2x.png | Bin 0 -> 3126 bytes src-tauri/icons/ios/AppIcon-60x60@3x.png | Bin 0 -> 4290 bytes src-tauri/icons/ios/AppIcon-76x76@1x.png | Bin 0 -> 2163 bytes src-tauri/icons/ios/AppIcon-76x76@2x.png | Bin 0 -> 3866 bytes src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png | Bin 0 -> 4103 bytes src-tauri/src/commands.rs | 163 + src-tauri/src/lib.rs | 43 + src-tauri/src/main.rs | 6 + src-tauri/src/state.rs | 58 + src-tauri/tauri.conf.json | 37 + src/App.tsx | 170 + src/api.ts | 109 + src/components.tsx | 455 ++ src/main.tsx | 10 + src/styles.css | 715 +++ src/views/CardEntry.tsx | 83 + src/views/DeckView.tsx | 377 ++ src/views/LibraryBrowser.tsx | 235 + tsconfig.json | 21 + tsconfig.node.json | 11 + vite.config.ts | 16 + 94 files changed, 11626 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 crates/mtgc-core/Cargo.toml create mode 100644 crates/mtgc-core/src/bin/ingest.rs create mode 100644 crates/mtgc-core/src/db.rs create mode 100644 crates/mtgc-core/src/decks.rs create mode 100644 crates/mtgc-core/src/error.rs create mode 100644 crates/mtgc-core/src/filters.rs create mode 100644 crates/mtgc-core/src/ingest.rs create mode 100644 crates/mtgc-core/src/lib.rs create mode 100644 crates/mtgc-core/src/library.rs create mode 100644 crates/mtgc-core/src/model.rs create mode 100644 crates/mtgc-core/src/search.rs create mode 100644 crates/mtgc-core/src/sql/library/0001_init.sql create mode 100644 crates/mtgc-core/src/sql/library/0002_location.sql create mode 100644 crates/mtgc-core/src/sql/library/0003_deck_format.sql create mode 100644 crates/mtgc-core/src/sql/reference_schema.sql create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100755 scripts/devel-setup.sh create mode 100644 src-tauri/Cargo.toml create mode 100644 src-tauri/build.rs create mode 100644 src-tauri/capabilities/default.json create mode 100644 src-tauri/icons/128x128.png create mode 100644 src-tauri/icons/128x128@2x.png create mode 100644 src-tauri/icons/32x32.png create mode 100644 src-tauri/icons/64x64.png create mode 100644 src-tauri/icons/Square107x107Logo.png create mode 100644 src-tauri/icons/Square142x142Logo.png create mode 100644 src-tauri/icons/Square150x150Logo.png create mode 100644 src-tauri/icons/Square284x284Logo.png create mode 100644 src-tauri/icons/Square30x30Logo.png create mode 100644 src-tauri/icons/Square310x310Logo.png create mode 100644 src-tauri/icons/Square44x44Logo.png create mode 100644 src-tauri/icons/Square71x71Logo.png create mode 100644 src-tauri/icons/Square89x89Logo.png create mode 100644 src-tauri/icons/StoreLogo.png create mode 100644 src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 src-tauri/icons/android/mipmap-hdpi/ic_launcher.png create mode 100644 src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png create mode 100644 src-tauri/icons/android/mipmap-mdpi/ic_launcher.png create mode 100644 src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png create mode 100644 src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png create mode 100644 src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png create mode 100644 src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png create mode 100644 src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png create mode 100644 src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 src-tauri/icons/android/values/ic_launcher_background.xml create mode 100644 src-tauri/icons/icon.icns create mode 100644 src-tauri/icons/icon.ico create mode 100644 src-tauri/icons/icon.png create mode 100644 src-tauri/icons/ios/AppIcon-20x20@1x.png create mode 100644 src-tauri/icons/ios/AppIcon-20x20@2x-1.png create mode 100644 src-tauri/icons/ios/AppIcon-20x20@2x.png create mode 100644 src-tauri/icons/ios/AppIcon-20x20@3x.png create mode 100644 src-tauri/icons/ios/AppIcon-29x29@1x.png create mode 100644 src-tauri/icons/ios/AppIcon-29x29@2x-1.png create mode 100644 src-tauri/icons/ios/AppIcon-29x29@2x.png create mode 100644 src-tauri/icons/ios/AppIcon-29x29@3x.png create mode 100644 src-tauri/icons/ios/AppIcon-40x40@1x.png create mode 100644 src-tauri/icons/ios/AppIcon-40x40@2x-1.png create mode 100644 src-tauri/icons/ios/AppIcon-40x40@2x.png create mode 100644 src-tauri/icons/ios/AppIcon-40x40@3x.png create mode 100644 src-tauri/icons/ios/AppIcon-512@2x.png create mode 100644 src-tauri/icons/ios/AppIcon-60x60@2x.png create mode 100644 src-tauri/icons/ios/AppIcon-60x60@3x.png create mode 100644 src-tauri/icons/ios/AppIcon-76x76@1x.png create mode 100644 src-tauri/icons/ios/AppIcon-76x76@2x.png create mode 100644 src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png create mode 100644 src-tauri/src/commands.rs create mode 100644 src-tauri/src/lib.rs create mode 100644 src-tauri/src/main.rs create mode 100644 src-tauri/src/state.rs create mode 100644 src-tauri/tauri.conf.json create mode 100644 src/App.tsx create mode 100644 src/api.ts create mode 100644 src/components.tsx create mode 100644 src/main.tsx create mode 100644 src/styles.css create mode 100644 src/views/CardEntry.tsx create mode 100644 src/views/DeckView.tsx create mode 100644 src/views/LibraryBrowser.tsx create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b11039 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Rust / Cargo +/target +**/*.rs.bk + +# Node / frontend +node_modules/ +/dist/ + +# Tauri generated +/src-tauri/gen/ + +# Local databases (reference.db is rebuildable; library.db is your synced data) +*.db +*.sqlite +*.db-shm +*.db-wal + +# OS / editor +.DS_Store +Thumbs.db +.idea/ +*.swp +*.swo diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..6a1b84a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4876 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.0", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.0", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "embed-resource" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.2+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +dependencies = [ + "libc", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mtgc-app" +version = "0.1.0" +dependencies = [ + "mtgc-core", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-dialog", +] + +[[package]] +name = "mtgc-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "nucleo", + "rusqlite", + "serde", + "serde_json", + "tempfile", + "thiserror 1.0.69", + "ureq", +] + +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nucleo" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5262af4c94921c2646c5ac6ff7900c2af9cbb08dc26a797e18130a7019c039d4" +dependencies = [ + "nucleo-matcher", + "parking_lot", + "rayon", +] + +[[package]] +name = "nucleo-matcher" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf33f538733d1a5a3494b836ba913207f14d9d4a1d3cd67030c5061bdd2cac85" +dependencies = [ + "memchr", + "unicode-segmentation", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plist" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.13.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.118", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.13.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.13.0", + "block2", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.118", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dom_query", + "dunce", + "glob", + "http", + "infer", + "json-patch", + "log", + "memchr", + "phf", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.2+spec-1.1.0", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.0", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.8", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..efd22f2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[workspace] +resolver = "2" +members = ["crates/mtgc-core", "src-tauri"] + +[workspace.package] +version = "0.1.0" +edition = "2021" +license = "MIT" + +[workspace.dependencies] +rusqlite = { version = "0.31", features = ["bundled"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +nucleo = "0.5" +ureq = { version = "2", features = ["json"] } +anyhow = "1" +thiserror = "1" + +# Lean, self-contained release binary (used by `cargo tauri build`). +# Trades a few minutes of extra build time for a smaller executable. +[profile.release] +strip = true # drop debug symbols +lto = true # link-time optimization +codegen-units = 1 # better optimization at the cost of parallelism +panic = "abort" # no unwinding machinery diff --git a/README.md b/README.md new file mode 100644 index 0000000..586184f --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +# mtgc — Personal Magic: The Gathering Library Catalog + +A desktop app (Tauri v2 + React) for cataloging your MTG collection: rapid +fuzzy card entry, rich criteria search (colors, commander color-identity +compatibility, mana value, type/subtype/keyword), and deck organization with +per-card deck membership. Built so the eventual mobile port is cheap — all logic +lives in a platform-agnostic Rust core. + +## Architecture + +``` +src/ React + TypeScript UI (swappable presentation) +src-tauri/ Tauri v2 shell — exposes the core as typed commands +crates/mtgc-core/ Pure Rust: db, fuzzy search, filters, decks, ingester +``` + +Two SQLite databases, joined at runtime via `ATTACH`: + +- **`reference.db`** — read-only card data built from Scryfall bulk. Bulky, + re-downloadable per device, **not synced**. +- **`library.db`** — your owned cards + decks. Small, portable, **the file you + sync** (drop it in Syncthing/Dropbox/Nextcloud). Point the app at it via + *Choose library file…*. + +Both default to the OS app-data dir (e.g. `~/.local/share/com.garrettmills.mtgc/`). + +## First-time setup + +```bash +./scripts/devel-setup.sh # Tauri system libs (webkit2gtk/gtk3) + Tauri CLI +npm install # frontend deps +``` + +## Run + +```bash +cargo tauri dev # launches the desktop app (dev mode) +``` + +On first launch, click **Download card data** — it fetches the Scryfall +*Oracle Cards* + *Default Cards* bulk (~a few hundred MB) and builds +`reference.db`. After that the app is fully offline. + +You can also build the card database from the command line: + +```bash +cargo run -p mtgc-core --bin mtgc-ingest -- /path/to/reference.db +``` + +## Production build + +Build an optimized app with the frontend embedded into the executable: + +```bash +./scripts/devel-setup.sh # once, if not already done +cargo tauri build +``` + +Outputs (under the workspace `target/release/`): + +- **`target/release/mtgc-app`** — the standalone executable. The web UI and all + app logic are baked into this one file. It dynamically links the system WebKit/GTK + libraries (`webkit2gtk4.1`, `gtk3`), so those must be present on the machine that + runs it — they are on any modern desktop Linux and are installed by + `devel-setup.sh`. +- **`target/release/bundle/appimage/*.AppImage`** — a single portable file that + bundles the app together with its runtime. This is the closest thing to a + download-and-run **single self-contained binary**: `chmod +x` it and run. + **Recommended** for copying to another machine. +- **`target/release/bundle/rpm/*.rpm`** and **`.../deb/*.deb`** — native installers + (Fedora / Debian-based) that add a menu entry and icon. + +Useful variants: + +```bash +cargo tauri build --bundles appimage # only the portable AppImage, skip installers +cargo tauri build --no-bundle # only the bare target/release/mtgc-app binary +``` + +Notes: + +- On Linux the truly portable single file is the **AppImage**; the bare `mtgc-app` + binary is also a single file but relies on the system WebKit/GTK libs. +- The binary does **not** embed card data — first run still downloads the Scryfall + bulk into the OS app-data dir, and your synced `library.db` lives there by default + (override with *Choose library file…*). +- The `[profile.release]` settings in `Cargo.toml` (strip + LTO) make the release + build take a few minutes but keep the binary small. + +## Using it + +- **Add Cards** — type a name (typos OK); ↑/↓ then Enter adds a copy. +- **Library** — filter owned cards by name, colors, commander identity (color + identity ⊆ chosen), mana value range, type/subtype/keyword, or deck. Hover a + row's thumbnail for a full-size preview that tracks the cursor. Rows show which + decks a card is in and a warning (⚠) when it's assigned to more decks than you + own. Toggle **List / Tiles** (top-right) to switch between the table and a grid + of full card images; the choice is remembered. +- Deck contents offer the same List / Tiles toggle and hover previews. +- **Decks** — create decks and assign owned cards; set per-deck quantities. + +## Develop / test + +```bash +cargo test -p mtgc-core # core unit tests (db, search, filters, decks, ingest) +npm run build # typecheck + build frontend +cargo build # build everything incl. the desktop app +``` + +## Mobile (future) + +The core and frontend are platform-agnostic; `cargo tauri icon` already emitted +iOS/Android icon sets. Porting is adding `tauri ios` / `tauri android` targets — +only reference-data provisioning differs (download on-device). diff --git a/crates/mtgc-core/Cargo.toml b/crates/mtgc-core/Cargo.toml new file mode 100644 index 0000000..88f1fc0 --- /dev/null +++ b/crates/mtgc-core/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "mtgc-core" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "Platform-agnostic core for the MTG library catalog: DB, fuzzy search, filters, decks, and Scryfall ingestion." + +[lib] +name = "mtgc_core" +path = "src/lib.rs" + +# Binary that builds reference.db from Scryfall bulk data. +[[bin]] +name = "mtgc-ingest" +path = "src/bin/ingest.rs" + +[dependencies] +rusqlite = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +nucleo = { workspace = true } +ureq = { workspace = true } +anyhow = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +tempfile = "3" diff --git a/crates/mtgc-core/src/bin/ingest.rs b/crates/mtgc-core/src/bin/ingest.rs new file mode 100644 index 0000000..7ba9d6c --- /dev/null +++ b/crates/mtgc-core/src/bin/ingest.rs @@ -0,0 +1,28 @@ +//! CLI: build reference.db from Scryfall bulk data. +//! +//! Usage: `mtgc-ingest [path/to/reference.db]` (defaults to ./reference.db) + +use std::path::PathBuf; +use std::process::ExitCode; + +fn main() -> ExitCode { + let dest = std::env::args() + .nth(1) + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from("reference.db")); + + eprintln!("Building reference db at {}", dest.display()); + match mtgc_core::ingest::build_reference_db(&dest, |msg| eprintln!(" {msg}")) { + Ok(stats) => { + eprintln!( + "Done: {} oracle cards, {} printings.", + stats.oracle_cards, stats.printings + ); + ExitCode::SUCCESS + } + Err(e) => { + eprintln!("Ingest failed: {e}"); + ExitCode::FAILURE + } + } +} diff --git a/crates/mtgc-core/src/db.rs b/crates/mtgc-core/src/db.rs new file mode 100644 index 0000000..18475a2 --- /dev/null +++ b/crates/mtgc-core/src/db.rs @@ -0,0 +1,118 @@ +//! Database wiring: open the library, run migrations, ATTACH the reference db. + +use std::path::Path; + +use rusqlite::Connection; + +use crate::error::Result; + +/// The reference schema, applied to a freshly-created reference.db by the ingester +/// (and by tests that build a fixture reference db). +pub const REFERENCE_SCHEMA: &str = include_str!("sql/reference_schema.sql"); + +/// Ordered library migrations. Index + 1 is the schema version recorded in +/// `PRAGMA user_version`. Append new migrations; never edit an applied one. +const LIBRARY_MIGRATIONS: &[&str] = &[ + include_str!("sql/library/0001_init.sql"), + include_str!("sql/library/0002_location.sql"), + include_str!("sql/library/0003_deck_format.sql"), +]; + +/// The attached alias under which the reference db is available at runtime. +pub const REF_DB: &str = "ref"; + +/// Correlated subquery selecting one image URL for the oracle card aliased `oc`. +/// Returns NULL when no printing has an image. Read it as `Option`. +pub fn image_subquery() -> String { + format!( + "(SELECT p.image_uri FROM {REF_DB}.printings p + WHERE p.oracle_id = oc.oracle_id AND p.image_uri <> '' LIMIT 1)" + ) +} + +/// Create (if needed) and open a reference.db, applying the reference schema. +/// Used by the ingester and by test fixtures. +pub fn open_reference_for_build(path: &Path) -> Result { + let conn = Connection::open(path)?; + conn.execute_batch(REFERENCE_SCHEMA)?; + Ok(conn) +} + +/// Open the library db at `library_path`, run migrations, and ATTACH the +/// reference db at `reference_path` under the [`REF_DB`] alias. +pub fn open(library_path: &Path, reference_path: &Path) -> Result { + let conn = Connection::open(library_path)?; + conn.pragma_update(None, "foreign_keys", true)?; + migrate_library(&conn)?; + attach_reference(&conn, reference_path)?; + Ok(conn) +} + +/// ATTACH a reference db to an already-open library connection. +pub fn attach_reference(conn: &Connection, reference_path: &Path) -> Result<()> { + conn.execute( + &format!("ATTACH DATABASE ?1 AS {REF_DB}"), + [reference_path.to_string_lossy()], + )?; + Ok(()) +} + +/// Apply any library migrations newer than the recorded `user_version`. +pub fn migrate_library(conn: &Connection) -> Result<()> { + let current: i64 = conn.pragma_query_value(None, "user_version", |r| r.get(0))?; + for (idx, sql) in LIBRARY_MIGRATIONS.iter().enumerate() { + let version = (idx + 1) as i64; + if version > current { + conn.execute_batch(&format!("BEGIN;\n{sql}\nPRAGMA user_version = {version};\nCOMMIT;"))?; + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn migrations_are_idempotent_and_bump_version() { + let conn = Connection::open_in_memory().unwrap(); + migrate_library(&conn).unwrap(); + let v: i64 = conn + .pragma_query_value(None, "user_version", |r| r.get(0)) + .unwrap(); + assert_eq!(v, LIBRARY_MIGRATIONS.len() as i64); + + // Running again is a no-op (no "table already exists" error). + migrate_library(&conn).unwrap(); + + // Core tables exist. + let n: i64 = conn + .query_row( + "SELECT count(*) FROM sqlite_master WHERE type='table' AND name IN ('decks','library_cards','deck_cards','library_card_copies')", + [], + |r| r.get(0), + ) + .unwrap(); + assert_eq!(n, 4); + } + + #[test] + fn reference_schema_applies_with_fts() { + let conn = Connection::open_in_memory().unwrap(); + conn.execute_batch(REFERENCE_SCHEMA).unwrap(); + // FTS5 + trigram tokenizer must be available in the bundled SQLite. + conn.execute( + "INSERT INTO oracle_names_fts(name, oracle_id) VALUES ('Llanowar Elves','x')", + [], + ) + .unwrap(); + let hit: i64 = conn + .query_row( + "SELECT count(*) FROM oracle_names_fts WHERE oracle_names_fts MATCH 'lan'", + [], + |r| r.get(0), + ) + .unwrap(); + assert_eq!(hit, 1); + } +} diff --git a/crates/mtgc-core/src/decks.rs b/crates/mtgc-core/src/decks.rs new file mode 100644 index 0000000..249969b --- /dev/null +++ b/crates/mtgc-core/src/decks.rs @@ -0,0 +1,287 @@ +//! Deck operations and deck membership lookups. + +use rusqlite::{params, OptionalExtension}; + +use crate::db::REF_DB; +use crate::error::Result; +use crate::model::{CardRef, ColorSet, Deck, DeckEntry}; +use crate::Catalog; + +impl Catalog { + /// Create a deck. `commander_oracle_id` is optional. + pub fn create_deck(&self, name: &str, commander_oracle_id: Option<&str>) -> Result { + self.conn.execute( + "INSERT INTO decks(name, commander_oracle_id) VALUES (?1, ?2)", + params![name, commander_oracle_id], + )?; + let id = self.conn.last_insert_rowid(); + self.get_deck(id) + .map(|d| d.expect("just-inserted deck must exist")) + } + + pub fn get_deck(&self, id: i64) -> Result> { + Ok(self + .conn + .query_row( + "SELECT id, name, format, commander_oracle_id, notes FROM decks WHERE id = ?1", + params![id], + Self::map_deck, + ) + .optional()?) + } + + pub fn list_decks(&self) -> Result> { + let mut stmt = self.conn.prepare( + "SELECT id, name, format, commander_oracle_id, notes + FROM decks ORDER BY name COLLATE NOCASE", + )?; + let rows = stmt + .query_map([], Self::map_deck)? + .collect::, _>>()?; + Ok(rows) + } + + pub fn delete_deck(&self, id: i64) -> Result<()> { + // ON DELETE CASCADE clears deck_cards. + self.conn + .execute("DELETE FROM decks WHERE id = ?1", params![id])?; + Ok(()) + } + + /// Set how many copies of a card belong to a deck. `quantity <= 0` removes + /// the card from the deck. Upserts on (deck_id, oracle_id). + pub fn set_deck_card( + &self, + deck_id: i64, + oracle_id: &str, + quantity: i64, + is_commander: bool, + ) -> Result<()> { + if quantity <= 0 { + self.conn.execute( + "DELETE FROM deck_cards WHERE deck_id = ?1 AND oracle_id = ?2", + params![deck_id, oracle_id], + )?; + return Ok(()); + } + self.conn.execute( + "INSERT INTO deck_cards(deck_id, oracle_id, quantity, is_commander) + VALUES (?1, ?2, ?3, ?4) + ON CONFLICT(deck_id, oracle_id) DO UPDATE SET + quantity = excluded.quantity, + is_commander = excluded.is_commander", + params![deck_id, oracle_id, quantity, is_commander as i64], + )?; + Ok(()) + } + + /// All cards in a deck — owned or not — with their in-deck quantity and how + /// many copies are owned (0 if the card isn't in the library). Callers split + /// this into "in your library" vs "not owned" groups. + pub fn deck_contents(&self, deck_id: i64) -> Result> { + let mut stmt = self.conn.prepare(&format!( + "SELECT dc.quantity, dc.is_commander, COALESCE(lc.quantity_owned, 0), + oc.oracle_id, oc.name, oc.mana_value, oc.colors, oc.color_identity, + oc.type_line, {image} + FROM deck_cards dc + JOIN {REF_DB}.oracle_cards oc ON oc.oracle_id = dc.oracle_id + LEFT JOIN library_cards lc ON lc.oracle_id = dc.oracle_id + WHERE dc.deck_id = ?1 + ORDER BY oc.name COLLATE NOCASE", + image = crate::db::image_subquery() + ))?; + let rows = stmt + .query_map(params![deck_id], |r| { + Ok(DeckEntry { + quantity: r.get(0)?, + is_commander: r.get::<_, i64>(1)? != 0, + quantity_owned: r.get(2)?, + card: CardRef { + oracle_id: r.get(3)?, + name: r.get(4)?, + mana_value: r.get(5)?, + colors: ColorSet(r.get::<_, i64>(6)? as u8), + color_identity: ColorSet(r.get::<_, i64>(7)? as u8), + type_line: r.get(8)?, + image_uri: r.get::<_, Option>(9)?.unwrap_or_default(), + }, + }) + })? + .collect::, _>>()?; + Ok(rows) + } + + /// The decks a given card is currently assigned to. + pub fn card_decks(&self, oracle_id: &str) -> Result> { + let mut stmt = self.conn.prepare( + "SELECT d.id, d.name, d.format, d.commander_oracle_id, d.notes + FROM deck_cards dc JOIN decks d ON d.id = dc.deck_id + WHERE dc.oracle_id = ?1 + ORDER BY d.name COLLATE NOCASE", + )?; + let rows = stmt + .query_map(params![oracle_id], Self::map_deck)? + .collect::, _>>()?; + Ok(rows) + } + + fn map_deck(r: &rusqlite::Row) -> rusqlite::Result { + Ok(Deck { + id: r.get(0)?, + name: r.get(1)?, + format: r.get(2)?, + commander_oracle_id: r.get(3)?, + notes: r.get(4)?, + }) + } + + /// Set the deck's format (e.g. "Commander"). Empty string clears it. + pub fn set_deck_format(&self, deck_id: i64, format: &str) -> Result<()> { + self.conn.execute( + "UPDATE decks SET format = ?1 WHERE id = ?2", + params![format, deck_id], + )?; + Ok(()) + } + + /// Set (or clear, with `None`) the deck's commander card. Also marks/unmarks + /// the corresponding deck_cards row as the commander when present. + pub fn set_deck_commander(&self, deck_id: i64, oracle_id: Option<&str>) -> Result<()> { + self.conn.execute( + "UPDATE decks SET commander_oracle_id = ?1 WHERE id = ?2", + params![oracle_id, deck_id], + )?; + // Reset any existing commander flag, then set it on the chosen card. + self.conn.execute( + "UPDATE deck_cards SET is_commander = 0 WHERE deck_id = ?1", + params![deck_id], + )?; + if let Some(oid) = oracle_id { + self.conn.execute( + "UPDATE deck_cards SET is_commander = 1 WHERE deck_id = ?1 AND oracle_id = ?2", + params![deck_id, oid], + )?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::test_support; + + #[test] + fn deck_membership_visible_from_library() { + let fx = test_support::make(); + let cat = &fx.catalog; + + cat.add_owned("llanowar-elves", 4).unwrap(); + let elves = cat.create_deck("Elfball", Some("llanowar-elves")).unwrap(); + cat.set_deck_card(elves.id, "llanowar-elves", 4, false) + .unwrap(); + + // card_decks reports membership. + let decks = cat.card_decks("llanowar-elves").unwrap(); + assert_eq!(decks.len(), 1); + assert_eq!(decks[0].name, "Elfball"); + + // list_library surfaces the deck name on the card row. + let lib = cat.list_library().unwrap(); + let row = lib + .iter() + .find(|c| c.card.oracle_id == "llanowar-elves") + .unwrap(); + assert_eq!(row.decks, vec!["Elfball".to_string()]); + assert_eq!(row.quantity_assigned, 4); + assert!(!row.is_over_assigned()); + } + + #[test] + fn over_assignment_detected_across_decks() { + let fx = test_support::make(); + let cat = &fx.catalog; + cat.add_owned("sol-ring", 1).unwrap(); // own one physical copy + + let a = cat.create_deck("Deck A", None).unwrap(); + let b = cat.create_deck("Deck B", None).unwrap(); + cat.set_deck_card(a.id, "sol-ring", 1, false).unwrap(); + cat.set_deck_card(b.id, "sol-ring", 1, false).unwrap(); + + let lib = cat.list_library().unwrap(); + let sol = lib.iter().find(|c| c.card.oracle_id == "sol-ring").unwrap(); + assert_eq!(sol.quantity_assigned, 2); + assert_eq!(sol.quantity_owned, 1); + assert!(sol.is_over_assigned()); + assert_eq!(sol.decks.len(), 2); + } + + #[test] + fn format_and_commander_metadata() { + let fx = test_support::make(); + let cat = &fx.catalog; + let d = cat.create_deck("EDH", None).unwrap(); + assert_eq!(d.format, ""); + + cat.set_deck_format(d.id, "Commander").unwrap(); + cat.set_deck_card(d.id, "llanowar-elves", 1, false).unwrap(); + cat.set_deck_commander(d.id, Some("llanowar-elves")).unwrap(); + + let d = cat.get_deck(d.id).unwrap().unwrap(); + assert_eq!(d.format, "Commander"); + assert_eq!(d.commander_oracle_id.as_deref(), Some("llanowar-elves")); + + // The card is flagged as commander in the deck. + let commander = cat + .deck_contents(d.id) + .unwrap() + .into_iter() + .find(|e| e.card.oracle_id == "llanowar-elves") + .unwrap(); + assert!(commander.is_commander); + + // Clearing works. + cat.set_deck_commander(d.id, None).unwrap(); + assert_eq!( + cat.get_deck(d.id).unwrap().unwrap().commander_oracle_id, + None + ); + } + + #[test] + fn deck_contents_include_owned_and_unowned() { + let fx = test_support::make(); + let cat = &fx.catalog; + cat.add_owned("llanowar-elves", 4).unwrap(); // owned + // serra-angel intentionally NOT owned. + + let d = cat.create_deck("Mixed", None).unwrap(); + cat.set_deck_card(d.id, "llanowar-elves", 4, false).unwrap(); + cat.set_deck_card(d.id, "serra-angel", 1, true).unwrap(); + + let contents = cat.deck_contents(d.id).unwrap(); + assert_eq!(contents.len(), 2); + + let owned: Vec<_> = contents.iter().filter(|e| e.quantity_owned > 0).collect(); + let unowned: Vec<_> = contents.iter().filter(|e| e.quantity_owned == 0).collect(); + assert_eq!(owned.len(), 1); + assert_eq!(owned[0].card.name, "Llanowar Elves"); + assert_eq!(owned[0].quantity, 4); + assert_eq!(unowned.len(), 1); + assert_eq!(unowned[0].card.name, "Serra Angel"); + assert!(unowned[0].is_commander); + } + + #[test] + fn setting_zero_removes_and_cascade_on_delete() { + let fx = test_support::make(); + let cat = &fx.catalog; + let d = cat.create_deck("Temp", None).unwrap(); + cat.set_deck_card(d.id, "lightning-bolt", 2, false).unwrap(); + cat.set_deck_card(d.id, "lightning-bolt", 0, false).unwrap(); + assert!(cat.card_decks("lightning-bolt").unwrap().is_empty()); + + cat.set_deck_card(d.id, "lightning-bolt", 1, false).unwrap(); + cat.delete_deck(d.id).unwrap(); + assert!(cat.card_decks("lightning-bolt").unwrap().is_empty()); + } +} diff --git a/crates/mtgc-core/src/error.rs b/crates/mtgc-core/src/error.rs new file mode 100644 index 0000000..f731964 --- /dev/null +++ b/crates/mtgc-core/src/error.rs @@ -0,0 +1,29 @@ +//! Core error type. + +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum CoreError { + #[error(transparent)] + Sqlite(#[from] rusqlite::Error), + + #[error(transparent)] + Io(#[from] std::io::Error), + + #[error("http error: {0}")] + Http(String), + + #[error("json error: {0}")] + Json(#[from] serde_json::Error), + + #[error("{0}")] + Other(String), +} + +pub type Result = std::result::Result; + +impl From for CoreError { + fn from(e: ureq::Error) -> Self { + CoreError::Http(e.to_string()) + } +} diff --git a/crates/mtgc-core/src/filters.rs b/crates/mtgc-core/src/filters.rs new file mode 100644 index 0000000..dd5723e --- /dev/null +++ b/crates/mtgc-core/src/filters.rs @@ -0,0 +1,228 @@ +//! Criteria search over the owned library, compiled to SQL across the library +//! and attached reference dbs. + +use rusqlite::types::Value; + +use crate::db::REF_DB; +use crate::error::Result; +use crate::model::{LibraryCard, LibraryFilter}; +use crate::Catalog; + +impl Catalog { + /// Search the owned library by the given criteria. An empty filter returns + /// the whole owned library (same set as [`Catalog::list_library`]). + /// + /// Semantics: within a facet, multiple `types`/`subtypes`/`keywords` values + /// are ANDed (the card must have all of them). Commander compatibility keeps + /// only cards whose color identity is a subset of the given identity. + pub fn search_library(&self, filter: &LibraryFilter) -> Result> { + let mut sql = format!( + "SELECT lc.oracle_id, lc.quantity_owned, lc.location, + oc.name, oc.mana_value, oc.colors, oc.color_identity, oc.type_line, {image} + FROM library_cards lc + JOIN {REF_DB}.oracle_cards oc ON oc.oracle_id = lc.oracle_id + WHERE lc.quantity_owned > 0", + image = crate::db::image_subquery() + ); + let mut vals: Vec = Vec::new(); + + if let Some(text) = filter.text.as_ref().map(|t| t.trim()).filter(|t| !t.is_empty()) { + sql.push_str(" AND oc.name_lower LIKE ?"); + vals.push(Value::Text(format!("%{}%", text.to_lowercase()))); + } + if let Some(c) = filter.colors_exact { + sql.push_str(" AND oc.colors = ?"); + vals.push(Value::Integer(c.0 as i64)); + } + if let Some(c) = filter.colors_include { + sql.push_str(" AND (oc.colors & ?) = ?"); + vals.push(Value::Integer(c.0 as i64)); + vals.push(Value::Integer(c.0 as i64)); + } + if let Some(id) = filter.commander_identity { + // subset: card_identity & ~commander == 0 (see ColorSet::is_subset_of) + sql.push_str(" AND (oc.color_identity & ?) = 0"); + vals.push(Value::Integer((!id.0 & 0b11111) as i64)); + } + if let Some(min) = filter.cmc_min { + sql.push_str(" AND oc.mana_value >= ?"); + vals.push(Value::Real(min)); + } + if let Some(max) = filter.cmc_max { + sql.push_str(" AND oc.mana_value <= ?"); + vals.push(Value::Real(max)); + } + for t in &filter.types { + sql.push_str(&format!( + " AND EXISTS (SELECT 1 FROM {REF_DB}.card_types x + WHERE x.oracle_id = oc.oracle_id AND x.type = ? COLLATE NOCASE)" + )); + vals.push(Value::Text(t.clone())); + } + for s in &filter.subtypes { + sql.push_str(&format!( + " AND EXISTS (SELECT 1 FROM {REF_DB}.card_subtypes x + WHERE x.oracle_id = oc.oracle_id AND x.subtype = ? COLLATE NOCASE)" + )); + vals.push(Value::Text(s.clone())); + } + for k in &filter.keywords { + sql.push_str(&format!( + " AND EXISTS (SELECT 1 FROM {REF_DB}.card_keywords x + WHERE x.oracle_id = oc.oracle_id AND x.keyword = ? COLLATE NOCASE)" + )); + vals.push(Value::Text(k.clone())); + } + if let Some(deck_id) = filter.in_deck { + sql.push_str( + " AND EXISTS (SELECT 1 FROM deck_cards dc + WHERE dc.oracle_id = oc.oracle_id AND dc.deck_id = ?)", + ); + vals.push(Value::Integer(deck_id)); + } + + sql.push_str(" ORDER BY oc.name COLLATE NOCASE"); + + let mut stmt = self.conn.prepare(&sql)?; + let rows = stmt + .query_map( + rusqlite::params_from_iter(vals), + crate::library::row_to_library_parts, + )? + .collect::, _>>()?; + + // Enrich with deck context (shared with list_library). + let deck_names = self.deck_names_by_oracle()?; + let assigned = self.assigned_totals_by_oracle()?; + Ok(rows + .into_iter() + .map(|(oid, qty, location, card)| LibraryCard { + quantity_owned: qty, + decks: deck_names.get(&oid).cloned().unwrap_or_default(), + quantity_assigned: *assigned.get(&oid).unwrap_or(&0), + location, + card, + }) + .collect()) + } +} + +#[cfg(test)] +mod tests { + use crate::model::{ColorSet, LibraryFilter}; + use crate::test_support; + + /// Own the whole fixture set so filters have something to select from. + fn owned_fixture() -> test_support::Fixture { + let fx = test_support::make(); + for c in test_support::seed_cards() { + fx.catalog.add_owned(c.oracle_id, 1).unwrap(); + } + fx + } + + fn names(cards: &[crate::model::LibraryCard]) -> Vec { + cards.iter().map(|c| c.card.name.clone()).collect() + } + + #[test] + fn empty_filter_returns_all_owned() { + let fx = owned_fixture(); + let all = fx.catalog.search_library(&LibraryFilter::default()).unwrap(); + assert_eq!(all.len(), 5); + } + + #[test] + fn commander_identity_subset() { + let fx = owned_fixture(); + + // A B/G commander: Golgari (BG) and mono-B/mono-G and colorless are legal; + // white/red cards are not. + let f = LibraryFilter { + commander_identity: Some(ColorSet::parse("BG")), + ..Default::default() + }; + let got = names(&fx.catalog.search_library(&f).unwrap()); + assert!(got.contains(&"Deathrite Shaman".to_string())); // BG + assert!(got.contains(&"Llanowar Elves".to_string())); // G + assert!(got.contains(&"Sol Ring".to_string())); // colorless + assert!(!got.contains(&"Serra Angel".to_string())); // W + assert!(!got.contains(&"Lightning Bolt".to_string())); // R + + // Mono-white commander: the BG card is illegal. + let f = LibraryFilter { + commander_identity: Some(ColorSet::parse("W")), + ..Default::default() + }; + let got = names(&fx.catalog.search_library(&f).unwrap()); + assert!(got.contains(&"Serra Angel".to_string())); + assert!(!got.contains(&"Deathrite Shaman".to_string())); + } + + #[test] + fn type_subtype_keyword_and_cmc() { + let fx = owned_fixture(); + + let creatures = names(&fx.catalog.search_library(&LibraryFilter { + types: vec!["Creature".into()], + ..Default::default() + }).unwrap()); + assert_eq!(creatures.len(), 3); // Llanowar, Deathrite, Serra + + let elves = names(&fx.catalog.search_library(&LibraryFilter { + subtypes: vec!["Elf".into()], + ..Default::default() + }).unwrap()); + assert_eq!(elves, vec!["Deathrite Shaman", "Llanowar Elves"]); + + let flyers = names(&fx.catalog.search_library(&LibraryFilter { + keywords: vec!["Flying".into()], + ..Default::default() + }).unwrap()); + assert_eq!(flyers, vec!["Serra Angel"]); + + let cheap = fx.catalog.search_library(&LibraryFilter { + cmc_max: Some(1.0), + ..Default::default() + }).unwrap(); + assert!(cheap.iter().all(|c| c.card.mana_value <= 1.0)); + assert!(!cheap.iter().any(|c| c.card.name == "Serra Angel")); + } + + #[test] + fn colors_exact_and_include() { + let fx = owned_fixture(); + let exact_g = names(&fx.catalog.search_library(&LibraryFilter { + colors_exact: Some(ColorSet::parse("G")), + ..Default::default() + }).unwrap()); + assert_eq!(exact_g, vec!["Llanowar Elves"]); // not Deathrite (BG) + + let has_g = names(&fx.catalog.search_library(&LibraryFilter { + colors_include: Some(ColorSet::parse("G")), + ..Default::default() + }).unwrap()); + assert!(has_g.contains(&"Llanowar Elves".to_string())); + assert!(has_g.contains(&"Deathrite Shaman".to_string())); + } + + #[test] + fn text_and_in_deck() { + let fx = owned_fixture(); + let cat = &fx.catalog; + + let bolt = names(&cat.search_library(&LibraryFilter { + text: Some("bolt".into()), + ..Default::default() + }).unwrap()); + assert_eq!(bolt, vec!["Lightning Bolt"]); + + let deck = cat.create_deck("Mono-R", None).unwrap(); + cat.set_deck_card(deck.id, "lightning-bolt", 1, false).unwrap(); + let in_deck = names(&cat.search_library(&LibraryFilter { + in_deck: Some(deck.id), + ..Default::default() + }).unwrap()); + assert_eq!(in_deck, vec!["Lightning Bolt"]); + } +} diff --git a/crates/mtgc-core/src/ingest.rs b/crates/mtgc-core/src/ingest.rs new file mode 100644 index 0000000..634e5f9 --- /dev/null +++ b/crates/mtgc-core/src/ingest.rs @@ -0,0 +1,448 @@ +//! Build `reference.db` from Scryfall bulk data. +//! +//! Two passes: the *Oracle Cards* bulk (one entry per `oracle_id`) fills +//! `oracle_cards` + the normalized type/subtype/keyword tables + the name FTS; +//! the *Default Cards* bulk (one entry per printing) fills `printings`. +//! +//! Both bulk files are large JSON arrays, so we stream them element-by-element +//! rather than loading the whole array into memory. + +use std::io::{BufReader, Read}; +use std::marker::PhantomData; +use std::path::Path; + +use rusqlite::{params, Connection}; +use serde::de::{DeserializeOwned, SeqAccess, Visitor}; +use serde::Deserialize; + +use crate::db::open_reference_for_build; +use crate::error::{CoreError, Result}; +use crate::model::ColorSet; + +const BULK_INDEX_URL: &str = "https://api.scryfall.com/bulk-data"; +const USER_AGENT: &str = "mtgc/0.1 (personal library catalog)"; + +/// Row counts from a build, for reporting/verification. +#[derive(Debug, Default, Clone, Copy)] +pub struct IngestStats { + pub oracle_cards: usize, + pub printings: usize, +} + +/// Download the latest Scryfall bulk data and (re)build `reference.db` at `dest`. +/// +/// `progress` is called with human-readable status lines. +pub fn build_reference_db(dest: &Path, mut progress: impl FnMut(&str)) -> Result { + let (oracle_uri, default_uri) = fetch_bulk_uris(&mut progress)?; + + if dest.exists() { + std::fs::remove_file(dest)?; + } + let conn = open_reference_for_build(dest)?; + // Bulk-load fast; this db is disposable/rebuildable so durability is moot. + conn.pragma_update(None, "journal_mode", "MEMORY")?; + conn.pragma_update(None, "synchronous", "OFF")?; + + progress("Loading oracle cards…"); + let oracle_cards = load_oracle_cards(&conn, http_reader(&oracle_uri)?)?; + + progress("Loading printings…"); + let printings = load_printings(&conn, http_reader(&default_uri)?)?; + + progress("Optimizing…"); + conn.execute_batch("INSERT INTO oracle_names_fts(oracle_names_fts) VALUES('optimize');")?; + + Ok(IngestStats { + oracle_cards, + printings, + }) +} + +// ---- Scryfall bulk index ------------------------------------------------- + +#[derive(Deserialize)] +struct BulkIndex { + data: Vec, +} + +#[derive(Deserialize)] +struct BulkEntry { + #[serde(rename = "type")] + kind: String, + download_uri: String, +} + +fn fetch_bulk_uris(progress: &mut impl FnMut(&str)) -> Result<(String, String)> { + progress("Fetching Scryfall bulk index…"); + let index: BulkIndex = ureq::get(BULK_INDEX_URL) + .set("User-Agent", USER_AGENT) + .set("Accept", "application/json") + .call()? + .into_json()?; + + let find = |kind: &str| { + index + .data + .iter() + .find(|e| e.kind == kind) + .map(|e| e.download_uri.clone()) + .ok_or_else(|| CoreError::Other(format!("Scryfall bulk index missing '{kind}'"))) + }; + Ok((find("oracle_cards")?, find("default_cards")?)) +} + +fn http_reader(url: &str) -> Result { + let resp = ureq::get(url) + .set("User-Agent", USER_AGENT) + .call()?; + Ok(BufReader::with_capacity(1 << 20, resp.into_reader())) +} + +// ---- Streaming array parse ---------------------------------------------- + +/// Deserialize a top-level JSON array element-by-element, invoking `f` per item +/// without holding the whole array in memory. +fn stream_json_array(reader: R, f: F) -> Result<()> +where + R: Read, + T: DeserializeOwned, + F: FnMut(T) -> Result<()>, +{ + struct SeqVisitor { + f: F, + _marker: PhantomData T>, + } + + impl<'de, T, F> Visitor<'de> for SeqVisitor + where + T: DeserializeOwned, + F: FnMut(T) -> Result<()>, + { + type Value = (); + + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a JSON array of objects") + } + + fn visit_seq(mut self, mut seq: A) -> std::result::Result<(), A::Error> + where + A: SeqAccess<'de>, + { + while let Some(item) = seq.next_element::()? { + (self.f)(item).map_err(serde::de::Error::custom)?; + } + Ok(()) + } + } + + let mut de = serde_json::Deserializer::from_reader(reader); + serde::de::Deserializer::deserialize_seq( + &mut de, + SeqVisitor { + f, + _marker: PhantomData, + }, + )?; + Ok(()) +} + +// ---- Oracle pass --------------------------------------------------------- + +#[derive(Deserialize)] +struct ScryOracle { + oracle_id: Option, + name: String, + #[serde(default)] + cmc: f64, + #[serde(default)] + colors: Option>, + #[serde(default)] + color_identity: Vec, + #[serde(default)] + type_line: Option, + #[serde(default)] + oracle_text: Option, + #[serde(default)] + keywords: Vec, + #[serde(default)] + card_faces: Option>, +} + +#[derive(Deserialize)] +struct ScryFace { + #[serde(default)] + type_line: Option, + #[serde(default)] + colors: Option>, +} + +fn load_oracle_cards(conn: &Connection, reader: impl Read) -> Result { + conn.execute_batch("BEGIN")?; + let mut count = 0usize; + { + let mut ins_card = conn.prepare( + "INSERT OR IGNORE INTO oracle_cards + (oracle_id,name,name_lower,mana_value,colors,color_identity,type_line,oracle_text) + VALUES (?1,?2,?3,?4,?5,?6,?7,?8)", + )?; + let mut ins_fts = + conn.prepare("INSERT INTO oracle_names_fts(name,oracle_id) VALUES (?1,?2)")?; + let mut ins_type = + conn.prepare("INSERT INTO card_types(oracle_id,type) VALUES (?1,?2)")?; + let mut ins_sub = + conn.prepare("INSERT INTO card_subtypes(oracle_id,subtype) VALUES (?1,?2)")?; + let mut ins_kw = + conn.prepare("INSERT INTO card_keywords(oracle_id,keyword) VALUES (?1,?2)")?; + + stream_json_array::<_, ScryOracle, _>(reader, |c| { + let Some(oracle_id) = c.oracle_id.clone() else { + return Ok(()); // rare face-only records without a top-level oracle_id + }; + + // Colors: prefer top-level, else union of faces. + let colors = match &c.colors { + Some(cs) => ColorSet::from_letters(cs), + None => { + let mut m = 0u8; + if let Some(faces) = &c.card_faces { + for f in faces { + if let Some(cs) = &f.colors { + m |= ColorSet::from_letters(cs).0; + } + } + } + ColorSet(m) + } + }; + let identity = ColorSet::from_letters(&c.color_identity); + + // Type line: top-level, else join faces. + let type_line = c.type_line.clone().unwrap_or_else(|| { + c.card_faces + .as_ref() + .map(|faces| { + faces + .iter() + .filter_map(|f| f.type_line.clone()) + .collect::>() + .join(" // ") + }) + .unwrap_or_default() + }); + + let changed = ins_card.execute(params![ + oracle_id, + c.name, + c.name.to_lowercase(), + c.cmc, + colors.0 as i64, + identity.0 as i64, + type_line, + c.oracle_text.clone().unwrap_or_default(), + ])?; + // Only emit satellite rows the first time we see this oracle_id. + if changed == 1 { + ins_fts.execute(params![c.name, oracle_id])?; + let (types, subtypes) = parse_type_line(&type_line); + for t in types { + ins_type.execute(params![oracle_id, t])?; + } + for s in subtypes { + ins_sub.execute(params![oracle_id, s])?; + } + let mut kws = c.keywords.clone(); + kws.sort(); + kws.dedup(); + for k in kws { + ins_kw.execute(params![oracle_id, k])?; + } + count += 1; + } + Ok(()) + })?; + } + conn.execute_batch("COMMIT")?; + Ok(count) +} + +/// Split a Scryfall type line into (types+supertypes, subtypes). Handles split / +/// double-faced lines joined by " // ". The separator is the em dash '—'. +pub fn parse_type_line(type_line: &str) -> (Vec, Vec) { + let mut types = Vec::new(); + let mut subtypes = Vec::new(); + for part in type_line.split("//") { + let part = part.trim(); + let mut halves = part.splitn(2, '—'); + let left = halves.next().unwrap_or("").trim(); + let right = halves.next().unwrap_or("").trim(); + for t in left.split_whitespace() { + types.push(t.to_string()); + } + for s in right.split_whitespace() { + subtypes.push(s.to_string()); + } + } + types.sort(); + types.dedup(); + subtypes.sort(); + subtypes.dedup(); + (types, subtypes) +} + +// ---- Printings pass ------------------------------------------------------ + +#[derive(Deserialize)] +struct ScryPrinting { + id: String, + oracle_id: Option, + #[serde(default)] + set: String, + #[serde(default)] + collector_number: String, + #[serde(default)] + finishes: Vec, + #[serde(default)] + rarity: String, + #[serde(default)] + image_uris: Option, + #[serde(default)] + card_faces: Option>, +} + +#[derive(Deserialize)] +struct ImageUris { + #[serde(default)] + normal: Option, +} + +#[derive(Deserialize)] +struct PrintFace { + #[serde(default)] + image_uris: Option, +} + +fn load_printings(conn: &Connection, reader: impl Read) -> Result { + conn.execute_batch("BEGIN")?; + let mut count = 0usize; + { + let mut ins = conn.prepare( + "INSERT OR IGNORE INTO printings + (scryfall_id,oracle_id,set_code,collector_number,finishes,rarity,image_uri) + VALUES (?1,?2,?3,?4,?5,?6,?7)", + )?; + stream_json_array::<_, ScryPrinting, _>(reader, |p| { + let Some(oracle_id) = p.oracle_id.clone() else { + return Ok(()); + }; + let image = p + .image_uris + .as_ref() + .and_then(|i| i.normal.clone()) + .or_else(|| { + p.card_faces + .as_ref() + .and_then(|f| f.first()) + .and_then(|f| f.image_uris.as_ref()) + .and_then(|i| i.normal.clone()) + }) + .unwrap_or_default(); + ins.execute(params![ + p.id, + oracle_id, + p.set, + p.collector_number, + p.finishes.join(","), + p.rarity, + image, + ])?; + count += 1; + Ok(()) + })?; + } + conn.execute_batch("COMMIT")?; + Ok(count) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_type_lines() { + let (t, s) = parse_type_line("Legendary Creature — Elf Druid"); + assert_eq!(t, vec!["Creature", "Legendary"]); + assert_eq!(s, vec!["Druid", "Elf"]); + + let (t, s) = parse_type_line("Instant"); + assert_eq!(t, vec!["Instant"]); + assert!(s.is_empty()); + + // Split card. + let (t, s) = parse_type_line("Sorcery // Sorcery"); + assert_eq!(t, vec!["Sorcery"]); + assert!(s.is_empty()); + } + + #[test] + fn streams_array_of_objects() { + #[derive(serde::Deserialize)] + struct Item { + n: i32, + } + let json = br#"[{"n":1},{"n":2},{"n":3}]"#; + let mut sum = 0; + stream_json_array::<_, Item, _>(&json[..], |it| { + sum += it.n; + Ok(()) + }) + .unwrap(); + assert_eq!(sum, 6); + } + + #[test] + fn oracle_and_printing_load_from_fixture_json() { + // Minimal Scryfall-shaped fixtures exercising the insert paths. + let oracle = r#"[ + {"oracle_id":"oid-1","name":"Llanowar Elves","cmc":1.0, + "colors":["G"],"color_identity":["G"], + "type_line":"Creature — Elf Druid","keywords":[]}, + {"oracle_id":"oid-2","name":"Serra Angel","cmc":5.0, + "colors":["W"],"color_identity":["W"], + "type_line":"Creature — Angel","keywords":["Flying","Vigilance"]} + ]"#; + let printings = r#"[ + {"id":"p-1","oracle_id":"oid-1","set":"m19","collector_number":"310", + "finishes":["nonfoil","foil"],"rarity":"common", + "image_uris":{"normal":"http://img/1"}} + ]"#; + + let dir = tempfile::tempdir().unwrap(); + let dest = dir.path().join("reference.db"); + let conn = open_reference_for_build(&dest).unwrap(); + + let oc = load_oracle_cards(&conn, oracle.as_bytes()).unwrap(); + let pc = load_printings(&conn, printings.as_bytes()).unwrap(); + assert_eq!(oc, 2); + assert_eq!(pc, 1); + + // Keyword table populated. + let flyers: i64 = conn + .query_row( + "SELECT count(*) FROM card_keywords WHERE keyword='Flying'", + [], + |r| r.get(0), + ) + .unwrap(); + assert_eq!(flyers, 1); + + // Bitmask colors stored. + let g: i64 = conn + .query_row( + "SELECT colors FROM oracle_cards WHERE oracle_id='oid-1'", + [], + |r| r.get(0), + ) + .unwrap(); + assert_eq!(g as u8, crate::model::G); + } +} diff --git a/crates/mtgc-core/src/lib.rs b/crates/mtgc-core/src/lib.rs new file mode 100644 index 0000000..9828f60 --- /dev/null +++ b/crates/mtgc-core/src/lib.rs @@ -0,0 +1,217 @@ +//! mtgc-core — platform-agnostic core for the MTG library catalog. +//! +//! Everything that isn't UI lives here: the SQLite schema/migrations, the +//! in-memory fuzzy name search, the criteria filters, deck operations, and the +//! Scryfall ingester. It has no Tauri (or any GUI) dependency so it stays +//! unit-testable and reusable when the app is ported to mobile. + +pub mod db; +pub mod decks; +pub mod error; +pub mod filters; +pub mod ingest; +pub mod library; +pub mod model; +pub mod search; + +use std::path::Path; + +use rusqlite::Connection; + +pub use error::{CoreError, Result}; +pub use model::{CardRef, ColorSet, Deck, DeckEntry, LibraryCard, LibraryFilter}; +pub use search::NameIndex; + +/// A handle to an open catalog: the library db with the reference db attached. +/// +/// All read/write operations go through this. It owns the SQLite connection; +/// the fuzzy [`NameIndex`] is built separately (see [`Catalog::build_name_index`]) +/// so the caller can cache it in app state. +pub struct Catalog { + pub(crate) conn: Connection, +} + +impl Catalog { + /// Open the library at `library_path` (running migrations) with the + /// reference db at `reference_path` attached. + pub fn open(library_path: &Path, reference_path: &Path) -> Result { + let conn = db::open(library_path, reference_path)?; + Ok(Self { conn }) + } + + /// Build (or rebuild) the in-memory fuzzy name index from the attached + /// reference db. Cheap enough to hold in app state and rebuild after a + /// reference-data update. + pub fn build_name_index(&self) -> Result { + NameIndex::build(&self.conn) + } + + /// Borrow the underlying connection (used by tests and advanced callers). + pub fn connection(&self) -> &Connection { + &self.conn + } +} + +#[cfg(test)] +pub(crate) mod test_support { + //! Builds a tiny library+reference catalog in temp files for tests. + + use super::*; + use crate::model::ColorSet; + + pub struct Fixture { + pub catalog: Catalog, + // Keep the temp dir alive for the lifetime of the fixture. + _dir: tempfile::TempDir, + } + + /// (oracle_id, name, mana_value, colors, color_identity, type_line, + /// types, subtypes, keywords) + pub struct SeedCard { + pub oracle_id: &'static str, + pub name: &'static str, + pub mv: f64, + pub colors: &'static str, + pub identity: &'static str, + pub type_line: &'static str, + pub types: &'static [&'static str], + pub subtypes: &'static [&'static str], + pub keywords: &'static [&'static str], + } + + pub fn seed_cards() -> Vec { + vec![ + SeedCard { + oracle_id: "llanowar-elves", + name: "Llanowar Elves", + mv: 1.0, + colors: "G", + identity: "G", + type_line: "Creature — Elf Druid", + types: &["Creature"], + subtypes: &["Elf", "Druid"], + keywords: &[], + }, + SeedCard { + oracle_id: "deathrite-shaman", + name: "Deathrite Shaman", + mv: 1.0, + colors: "BG", + identity: "BG", + type_line: "Creature — Elf Shaman", + types: &["Creature"], + subtypes: &["Elf", "Shaman"], + keywords: &[], + }, + SeedCard { + oracle_id: "serra-angel", + name: "Serra Angel", + mv: 5.0, + colors: "W", + identity: "W", + type_line: "Creature — Angel", + types: &["Creature"], + subtypes: &["Angel"], + keywords: &["Flying", "Vigilance"], + }, + SeedCard { + oracle_id: "lightning-bolt", + name: "Lightning Bolt", + mv: 1.0, + colors: "R", + identity: "R", + type_line: "Instant", + types: &["Instant"], + subtypes: &[], + keywords: &[], + }, + SeedCard { + oracle_id: "sol-ring", + name: "Sol Ring", + mv: 1.0, + colors: "", + identity: "", + type_line: "Artifact", + types: &["Artifact"], + subtypes: &[], + keywords: &[], + }, + ] + } + + pub fn make() -> Fixture { + let dir = tempfile::tempdir().unwrap(); + let ref_path = dir.path().join("reference.db"); + let lib_path = dir.path().join("library.db"); + + // Build reference.db with fixture cards. + { + let rconn = db::open_reference_for_build(&ref_path).unwrap(); + for c in seed_cards() { + let colors = ColorSet::parse(c.colors).0 as i64; + let identity = ColorSet::parse(c.identity).0 as i64; + rconn + .execute( + "INSERT INTO oracle_cards + (oracle_id,name,name_lower,mana_value,colors,color_identity,type_line) + VALUES (?1,?2,?3,?4,?5,?6,?7)", + rusqlite::params![ + c.oracle_id, + c.name, + c.name.to_lowercase(), + c.mv, + colors, + identity, + c.type_line + ], + ) + .unwrap(); + rconn + .execute( + "INSERT INTO oracle_names_fts(name,oracle_id) VALUES (?1,?2)", + rusqlite::params![c.name, c.oracle_id], + ) + .unwrap(); + for t in c.types { + rconn + .execute( + "INSERT INTO card_types(oracle_id,type) VALUES (?1,?2)", + rusqlite::params![c.oracle_id, t], + ) + .unwrap(); + } + for s in c.subtypes { + rconn + .execute( + "INSERT INTO card_subtypes(oracle_id,subtype) VALUES (?1,?2)", + rusqlite::params![c.oracle_id, s], + ) + .unwrap(); + } + for k in c.keywords { + rconn + .execute( + "INSERT INTO card_keywords(oracle_id,keyword) VALUES (?1,?2)", + rusqlite::params![c.oracle_id, k], + ) + .unwrap(); + } + // One printing per card, carrying an image URL. + rconn + .execute( + "INSERT INTO printings(scryfall_id,oracle_id,image_uri) + VALUES (?1,?2,?3)", + rusqlite::params![ + format!("print-{}", c.oracle_id), + c.oracle_id, + format!("http://img/{}", c.oracle_id) + ], + ) + .unwrap(); + } + } + + let catalog = Catalog::open(&lib_path, &ref_path).unwrap(); + Fixture { catalog, _dir: dir } + } +} diff --git a/crates/mtgc-core/src/library.rs b/crates/mtgc-core/src/library.rs new file mode 100644 index 0000000..eb6dfd1 --- /dev/null +++ b/crates/mtgc-core/src/library.rs @@ -0,0 +1,192 @@ +//! Ownership operations: what cards the user has and how many. + +use std::collections::HashMap; + +use rusqlite::{params, OptionalExtension}; + +use crate::db::REF_DB; +use crate::error::Result; +use crate::model::{CardRef, ColorSet, LibraryCard}; +use crate::Catalog; + +/// Map a row shaped `(oracle_id, quantity_owned, location, name, mana_value, +/// colors, color_identity, type_line, image_uri)` into its library parts. Shared +/// by [`Catalog::list_library`] and the criteria search. +pub(crate) fn row_to_library_parts( + r: &rusqlite::Row, +) -> rusqlite::Result<(String, i64, String, CardRef)> { + let oracle_id: String = r.get(0)?; + Ok(( + oracle_id.clone(), + r.get::<_, i64>(1)?, + r.get::<_, String>(2)?, + CardRef { + oracle_id, + name: r.get(3)?, + mana_value: r.get(4)?, + colors: ColorSet(r.get::<_, i64>(5)? as u8), + color_identity: ColorSet(r.get::<_, i64>(6)? as u8), + type_line: r.get(7)?, + image_uri: r.get::<_, Option>(8)?.unwrap_or_default(), + }, + )) +} + +impl Catalog { + /// Add `delta` copies of a card to the library (increment, creating the row + /// if needed). `delta` may be negative to decrement; the quantity floors at 0. + /// Returns the new owned quantity. + pub fn add_owned(&self, oracle_id: &str, delta: i64) -> Result { + let current: i64 = self + .conn + .query_row( + "SELECT quantity_owned FROM library_cards WHERE oracle_id = ?1", + params![oracle_id], + |r| r.get(0), + ) + .optional()? + .unwrap_or(0); + self.set_owned(oracle_id, current + delta) + } + + /// Set the absolute owned quantity for a card (0 removes ownership but keeps + /// any deck assignments referencing it). + pub fn set_owned(&self, oracle_id: &str, quantity: i64) -> Result { + let q = quantity.max(0); + self.conn.execute( + "INSERT INTO library_cards(oracle_id, quantity_owned) VALUES (?1, ?2) + ON CONFLICT(oracle_id) DO UPDATE SET quantity_owned = excluded.quantity_owned", + params![oracle_id, q], + )?; + Ok(q) + } + + /// List every owned card (quantity_owned > 0) with deck context, ordered by + /// name. This is the "browse my library" view without extra criteria. + pub fn list_library(&self) -> Result> { + let deck_names = self.deck_names_by_oracle()?; + let assigned = self.assigned_totals_by_oracle()?; + + let mut stmt = self.conn.prepare(&format!( + "SELECT lc.oracle_id, lc.quantity_owned, lc.location, + oc.name, oc.mana_value, oc.colors, oc.color_identity, oc.type_line, {image} + FROM library_cards lc + JOIN {REF_DB}.oracle_cards oc ON oc.oracle_id = lc.oracle_id + WHERE lc.quantity_owned > 0 + ORDER BY oc.name COLLATE NOCASE", + image = crate::db::image_subquery() + ))?; + let rows = stmt + .query_map([], row_to_library_parts)? + .collect::, _>>()?; + + Ok(rows + .into_iter() + .map(|(oid, qty, location, card)| LibraryCard { + quantity_owned: qty, + decks: deck_names.get(&oid).cloned().unwrap_or_default(), + quantity_assigned: *assigned.get(&oid).unwrap_or(&0), + location, + card, + }) + .collect()) + } + + /// Set the free-text storage location for a card (creates the library row if + /// absent). Empty string clears it. + pub fn set_location(&self, oracle_id: &str, location: &str) -> Result<()> { + self.conn.execute( + "INSERT INTO library_cards(oracle_id, location) VALUES (?1, ?2) + ON CONFLICT(oracle_id) DO UPDATE SET location = excluded.location", + params![oracle_id, location], + )?; + Ok(()) + } + + /// Map each owned/assigned oracle_id to the deck names it appears in. + pub(crate) fn deck_names_by_oracle(&self) -> Result>> { + let mut stmt = self.conn.prepare( + "SELECT dc.oracle_id, d.name + FROM deck_cards dc JOIN decks d ON d.id = dc.deck_id + ORDER BY d.name COLLATE NOCASE", + )?; + let mut map: HashMap> = HashMap::new(); + let rows = stmt.query_map([], |r| Ok((r.get::<_, String>(0)?, r.get::<_, String>(1)?)))?; + for row in rows { + let (oid, name) = row?; + map.entry(oid).or_default().push(name); + } + Ok(map) + } + + pub(crate) fn assigned_totals_by_oracle(&self) -> Result> { + let mut stmt = self + .conn + .prepare("SELECT oracle_id, SUM(quantity) FROM deck_cards GROUP BY oracle_id")?; + let mut map = HashMap::new(); + let rows = stmt.query_map([], |r| Ok((r.get::<_, String>(0)?, r.get::<_, i64>(1)?)))?; + for row in rows { + let (oid, total) = row?; + map.insert(oid, total); + } + Ok(map) + } +} + +#[cfg(test)] +mod tests { + use crate::test_support; + + #[test] + fn add_increment_and_floor() { + let fx = test_support::make(); + let cat = &fx.catalog; + + assert_eq!(cat.add_owned("sol-ring", 1).unwrap(), 1); + assert_eq!(cat.add_owned("sol-ring", 2).unwrap(), 3); + assert_eq!(cat.add_owned("sol-ring", -10).unwrap(), 0); // floors at 0 + assert_eq!(cat.set_owned("sol-ring", 4).unwrap(), 4); + + let lib = cat.list_library().unwrap(); + let sol = lib.iter().find(|c| c.card.oracle_id == "sol-ring").unwrap(); + assert_eq!(sol.quantity_owned, 4); + assert!(sol.decks.is_empty()); + // Image URL is plumbed through from the printings table. + assert_eq!(sol.card.image_uri, "http://img/sol-ring"); + } + + #[test] + fn location_is_stored_and_listed() { + let fx = test_support::make(); + let cat = &fx.catalog; + cat.add_owned("sol-ring", 1).unwrap(); + cat.set_location("sol-ring", "Box 1").unwrap(); + + let lib = cat.list_library().unwrap(); + let sol = lib.iter().find(|c| c.card.oracle_id == "sol-ring").unwrap(); + assert_eq!(sol.location, "Box 1"); + + // Clearing works. + cat.set_location("sol-ring", "").unwrap(); + let lib = cat.list_library().unwrap(); + let sol = lib.iter().find(|c| c.card.oracle_id == "sol-ring").unwrap(); + assert_eq!(sol.location, ""); + } + + #[test] + fn list_is_ordered_and_excludes_zero() { + let fx = test_support::make(); + let cat = &fx.catalog; + cat.add_owned("serra-angel", 1).unwrap(); + cat.add_owned("lightning-bolt", 1).unwrap(); + cat.set_owned("sol-ring", 0).unwrap(); + + let names: Vec<_> = cat + .list_library() + .unwrap() + .into_iter() + .map(|c| c.card.name) + .collect(); + assert_eq!(names, vec!["Lightning Bolt", "Serra Angel"]); + } +} diff --git a/crates/mtgc-core/src/model.rs b/crates/mtgc-core/src/model.rs new file mode 100644 index 0000000..de02dcb --- /dev/null +++ b/crates/mtgc-core/src/model.rs @@ -0,0 +1,165 @@ +//! Shared domain types used across the core. + +use serde::{Deserialize, Serialize}; + +/// A card's color identity / colors, encoded as a 5-bit WUBRG mask. +/// +/// Bit layout (little end first): W=1, U=2, B=4, R=8, G=16. +/// Storing colors this way turns commander color-identity compatibility into a +/// single integer predicate (see [`ColorSet::is_subset_of`]). +pub const W: u8 = 1 << 0; +pub const U: u8 = 1 << 1; +pub const B: u8 = 1 << 2; +pub const R: u8 = 1 << 3; +pub const G: u8 = 1 << 4; + +/// The five colors in canonical WUBRG order, paired with their single-letter code. +pub const WUBRG: [(u8, char); 5] = [(W, 'W'), (U, 'U'), (B, 'B'), (R, 'R'), (G, 'G')]; + +/// A set of colors as a WUBRG bitmask. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] +pub struct ColorSet(pub u8); + +impl ColorSet { + pub const COLORLESS: ColorSet = ColorSet(0); + + /// Build from an iterator of single-letter color codes (e.g. Scryfall's + /// `["B", "G"]`). Unknown letters are ignored. + pub fn from_letters(letters: I) -> Self + where + I: IntoIterator, + S: AsRef, + { + let mut mask = 0u8; + for l in letters { + match l.as_ref() { + "W" | "w" => mask |= W, + "U" | "u" => mask |= U, + "B" | "b" => mask |= B, + "R" | "r" => mask |= R, + "G" | "g" => mask |= G, + _ => {} + } + } + ColorSet(mask) + } + + /// Parse from a compact string like "BG" or "wubrg". Order-insensitive. + pub fn parse(s: &str) -> Self { + Self::from_letters(s.chars().map(|c| c.to_string())) + } + + /// Canonical WUBRG string, e.g. `ColorSet(B|G)` -> "BG". Colorless -> "". + pub fn to_letters(self) -> String { + WUBRG + .iter() + .filter(|(bit, _)| self.0 & bit != 0) + .map(|(_, ch)| *ch) + .collect() + } + + pub fn is_colorless(self) -> bool { + self.0 == 0 + } + + pub fn contains(self, bit: u8) -> bool { + self.0 & bit != 0 + } + + /// True if every color in `self` is also in `other`. + /// + /// This is the commander-legality rule: a card is playable in a commander of + /// color identity `C` iff `card_identity.is_subset_of(C)`. + pub fn is_subset_of(self, other: ColorSet) -> bool { + self.0 & !other.0 == 0 + } +} + +/// A card as returned by search / library queries. Oracle-level (one per +/// `oracle_id`), enriched with the caller's ownership + deck context where relevant. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CardRef { + pub oracle_id: String, + pub name: String, + pub mana_value: f64, + pub colors: ColorSet, + pub color_identity: ColorSet, + pub type_line: String, + /// URL of a card image (Scryfall "normal" size) for one of its printings, + /// or empty if none is available. + pub image_uri: String, +} + +/// A library row: a card the user owns, with quantity and its deck memberships. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LibraryCard { + #[serde(flatten)] + pub card: CardRef, + pub quantity_owned: i64, + /// Names of decks this card is currently assigned to. + pub decks: Vec, + /// Total copies assigned across all decks. If it exceeds `quantity_owned`, + /// the physical copies are over-committed. + pub quantity_assigned: i64, + /// Free-text physical storage location (e.g. "Box 1"). Meaningful mainly for + /// cards not currently in a deck. + pub location: String, +} + +/// A card in a deck, whether or not the user owns it. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct DeckEntry { + #[serde(flatten)] + pub card: CardRef, + /// Copies of this card in the deck. + pub quantity: i64, + pub is_commander: bool, + /// Copies owned in the library (0 if the card isn't in the library). + pub quantity_owned: i64, +} + +impl LibraryCard { + pub fn is_over_assigned(&self) -> bool { + self.quantity_assigned > self.quantity_owned + } +} + +/// A deck. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Deck { + pub id: i64, + pub name: String, + /// Free-text format, e.g. "Commander", "Modern" (empty if unset). + pub format: String, + /// The commander card's oracle id (meaningful for Commander/Brawl formats). + pub commander_oracle_id: Option, + pub notes: String, +} + +/// Criteria for searching the owned library. All fields are optional; an empty +/// filter matches every owned card. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(default)] +pub struct LibraryFilter { + /// Fuzzy-ish name text (applied as a case-insensitive substring here; the + /// fast fuzzy path is [`crate::search`]). + pub text: Option, + /// Cards whose `colors` exactly equal this set. + pub colors_exact: Option, + /// Cards whose `colors` include at least these. + pub colors_include: Option, + /// Commander compatibility: only cards whose color identity is a subset of + /// this set (playable under a commander of this identity). + pub commander_identity: Option, + pub cmc_min: Option, + pub cmc_max: Option, + /// Card supertype/type, e.g. "Creature", "Instant" (matched against the + /// normalized `card_types` table). + pub types: Vec, + /// Subtype, e.g. "Elf", "Equipment". + pub subtypes: Vec, + /// Keyword ability, e.g. "Flying", "Deathtouch". + pub keywords: Vec, + /// Restrict to cards assigned to this deck id. + pub in_deck: Option, +} diff --git a/crates/mtgc-core/src/search.rs b/crates/mtgc-core/src/search.rs new file mode 100644 index 0000000..3790a41 --- /dev/null +++ b/crates/mtgc-core/src/search.rs @@ -0,0 +1,108 @@ +//! In-memory fuzzy search over oracle card names (the "rapid entry" path). +//! +//! Names are loaded once from the reference db into a [`NameIndex`]; each query +//! is ranked with nucleo (fzf-quality), giving typo-tolerant ordering far better +//! than SQL `LIKE`. ~30k names rank in well under a millisecond. + +use nucleo::pattern::{CaseMatching, Normalization, Pattern}; +use nucleo::{Config, Matcher}; +use rusqlite::Connection; + +use crate::db::REF_DB; +use crate::error::Result; +use crate::model::{CardRef, ColorSet}; + +/// One searchable entry, carrying the full [`CardRef`] so a hit needs no extra +/// DB round-trip. `AsRef` yields the name that nucleo matches against. +struct Entry { + card: CardRef, +} + +impl AsRef for Entry { + fn as_ref(&self) -> &str { + &self.card.name + } +} + +/// An immutable, in-memory index of all oracle card names for fuzzy search. +pub struct NameIndex { + entries: Vec, +} + +impl NameIndex { + /// Load every oracle card from the attached reference db. + pub fn build(conn: &Connection) -> Result { + let mut stmt = conn.prepare(&format!( + "SELECT oc.oracle_id, oc.name, oc.mana_value, oc.colors, oc.color_identity, + oc.type_line, {image} + FROM {REF_DB}.oracle_cards oc", + image = crate::db::image_subquery() + ))?; + let entries = stmt + .query_map([], |r| { + Ok(Entry { + card: CardRef { + oracle_id: r.get(0)?, + name: r.get(1)?, + mana_value: r.get(2)?, + colors: ColorSet(r.get::<_, i64>(3)? as u8), + color_identity: ColorSet(r.get::<_, i64>(4)? as u8), + type_line: r.get(5)?, + image_uri: r.get::<_, Option>(6)?.unwrap_or_default(), + }, + }) + })? + .collect::, _>>()?; + Ok(Self { entries }) + } + + pub fn len(&self) -> usize { + self.entries.len() + } + + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + /// Rank cards by fuzzy match against `query`, returning up to `limit` of the + /// best matches (highest score first). An empty query returns nothing. + pub fn search(&self, query: &str, limit: usize) -> Vec { + if query.trim().is_empty() { + return Vec::new(); + } + let mut matcher = Matcher::new(Config::DEFAULT); + let pattern = Pattern::parse(query, CaseMatching::Ignore, Normalization::Smart); + let mut matches = pattern.match_list(self.entries.iter(), &mut matcher); + matches.truncate(limit); + matches.into_iter().map(|(e, _score)| e.card.clone()).collect() + } +} + +#[cfg(test)] +mod tests { + use crate::test_support; + + #[test] + fn typo_still_ranks_target_first() { + let fx = test_support::make(); + let idx = fx.catalog.build_name_index().unwrap(); + assert_eq!(idx.len(), 5); + + // Misspelled "Llanowar Elves". + let hits = idx.search("lanowar elvs", 5); + assert!(!hits.is_empty(), "typo query should still match"); + assert_eq!(hits[0].name, "Llanowar Elves"); + } + + #[test] + fn prefix_and_limit() { + let fx = test_support::make(); + let idx = fx.catalog.build_name_index().unwrap(); + + let hits = idx.search("light", 5); + assert_eq!(hits[0].name, "Lightning Bolt"); + + assert!(idx.search("bolt", 1).len() <= 1); + assert!(idx.search("", 10).is_empty()); + } +} diff --git a/crates/mtgc-core/src/sql/library/0001_init.sql b/crates/mtgc-core/src/sql/library/0001_init.sql new file mode 100644 index 0000000..353bbbe --- /dev/null +++ b/crates/mtgc-core/src/sql/library/0001_init.sql @@ -0,0 +1,41 @@ +-- library.db v1 — the small, portable, synced file. +-- References reference cards by stable oracle_id / scryfall_id. + +CREATE TABLE decks ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + commander_oracle_id TEXT, + notes TEXT NOT NULL DEFAULT '', + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +-- Oracle-level ownership: one row per distinct card. +CREATE TABLE library_cards ( + id INTEGER PRIMARY KEY, + oracle_id TEXT NOT NULL UNIQUE, + quantity_owned INTEGER NOT NULL DEFAULT 0 +); + +-- Optional per-physical-copy printing detail (the "hybrid" identity). +CREATE TABLE library_card_copies ( + id INTEGER PRIMARY KEY, + library_card_id INTEGER NOT NULL REFERENCES library_cards(id) ON DELETE CASCADE, + scryfall_id TEXT, + set_code TEXT, + collector_number TEXT, + finish TEXT, + condition TEXT +); +CREATE INDEX idx_copies_card ON library_card_copies(library_card_id); + +-- Deck membership at the oracle level. +CREATE TABLE deck_cards ( + id INTEGER PRIMARY KEY, + deck_id INTEGER NOT NULL REFERENCES decks(id) ON DELETE CASCADE, + oracle_id TEXT NOT NULL, + quantity INTEGER NOT NULL DEFAULT 1, + is_commander INTEGER NOT NULL DEFAULT 0, + UNIQUE(deck_id, oracle_id) +); +CREATE INDEX idx_deck_cards_oid ON deck_cards(oracle_id); +CREATE INDEX idx_deck_cards_deck ON deck_cards(deck_id); diff --git a/crates/mtgc-core/src/sql/library/0002_location.sql b/crates/mtgc-core/src/sql/library/0002_location.sql new file mode 100644 index 0000000..ac0075b --- /dev/null +++ b/crates/mtgc-core/src/sql/library/0002_location.sql @@ -0,0 +1,3 @@ +-- v2 — physical storage location for owned cards (e.g. "Box 1", "Binder A"). +-- Most relevant for cards not assigned to any deck. +ALTER TABLE library_cards ADD COLUMN location TEXT NOT NULL DEFAULT ''; diff --git a/crates/mtgc-core/src/sql/library/0003_deck_format.sql b/crates/mtgc-core/src/sql/library/0003_deck_format.sql new file mode 100644 index 0000000..fd19e8d --- /dev/null +++ b/crates/mtgc-core/src/sql/library/0003_deck_format.sql @@ -0,0 +1,3 @@ +-- v3 — deck format metadata (e.g. "Commander", "Modern"). The commander card +-- itself is stored in the existing decks.commander_oracle_id column. +ALTER TABLE decks ADD COLUMN format TEXT NOT NULL DEFAULT ''; diff --git a/crates/mtgc-core/src/sql/reference_schema.sql b/crates/mtgc-core/src/sql/reference_schema.sql new file mode 100644 index 0000000..4145ace --- /dev/null +++ b/crates/mtgc-core/src/sql/reference_schema.sql @@ -0,0 +1,56 @@ +-- Schema for reference.db: read-only card data built from Scryfall bulk. +-- Applied against a connection whose `main` database IS the reference db +-- (the ingester opens reference.db directly; at runtime it is ATTACHed as `ref`). + +CREATE TABLE IF NOT EXISTS oracle_cards ( + oracle_id TEXT PRIMARY KEY, + name TEXT NOT NULL, + name_lower TEXT NOT NULL, -- for cheap case-insensitive substring + mana_value REAL NOT NULL DEFAULT 0, + colors INTEGER NOT NULL DEFAULT 0, -- WUBRG bitmask + color_identity INTEGER NOT NULL DEFAULT 0, -- WUBRG bitmask + type_line TEXT NOT NULL DEFAULT '', + oracle_text TEXT NOT NULL DEFAULT '' +); +CREATE INDEX IF NOT EXISTS idx_oracle_name_lower ON oracle_cards(name_lower); +CREATE INDEX IF NOT EXISTS idx_oracle_ci ON oracle_cards(color_identity); +CREATE INDEX IF NOT EXISTS idx_oracle_mv ON oracle_cards(mana_value); + +-- Normalized attribute tables for fast type/subtype/keyword filtering. +CREATE TABLE IF NOT EXISTS card_types ( + oracle_id TEXT NOT NULL, + type TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_card_types_type ON card_types(type); +CREATE INDEX IF NOT EXISTS idx_card_types_oid ON card_types(oracle_id); + +CREATE TABLE IF NOT EXISTS card_subtypes ( + oracle_id TEXT NOT NULL, + subtype TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_card_subtypes_sub ON card_subtypes(subtype); +CREATE INDEX IF NOT EXISTS idx_card_subtypes_oid ON card_subtypes(oracle_id); + +CREATE TABLE IF NOT EXISTS card_keywords ( + oracle_id TEXT NOT NULL, + keyword TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS idx_card_keywords_kw ON card_keywords(keyword); +CREATE INDEX IF NOT EXISTS idx_card_keywords_oid ON card_keywords(oracle_id); + +-- One row per printing (default-language bulk). +CREATE TABLE IF NOT EXISTS printings ( + scryfall_id TEXT PRIMARY KEY, + oracle_id TEXT NOT NULL, + set_code TEXT NOT NULL DEFAULT '', + collector_number TEXT NOT NULL DEFAULT '', + finishes TEXT NOT NULL DEFAULT '', -- comma-joined, e.g. "nonfoil,foil" + rarity TEXT NOT NULL DEFAULT '', + image_uri TEXT NOT NULL DEFAULT '' +); +CREATE INDEX IF NOT EXISTS idx_printings_oid ON printings(oracle_id); + +-- Secondary trigram FTS index over names (typo-tolerant fallback; the primary +-- interactive search is the in-memory nucleo matcher). +CREATE VIRTUAL TABLE IF NOT EXISTS oracle_names_fts + USING fts5(name, oracle_id UNINDEXED, tokenize = 'trigram'); diff --git a/index.html b/index.html new file mode 100644 index 0000000..0a9e379 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + MTG Library Catalog + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ebdaa3f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1971 @@ +{ + "name": "mtgc", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "mtgc", + "version": "0.1.0", + "dependencies": { + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.6.3", + "vite": "^5.4.11" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tauri-apps/api": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.4.tgz", + "integrity": "sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.11.4", + "@tauri-apps/cli-darwin-x64": "2.11.4", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.4", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.4", + "@tauri-apps/cli-linux-arm64-musl": "2.11.4", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.4", + "@tauri-apps/cli-linux-x64-gnu": "2.11.4", + "@tauri-apps/cli-linux-x64-musl": "2.11.4", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.4", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.4", + "@tauri-apps/cli-win32-x64-msvc": "2.11.4" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.4.tgz", + "integrity": "sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.4.tgz", + "integrity": "sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.4.tgz", + "integrity": "sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.4.tgz", + "integrity": "sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.4.tgz", + "integrity": "sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.4.tgz", + "integrity": "sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.4.tgz", + "integrity": "sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.4.tgz", + "integrity": "sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.4.tgz", + "integrity": "sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.4.tgz", + "integrity": "sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.4.tgz", + "integrity": "sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.7.1.tgz", + "integrity": "sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.42", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz", + "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001800", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz", + "integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.387", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.387.tgz", + "integrity": "sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..addd2e5 --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "mtgc", + "version": "0.1.0", + "description": "Personal Magic: The Gathering library catalog", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "^2", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.6.3", + "vite": "^5.4.11" + } +} diff --git a/scripts/devel-setup.sh b/scripts/devel-setup.sh new file mode 100755 index 0000000..33614c1 --- /dev/null +++ b/scripts/devel-setup.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# +# devel-setup.sh — one-time developer environment setup for mtgc. +# +# Installs the Tauri v2 Linux system libraries and the Tauri CLI, and verifies +# the toolchain (Rust, Node, pkg-config libs) needed to build the desktop shell. +# +# Safe to re-run: package installs are idempotent and the CLI install is skipped +# if already present. +# +# Usage: +# ./scripts/devel-setup.sh +# +set -euo pipefail + +info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; } +err() { printf '\033[1;31m[err]\033[0m %s\n' "$*" >&2; } + +# --- 1. Prerequisites already expected on the machine ---------------------- + +if ! command -v rustc >/dev/null 2>&1; then + err "Rust not found. Install from https://rustup.rs and re-run." + exit 1 +fi +info "Rust: $(rustc --version)" + +if ! command -v node >/dev/null 2>&1; then + warn "Node.js not found — needed for the Vite/React frontend." + warn "Install Node 18+ (e.g. 'sudo dnf install nodejs') before running the app." +else + info "Node: $(node --version)" +fi + +# --- 2. Tauri v2 system libraries (Fedora / dnf) --------------------------- + +FEDORA_PKGS=( + webkit2gtk4.1-devel + gtk3-devel + libappindicator-gtk3-devel + librsvg2-devel + file + openssl-devel +) + +if command -v dnf >/dev/null 2>&1; then + info "Installing Tauri system libraries via dnf (sudo required)…" + sudo dnf install -y "${FEDORA_PKGS[@]}" +else + warn "dnf not found — this script targets Fedora." + warn "Install the equivalents of: ${FEDORA_PKGS[*]}" + warn "See https://tauri.app/start/prerequisites/ for your distro." +fi + +# --- 3. Verify the libraries pkg-config will look for ---------------------- + +check_pkg() { + if pkg-config --exists "$1" 2>/dev/null; then + info "found $1" + else + warn "missing $1 (Tauri build may fail)" + fi +} +if command -v pkg-config >/dev/null 2>&1; then + check_pkg webkit2gtk-4.1 + check_pkg gtk+-3.0 +else + warn "pkg-config not found; cannot verify libraries." +fi + +# --- 4. Tauri v2 CLI ------------------------------------------------------- + +if cargo tauri --version >/dev/null 2>&1; then + info "Tauri CLI: $(cargo tauri --version 2>&1 | head -1)" +else + info "Installing Tauri v2 CLI (cargo install tauri-cli --version '^2')…" + cargo install tauri-cli --version '^2' +fi + +info "Setup complete. Next: scaffold + run the desktop shell with 'cargo tauri dev'." diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..a7c9a9b --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "mtgc-app" +version = "0.1.0" +edition = "2021" +description = "MTG Library Catalog desktop app" + +[lib] +# The Tauri app logic lives in a lib so the same entry point can serve desktop +# and (later) mobile targets. +name = "mtgc_app_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-dialog = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +mtgc-core = { path = "../crates/mtgc-core" } diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..8a73be3 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,7 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capabilities for the main window", + "windows": ["main"], + "permissions": ["core:default", "dialog:default"] +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..1a9cbc2d163f6b6c765af52577a7347890e1e9bf GIT binary patch literal 3361 zcmZ{nS2Wy<7RLX>Foq#SO^6Z%Cx{p&h|bYM7?NnCpO}P%45BkpbMz9UM;&2AFF|xs z5}k?Oi85OBQ6_HA+qrAq`>^+3>wEate)t}K-`|D?TJ*HnX#oJB*U{EM{JxlfhKBn0 z?ohJr1pwH(j)tm<*VH!B!kJ}_Rq0@`uXH?_inF{8#K~3IkJD*Wp)Gn>C>yULkOoPR z87f@DA*}JbkGKyIKT7a~lgDB#MeHK^CA#hED7}!nFzTyZrClj@u7JJYd~e@j#%0#5 z+5~df$I;W#aqw@MdlLf#zb-uo0-N2=qhnYV8OjMCX{;!y0Xj-dD5$j^u(1WoB>+w^jWI+Ih*O^WFWsGq zpeSXIkaz`uiLk)uX$(0H1qo*0JQnjZr5>7U@o5^TmS6m)4f~p4RrpY|lOM&35`}B* ziR%0g4GM zW6Y3WHm#g8C=yg-M{hgREsS(S7D+juhI0)tO!)3R!afgxNOwtLA`M&#pv)|mZR^Vq zM%AjIKAyRuAzxV*8Z@tiVONej7RkOg=q;JjyFslL7aEk`Pc_$Z{k!XvU}Jme5q0}Q ze^wO_POmfV`oq>-9I|}7pOP`AFEJo#bvVt30zi|N;8$>yLj74?5Vi&pWpE@UJ;X^4 zuET4}VBy(F`SnY+qtoq13G&KzVuzNaQmTIrXQLAiXbYh#9%~8Ft)*vu`JkdG!U9-2 zMJ4o1CmTe%8=4;IiEP$x9YyIz=E^D;c_|qw?jPeix)ImQ09i#QO4Q~ItiNP-UpTI< zRu_cHXcqXB#3pLR@7`S{R>{RK$4QIAQ{@5zR0g;>Pwc+?b#+3zl7InoLMt~vDT9ZOp3)Eso?aIK`9 zU+mvWyIpqVIbLm`UMznf5UZ9Y@}>x_ut8z~hs{e_clYz0+5wTg4}(j?>2zNYjUZHY zwN7L!;?PO~+aMa$!CvYup1IKUCoxO#R4S#!uxI<=`$vb5QO~WG6c|ar^FG$X>3Xow zM6$WGdMhO!x3PMWh@i&i&xW&Gkp=yhld{@au!gMZQWblt?WpL1Uz_8uOJwVwK1bWZ zeEZlh!dn-6^Vv8Lg+2l2$*ne^r=X*dmvEK!8LI1@BmDWR%Iq_Zv)Nw9OW3A035tCf zKKodz`&SysvmDXZ1w#sS;DpZSjuSe#t!Zb=+RzGw2mPuhPlV;vQ(?(v5 z912`ytTh>qw_(ST-}2S%e^mvy(bxYC?g@JTwPNN^hVW-La)|f&KM9tTo@nK%7^-R4FL| ze8KSJ?BhZ0+u8UJnf3MjBqu_~wH55K3I%4s|9R2Q*J!y&^;4OT;*;q9HIpOTuI+^ku z_*I4-{K1$2N14Svp~3d~-`6L%%iRx*1}!A7#LLguM41d|Kz7DzVi%5E>&--LBhKQN zKNMQqW3$F*PTVd_x)K{S^4540YzPw#-4h2Tf$0t!co!R6(GzDKLGD8Ag61qge< zL}sg4{i+th-DQG`epnEVm`s)x)P(NR`_oeThi7GnJR^FFLykrXq9N-+tmXTYnJ8$N zf6{mvk`2R|*s61_zr<+$jL0B0zl>_u#PWO}x;7&OxvQl6&yyyvi9(q=QkT}JQ^kxg z3M|$8s&tSrOzz$6ulmR;@7N6ElxLKJf-qTIv+)*NQWw>Qxl#M2aZ6w>C5gu!3^!Jm zJX!4VTc13nHm4Bwk)oq)UAD(OU8^oz@0|L)W|S@@ndZp%j*VH6;Nf}m#NJFjD#_W9iu;}c56MpuT&Qf?H9_N6fdVij?zhBd5JI84L#%b-6_R8*Tcb2Sxm;h1M z>n`NTjjL$SDa#k`${(kJ346gW=9cZW-8)q2B`p~QW3J}Xgm&{>u!|>tihadZ3DzLn z)fZa(U7eDb$)@sR5(6FIrlH2%83nP8VKmboT;tGPD)=ieL@F{%CR4YoOY0y%!0Wj9 zx_YD-CXiBnJ#+bVX7)23F)y+Tq4vjauIc6W8@R+V!>M>nqtoDsByI%QVv23AgxuyRb7lPozYSe=DrU`RcC~ahFD~1qOqWsa+ z^l&%9vT6zs4JeYBj9V~9v^02+Tv&i|13(bKKsa%&s|dBG@=X`+ya1hA;8ChzR4`b@ zzgmo6u#e3MK`3Kx3e+SA-KS7)1zMw{EXuP>ZsY||yFG=%jg*(j!`Rb=Gpb1_qiRc( z(Nn0KVfzC@Y=q(%IOXg+3}6v-&u%Jax3)m)3Zs)#aFqc!QjQ`mU>Fstw<-9?m}*}c z8I^hK^xOajAog!M8o~Ub^8Anq>5PWh-C_37u%d#vQae66MhD^(dp8QCE(g8+?1Ga- zG(6<((r~C=Y}3#|tbVLr+Zw0oDYG*X5|G7g`iHJ|QU`g0M`6~hUZ~REp+u^d5`zB9K+>y z%nA{d2Kq?9XN0Lt3&eP)kd~p`#;()-+xJMkHjB>|{~@R>Zcy50y*e@Jdz=$%>9Fyj zkjp{eT~Tv?>Sz!^72+b|?_>`Md8&kwd~+r5T3mOG+yndk$dJ#SyvKKcahfkNY<{Ox zFsFg6-ElLcZ#KhTDGNI=H~j8rrL77wL3E8;>pHvL9b5l_ejG2$ckU5hI^|V1SPQ*h z_GIJjtVw9EZ*#h*MA$7%VSQ`vecT$yqkMG5tV`y=Xte^(JLm=H0jHsKASMo zh%?v5*?~7H8J|t%D)cgXW~0N``<8@~`18EXf_5nU4~rJxO|DYUx^-}{ung*<+ez0D z1q`Vi2F2VDxG{&5>u$+%_VuH)VZ(@ggnu<{R|SToEBBuDKS-ou?de{1(Pf3ak2% z6@@F&EYH!-EJ_ofEF?>vgT>ZXOkRInf7N(sSk#%;H#&A>w-tYM=WB~l*`pv3R}AY) z$H8okKzQtcpX7P|Y4vGA_X&qDEyjdNV0KwuY&^60Q(J?WZ%&CDJV;G=?wjbV<1}N4 zCEn9Y-p_qCaWXk=w?gxX3n+SN+5XD!>4Tf~^)(g@R&T|((7V&{dscCR|G*pVKI5Pe zUTaRt%;ftFQL$4T3UDi4r5SLbM+sB|N22ls(YMlh_4k_O- R|E@lOj;4V|shUmDe*hgcSMUG; literal 0 HcmV?d00001 diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c91c3f63aeba63b074ec86cb7b233cd7f9489632 GIT binary patch literal 5948 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 Lb=AsLABO!0GaL5@ literal 0 HcmV?d00001 diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..534a783b98fe48a2cdeb210dda9e721c4d323cf8 GIT binary patch literal 1124 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 JPDHLkV1nB?f$jhR literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..489f783d9b593e17d9c003b1805846bef2785157 GIT binary patch literal 3596 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 GBmNIaB;@J< literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bad69674b783b1c60c6ac2a3bd8067f45da51a81 GIT binary patch literal 3791 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf2fcf60353722a87b9970eef8d92c6c64fb51a GIT binary patch literal 6744 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 Vf?xKJh+m8V8W3I83gstZ{{w>Fq;dcN literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f232a2f15966393f8b0e2a7538b368afe54f49f2 GIT binary patch literal 1041 zcmV+s1n&EZP)fOKgBoPvhMlJ|Z}FBALY@Wq*!uvT_s+siB|YF7U=A;P%AoCAk03V8p(-)M zgi*KPFzsXD(alq+2-c`5cz}6=!^@sBsFQTm3#B5*hD_RXvj$B$?4k17$c9;}LQua1 z6sR6To6aFpcQNLfkv7h|CNr<@8i7+NbLCj%E)NCl3!0f3hm1}+dR{vvVOavEWoEW@ z?6e&dFw4r%U2VA?A4cEX7wS6~NcGHxsG2cHf%9b&X!J)PjR>*8rzlw4&_-zsDWmcg zrjl2Nl(F zq~grq+;uW(^{}TF@zC@I5UQF@-ao>pNEdO_P~+3vPWt^Aq3|pUc|fgYhG@I;)50i- zljJ=QHo~z-@aowWRh*pW?x&ZD0WtMVg;uSdeAmMG!hp#*<~DD_w3@ki({&xgzoHoY z`5kYb-#~bJBO01FQr_cfx|9i+y-C`?DQ|1hv~(MOe2yT}eH)RNw=nSaJ>`8n>KfM| znHbJgdwD!gHv`_Zb?qT)1{LUfa1PHOUPK@iz*3saGN#PH++6JV5ausgjrs-a$kW#t z_|}X1ruEdB+F&P!U8AXQ~~RO$fl-BH8N% zuz2M@^hchkxkYVz)Epn7nHDR`0L+bq3yTLgc|Owvl{2vG=q2^srY6C2+|`na-(-Ma zAN-q3h?p&JB+ zYxl8RsHtrv-n!XiQA{eKa;_UaKn4f;apS@^I$+`%F<^N#Qt_It@O2_*s5M7N)qy@* z<(TtR79fLitl>0BGtG#U=Uv0YQ{p0Pq~J8D^OeJ-hcRY|Jmc>*yhkt$^!0X-tcs?^ zqZ}`NT%`tCm~n1du9V~I>FmI>J7?(B3Z-SPr?|XIlc~cRiRB8W`aeVH@Uo{2s$`Xv z`RF87)r|@>hqzhVCEyQMYfq(|jK@+J8F!&FgnG1Ki)bMK_P^X8m$;=6h^UaN00000 LNkvXXu0mjfk7EJh literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6056e30b347a5859aa309b1e204c014dc19fc083 GIT binary patch literal 7196 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 W&mSny&f{MF04fS<^5wE7f&T%x8I#=r literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..da937e4c788940120484da8f20dc693915453740 GIT binary patch literal 1437 zcmV;O1!DS%P)Ca3CZ_3wX^ed^CjJ4YFMTl4H}%aIANrz+ z(P(_CF~(F=<0ZCIjo4OdQzRusY6}Ww*_~zP=l6ZP4D7O_dHTWRddD;zL`U2x)r z(?ztR0lHz*jTGHi3z^vTJ(-mGx!d$y4VGOI2-*w+n@Zk6!>&F&@YpM;YwCus86_o{ ziJ6`p!No7%!sLx1*kzTgg4S|vZIRjfrd~Y#;wOk!S_FQR0?G!Q1n}wUqXK$0ruCq= zWo8*{d^QQsRhzQU-*I3^fd0cL^VM)#ORQOz0W)hDHq*W-3>zk`rUTSAcA~th5sCN| z#g4^M7{IV5Au0h@YR#@`7`ovD)$$2CrM5U*EBLIcyj)@(>r$S(nzi`8y^;VgYgv~w z8RW?u%ye0}U`LINe3l;IQf^sDDe=+V)iN!e^qGA=|IK%-S|J#Al}P#hFtS z)hS_=cPBB6zJo8Jy?c=Uc75h}CHUp84}XQ>?@p5gR0-S4^XMfac;v|I6yPy&hyw(TITUmJxCPu%f{bw|H?1Pg`YnIu)arHbV$1b3{u9YeGtRc9gB!z2^k zl!x;YUmre$>6QgfsdsA=fH&X#@&!nxT= z()ULcP^2}&_S?{hQh|A{gQnJp5UXn=DJ3_=)5aJkO&VqdJp(619+nlwl^@?Hr=YrE z&cVpg2UNh6OQ7{We1crYTC>(IIbgOgCozMz?kCXD)Fa1kP5deWIDYMGlEjEeU`KNw zTJAdxXMQF)S!|AmHIYKJ!bWHRb7XCCV!RALoqHRoDVpA(k3`l_mF#y$Iviq&nD-EaVtcd>2N?x@G^zGp-t%q%m_ z8F0_uXA!H9Nq~mYUWL=<6KI!gwR`{b6l9gY<*}W0?LRJcMo?k2C4i*|Bxh)9@58Q+ z1DKf_!_4h5H19q{mPd_%qe2600^zmk>{th$dKGu?IV6W#yB?=N1Hv3?m_>I4%7D@h zzn(~I*+XMXF9GhAV_ZJ+F*U(ZUd)S_g|z{i7=MDYmrL1;vsR?CJ}3Km1l05Y zUy_2N9&MT$&0Es`@U?s|tyF+H!gv7FOoPhNhp;GeEW3Mf*>t&X(PVG+;Fmdq6$t-Yuohne{W>B%d*2CxL zmP$irKHr3pjJwP3tz~o^f+W^6c)iGsB|%q6^N8%$aV6kPud-NZZ&{bm@!?6fS+Ul7 zt{8aa$u%qw=6KfSQgM?R6qJ1DXIWHB|NYlBYIY~nQ>UD^S`VrZc#6nD5v{aoDYZ@6 zqg2r?5I;j_-w3Rmwbj0qk=C8Yt=viQV2)>95?Bf)o5ZsWm{|rpG5W1Esdotah0Ly2R8yY+ r#6ztEw6IL46iUgj`u~f?|Iqd?SntR^@|5p~00000NkvXXu0mjfh)Bcf literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e5ffa50cc15acfab7ff9b6df553faab078129d2 GIT binary patch literal 2050 zcmV+d2>thoP)15nW~S-Fw!2-nyR*v-EZufzr~7|%&i9?$ zk5;>&QG$EKJ%kH^3yuqp3yuqp3y$LtE!8(FilBd-zd|YTuaOyW7!*9Bkr6QT2;i@F zEG`QE5vm7NkDoucH1z2MM-eJSMu+JMR8%j5lJXi5ng-YfuVn4@b*Q% zk=}yRDFLKS#bQDu_h4@Q23WcAeW;$d5yqG1VlZ=QvY-#&$F-ABOZFH73Z zlw{HevKRuUxpfC@*zyq>4n#tbvM*=UweZ7wye2&!JNPl2`QtORpz@?FNK5A8o^2#@OgS=YY23x@iyLT}e01gI<_Kq5)9VOJt7Y+!QfAj>B!CoE_k znx`Ns%MppJhE$2jmpF!8Dc69B5fkK}%Gryda?aDdu0)nE68IFeJgE|qZ!U1m9 ziEC*^9j_~qnrMj`U$R{1LcEt`@Pq5|rBTZBt{f)Q2#x~aCZ6t*p2YL<%vN#8HM0TE zBZP71aXXG!I2z82pG841BdsvSvI1? z+)`RVcw`9bm%Redz5XFS*C$;RUDN9m@awm`pcj)yum7Q}noz@Ygz&(N@9u-L8FMhb zk!)p-vmCy|(J&0$zA!=biLPNpq4Tdj(6a75D6M=7Bh~nevBB=|>xHfpU!moM<6e`_ zQ&q1O)QziGI8v=}p_IGCfB3JwWczJ9)aAB020Txyv3x&_mc z1dHjZOd1nD3?qVzDTE6rzk%Rz81K6FD4QZ7shS3X!601h_#Qzl;`cpzJ!w6RX<=lM z`I4?gizqxQeHhml8+q>b2|%|L7NVuXMBpP%5~-797mEoGhufi|~Dda6R#@x91V z@CfG!%Am0Va2~ma_+%{M&2WO3QE-?viImAQ{KatN%2Bx4)4_^l13+rz^(k=u(oyKU z)(#$D@z`tT`|}MPbP1{hl~>iXT5(^%suhtEk($T24H`(-@jd3biedq3K>5stmh{U! z0tXjJQrz6S-HKJB$SWAb_)DPY+(8(;a~nJ!m90kes_@^9Ug$l4gkQHw++R2YnpSNC zJu=Fb$-DuF>(HSfRM)J8+Ql1Ln(1f6CxM|pxNzz_bRRI44CTA~@BJ_u9N^%N&I@A= zdw$a|S zxn%R2w$zfcTmXmEi7ckBu@&pl%h{4RL`QJ?`#Pbg^Jge72e@?RN0jf&EZ+oTBou(z zb*rJKVVPxdY96YU)q(WknwwhRX6~@vgT7KE92z0{-Zv)S^mCi5$n7|X*IB@5`jsWF-iUVyIG9SqTj- zud&O}g4sf%A!z^QlNdK7*K1g@6-p|~;s%R(S0*(xcXF-mEY$|bJ+TuU9PpuX`HCu_ zY4tV+l-xRS=Nm_tv>Bs0y)0ZG@P zG(_619;sMylHz=DNL{EFW8Qs*D|}{9cYj=ODfOP%NT8n+P*yR;tPl!LRCVWqV-hSN zna+b3-ucoRk+xcF)`VCUkDI~NU4~;HfLXQAz~-I%t*l*|4Nqy-i3*Bm^2}d4c}h!X zO+p$HRi+e{uH72<49ctxT~4`+z~CZrmtwoOW!+m4e`3_Gg(*&TD-)oRpbWFZ0V-54IcZU&_ZT0J7sC<$%kqWwy=Er;i63_VAO*Rh-GQLwvu z64@a{$Cbm&w2>vH*e-` zIkfwP?u4V23|B@RL#{*?bNTcS)^LJM-%h=Ur$MPzOWSg2_ldd5LON2eNtK9viDSr> zrm}@`%HWJ`>_Q*5cLzKDAXOsrwLkZ?rVsWnpdrRw+E<>6h-i;9F`Dmbg}ZCU1;+)) g1;+))1;_cv|E;vpE}JkECIA2c07*qoM6N<$f<)ZoSpWb4 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e9c6a1e14d3ba717c9ad65177165e9f74e14e9e3 GIT binary patch literal 2454 zcmV;H32F9;P)B9%9*;(H9u9$cOMEz&_+(r~KdPM4`6&>(A&_0k|~kTuA9X^=I@dTEg7W7)HQ1;e1<-g~8# z%YT#rEObw%Z+~=8YL@pjE07J-g6kxZNDT82u z4>E%pkDf)OwizAkwxXr&8bs<^U|M0mOtFR{gjcTHO(hW>>P2tQE*$NC3q$=qsHj@# zTXgnTZR8>l8RQ}ou_0W#{!VPT=^@lM)QgvJ-JGr=n8C`F4=08PM)1x5|BHQ}Jpnsh z?HiApTaej;6R|;TxcL!my!oFX2T*l z$gvM*7r_=ho>`RYV%NyV6^jCEZ+HL;7p+7pIV#WR9b^VGNi3qZ>(AnY#YGt9L9-dq zRLVhBO$e7<@fX4&O3zty#4ZR_W)bX?3>YY<#RkP5qN(*NkJ9m;Am5hEFUmZaX_~MI zqr5=o#rIvkpvez@bK#T8eEHoJ9$c1LLC>fxD~5#D`;D=Zv8v>R_exdm3J(och_NnDXd;?@vaR$PP4e|wQM7&R;R6ey6B z@evXFSt6Wj-*1o=kB*$d{x6=zk`@0(Ld^_h#*z=~-uV=LVtz$sq=C-kOexp09`2CH zQ*!KrZ_czV7?v$yPWm8=swA!U@ZQ()+vzi~L-w?ROf;3Sg}&YsIJ*CB0=Qn{*_7wt z>+rvpAca%@CwS~*Jn_MRv9BEd?PvV(&C8U;#I!;T|C;d+tW2cAaut_IEx@6@uVJ9?YzCdnpl#hd$f9e-Kc}3T@lPwC$Xq=I4|28cWHy zH!p9a?}r0Zd=nr`reH>{m93deSI4Z-qiPGtl0U$yo&!`SX5+-6FL3e*)3yZ}fsFz0 z+H@~imIxAO4h+a*i7{ZsWw+y^KXf2DuHH=OTx;C-d4B94}67uVfEtjEn z^(}NP3Mv2<56IwpGmxcLqhswBI3$gZ)tfP$e);XFU%Zn1Y-|G1<7r#l>OV<)MW+AOAWv(}+&&Pg zt0Km*#oxVIlY-p2j%ZtrB{dyEtvEG<+&(~pny6_^=h!XqrbHeT5INv+)qh8twpq{x zhdF_qB09GW%UAu8H02)ZKsqaR8L?l#tP z?{(7!hn^qj%}s4XtKYs{LLlFI_tO{DcqPE)fsT2M1X#TEN^E;@=LOw~l-Ly@aN33B zyoKfgWty!nv`VG|aUJ%2G=b0m`3$XHKsQV3DFJ#)0E$}bG=$%}lBO@mrP6(dym88g zE1B6OIJv%oC;v&7N0p!3;QQv<4<|X|S176c2R^QvKh-+rnHCO#onJjr-HvGEbF*}{Iu7jQCjp)`5m$;5tOQH zEou{*3TzkKaR^-WlEVj5VN-D^)4`vs^7W~x*|M9cs zR@R?hBe(zi$q?}>J~|+?D|xgcPyXYYr&Evj}#N;)Ca768KoF0fTFYyg4I za-9vpc~Rl`7_x|AZw8I(jaS)HOP8tkc)Bs4n$Z` U1N|PE^8f$<07*qoM6N<$g7f9ccmMzZ literal 0 HcmV?d00001 diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..4d0a1c23f057b4a1f2217e175e84e3574a900004 GIT binary patch literal 1532 zcmVyR0=@V)nQ2L;j0yPF1G{KaW_((J{XpCAHZrm6*u34%Z{{T%~U?Q%F z5o2Pg#u#M*#RSu&MWJacEn2`9+i9osy7%~f-<^)d&a|BeGb8CGw;9fyd**xozSp(( zbo6RG0GELW_+Wa- zIyCB=dQi9PVWG_hpiMR6A_O~jv3(*l1T#Z`*I%6xy)rs`IVNCw1F9_iuP${z7$Ny+ zy+IJoK3eCKJy^LiVHgUgN2RsF_$DCd^`&PtK`^Twof9)wF1ZgAGClq?hR=UMI3JKT zMgMF&hCodV244IytyWmzHc@;2%)1DOZV{|{#qI?uV=H+4nNw)q+fRaUHq~nW8X9wQe%N(w+HV-{r* zkwrZ6v8#&4*~+q8!PhbbL;H%In`Ddjhp>lKNzV@RF9geZfI-Zp-pwVQ!KL{}S{O7HQ_% zm9tp7J0rOhqg7w+WVk?Vi z?sx*NUB?ioeT-P;!w45air5h`ISzXDl!|vAd>VgD4yR&F+4)F9oEYeN z^dyY!zp6oE1+VdK zle1J-??mt6S0op6wV}(D5sJ<%=$$QHXy5-7r5zy{- z=L1LIAgNoaEI=Ta)>8hh%e-aEf{sUQ?A~{bT1iK!aWRj`@;q7ya@)Ftq?rViQNosK z)+H%1bmFy7u(WVf)~aiFx1-*Z!?D z+-DdFkUT3N?yOwRhY^V(mI>p)X2(|~i*;#n2~H5z|Jy>oPYFA5qdIh}$}=?#X|v zOs>syeZGLGrScAH*%O{agRc0qsjVSpRsnH!tibb$a_KKJz{H|U8MSDn>s#@-izh`q zWa0S)Hx){C@o!sRg}?7i;O^~fRLA(!)-gt=)+HanlOjeEUxg>tA}!|4h~V{mgr?!4 z_XzF68EY7o>CK1L(OX;hfDBo9K4F-})&@iq%*2gfzuj=Gpf-Ai!(}EUlOi6n$U)FW zfC5Q+#3$ICSQGBZEwj(6&E7>}X1IA)s_``Y1e=qjKl#Lw)tQ~gTZz(4tDH@l$SNQf iTN~Ri)oe3TCf7gyu;!rsPYEOd0000 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..2ffbf24 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..540180bc27f43ab4c9b18c152497c873a621ddbb GIT binary patch literal 1538 zcmV+d2L1VoP)j9%00}v)YwQL~P@}95gCww!~+T%Zs z+e9@ju<(U~1J}vW^L0HN)`0Z>I#whG(~S6Ff-vk70LPg{thF2c8^;iB?W6@gU-c>& z;e7-Ka&xyaap43q=}AOd;{Jx+D~J++)qTTw;kA#@)!SDubsGc&-1>I{M-Lys)eAoo zkhYo{QA^T^Y``nz=|M^CdHV}=r}~h-?a+pqAsDzXiuMYKCnE%85?_9@2ieSxBDHi? z!!~MZfo&kQepEET08A?agFyJdR)D|$Gfg{!^eqR;?p{1TxC3r(PH5I(#MVVmp?w=* zQ1coH(=|cB@DYu5(y6Ygb*nBdGbdFwcc?39P%Ys;_1z?@FkY5LfZk~Vfv%t}@9yi` zs7wJdP_OI>6nlgam4K)KL8xjrqsZt@*~roc^Y^?%q89Z3cbEnkmI2!~7Cb85_UJR8 z3poL-MhVkWaI(|*{;Lm>xpNIR`E-}0G02mQ?0N^CJ?rUyfzC|=R6tAA#MH!JICFeWU{9PnO9=XhI5^Ob| z-}^qCIiikd3XM$5#Q4vj;@4vb$zVGOoQ3S{4Ro&=#xpzj!8VeBt1H?^IQ4|`tw1SL zJXb8onk4Pm0?dxuxHWYZ=TCnlV`7OOwD7#PRW!fDw+Y>N z05$F<6fBbS{eEyO75&d~X1l0I;97imfO!mFk!PksAvbF=7O zJB-x8h=7{7c${*dQ$!iy%K4MHcIkwSORXElswcKeCg7*_x>;&$B|B?VvNkmGrlb+M zEbsndu~RnaJPB=>Ha2d1i#$=QP*62hqd@tADeuf+U}%&)&Ss&`<#Wd{{?id$x$uMR z_^LHq&`bWI-qpe2r>&TP@HJ%Ok!~BUb({V_S-}65!KQXTQ&^8g0^HExn?KO z3Nf%JG2gZ3)E(HcWj7L?eN=~}smPfYuhrf)fWfE72moYU)ApSVg9@lDh)$b`R=eQ2 zIq^hX)~?(15*|xENm1G#*L)!K;v&iqxG?Uac#mKFl(ebt8=aKwu4Q*%Waqmg6d%d%0W#tR&KqUrEMMmPo44&G|p~M!B`v*_)EamJwqAi2=mhBbd24MJfDoT0dIsk~B&u zZU|7}&&^%#jlTH$LFvqe+ln;>z|lhosN;A!fL`n(gLSCriMOYuwdi-|yt?~B^#Rag z156>-z)}Fpd$DSH?4*&oF07*qoM6N<$g22Y?8~^|S literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd2052179fbd8b46a62f2c4d95f12581158f8fc GIT binary patch literal 4061 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 QY&*bE&jeAUdpGXC0PstT;{X5v literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..dc5141feaa25792f3ffffcc4786b14be9a3d5b74 GIT binary patch literal 1709 zcmV;e22%NnP)6Jtz#^u-5X^q=rS zltkjQka$Dm1Eh)=E=sE{Y%f3?QYp6G?(STU-*;x)-ICqX-JM+;!X(q_&dizle&@UW zzVD2N2Vm141Q15KUl8uxDfu{YZCI4+9l~p23*Ec`CrT51hxBqrC$5c%ih+o9^+|8_ zNSn+^6dS!P$_k=A+$qcIMD%o_X_y)x6J-Gr?(CJiQAID3?oiJ)!3YE#D}}_;EM}%> zgl8b4kL;1V_PvF+&OzvgFa1VQCN(e!3)8U+_;cuUEG^u`jj0Jy5(ozH%Z+V>o6 z+j|A%a}8Yw#;^R0(_eo`KxS}#GA4@76RZISu(W8ywrrZH^5;iM=BgV4+jkvA-~P98 z_S8qkCp-25HX+sk1JDhB#j>z%nXpZuwQ~T0nh*t)pu9eDRTM-!I0~^7D<5DJ5|w2r zLWs0k86-mh(y)?grUx zwK{2~;;3tkU~6ZjdujHC#{ZZUHh;(b+yo3?P@T7}1U5BvpuOt}!lh@xm6eK1 zHz5g;#L%~&V(j8c)YLUAGf6GoL49*K-uU1fG=#d~Bp8JThpzd30=K8H;ncA=3B)-3 zRkd_Ur%AYl9ea+Tqvs3QPGIHvMHfYd(P@FI>Us))4YW5Yq3T23O2Dg^egaRt&|Dv2 z^Woq&dhrJ=-kCx}Qy1M|hd{6qflYN}VzmWxfNWW{(vlK8woPVZs}M+kV328zoc{{( zg*oyUU7gbm4e|eG>HG=uqZ)S{k5S{@Jv&=husn6c?{f5$6sc0lc~k`(Fg<<_Q~&%5 zMYHIN!4zWnEN)HxfvP|~Qi*wWzZ#wn}MiE1U191=oTLbE!023}SEVfd;r`S)qej1SXq;AZR+ zu8;mgrshYmHiYhhmtEaC_67ivHY-i{AsL@TTVx-F$^#^vL-oKN44*rpOzN+p<78?J z%73ejQmLF zs%fvG>xW4rDMj$Of|GKi^qrVG6H!aV=dr!_07C8k%3%|Wv-tkRdz8WF$>eNo=@>wG zCu?DWKpNSU$Qux)v`%<3Ih>j)Nj6WuvHL|5j+2~?`P<_voU-lr>^`h~#7sF#dmFC< zC|$EkF0q3jL0wZs3C{_U!Egre>3>aK*H#g(4Ftl~hM*h(Y!0=etM7Rv7G|j;oF%jR z7hU}?5G83=*LB?+UnoN_R<4jkD`lZ~?@=_h^`kntnU0U4_wl3ZJYSa^rxz<$h?=S& z($cY|B;pc{ExmZ@ons{UA|=92iWr(HN{Urk^CwU+*(*asSyN$>omM3+ktsx&4~5!y zsZ(x^;bc)fp{V)tIFU98QKYA6#W!~)&Flg>>YPe!N!mGcyEv4GkUv<6&zqzfEAuJ_ zMRbG5@fc2eCTXis0Hqa0B&FM|tHzUuKE~j|_bJ-=Y341JACd_uezaWVuNq3W#*V!; z`V?My_axDEE}T*nmRsoP&#t&UFeiD}F~@bT!rG&|kxhNL#B{7ory^JF?O2j3xh)iO zpUqV2T%MX9MyJ*7^|fg-+j(*+vpdc@DjNnuLT8V&Ob4Z`X&8B%95SuiW%J2-pH?(& zD2%>Tq|Oo4d=c#maTgGVgP2OEnYNpEyJ2T8PQ(*t|Z_(%K@qJH5Sk zzv_)D<|#x)da*1s)A*T*%M|5qp}M9Kv9a8XFKb38ULT^5ow0A_L zMRhWFr@6W7J^NWL68iv~5Np8iuU!*B!OLCk;iy!t8By*vJDtoau%-!>wD6d(z3#DR z-*fg$UIO-d{kqikY;ixnBJld_f`^|);Nho2-fjN_a-`KM^%U^100000NkvXXu0mjf DwE{dP literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9bfbf3ba1ebb7340290a4762368b4e52e4e7ead4 GIT binary patch literal 1534 zcmV)EvOx1uQ6ay*tf-R+iUI?Yzz0S<>`_9a6SKYSTomtz)E}3-MIcLsy`F*$FXxQc- zZAXC}1-yNX^%m@XzE^IxkW=?Y#A^4%Ru;S#MZ|Uyel;bGjcgV|qf0bYKq)0%cKtC> z)%qkfi(Sm8VOd4jaeh0=d8VcZ5Ddl2%6JZ97M`AjVlj)|dykOrlb5(n{6_@Ci8W(l9R-BKJi~@9&%y_S z0mugBC|&4eup&ZX9UWb-VtVw7GNP2$sxCKbu?=IMw=2ubMrI&B!x5CvtR-0(wSZFv ztf=xRI?=Z6$|Rz^Cba4uilntFC@bV*qo5)+%ZhYbOQthC;g+lw4C>`$6pqQ;mL>mH zFIXTwpn>JBTyL4E?$sLWjVA?6GmlJq4ysD1Y-^E1%`if{$l9_3wfVJbid^4TZY^W4igb^a0=P)7+}y3St7 z(c1U$=>BiCw~0Jer28gLzViv%Q%6w9TBMl%-zdQ5qT~RR>cir~xGFlDW{{A1jQ{>M zrpMmKzJn)_O`B?UAZlQ8_zEU&f1~#0vkS0IR-j15D+@2(iRLY1=v4L->N0{+MB_=a zY#w*7f2Cr9h2=Q0==;d^FUfNzBC#YX9HkD`Lfya5w)K%G;#hhzh0)>5s`xSf z|G4oj(u)ts>T$Kk&rU@1qxx{QG|Bp1ht8ro`GWdwZ1{7k+{?%;J*DqoD44XgrjY78 zO`gg)71pgUn?Dm%6q?jYCR#d>I&@mqfccr*6wFsKH9CaHvm;92)Qe{kZ*HRi&I3Np z6`G&jq%CvkJ~Du4Vh{b_!p$E(#;u`C$|JE|ZRkGy8sR?U7OpL(02e2-kVb1qFFLvh zRAPBJ`3q+6-*5({o)bv6ccDPtEqi0@(JI`znl@z}py%irl`6ToaheQJVE4RqMg_D= zuRe~YzVbpWY#~S1?tLAt?T4w5%_FgdlVH`OA z78W1>Nl4e!_i!YLqUo%u+~ajJ^45*gKivotG@Oh3A2u+z5c`jvr<8flX(hsOWHTlO zvZ3%_+QXBUf|J|?1^8TU$TCZ_s+GuP7F8Kj2ZmqDzlP4ztvn*g_^c4-;jL)~e-sU4mB-yo$U6!Zmx zR*TcM)*0AFee&xH>&f)UwDE=*<1qnZZ&IA3lwsbE{=@)gU zR*x}2f@-mHQIaKYWX(+h5;ov&;bu^*!f{Ez6c8v4#G+a^YD(AoZhl`62?Q%!t~~6Y zH9?dzlMppk>zamo4m|1dHcWl27>BT}i)wjwjbb635wyyBl^EQI^O>$D^((-GICGUu z_c3F}q!aTtS~-{dO>9XRhLZpujQymhJcra0^(tU1qLsNyru&#NW75g@PX*1w!e1M_ zhzAoxYJOOcsjexAdFExVlIcEX%oMq+a4h^|w1h+MvgKKLBM>U_$~DbXYtrkbKr7_Z zY7}fJy-u)U%R9nLe_n8u9>heeldl`aFA4=2=i1mryMb3sjeh$i8uy9`XvB+w2C2KP k-%?`dP5q7n{~rbZ0xKVJCh9^0lmGw#07*qoM6N<$f?|T_Hvj+t literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..e427438ecf30c22bee970f6056ab73212100ae36 GIT binary patch literal 2868 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 YbsYvWP1KUk+I#?QO?{0zbzsc@08z^Gr~m)} literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..adb751d9f016435d5f8e00714d74d1d7fb90f480 GIT binary patch literal 3232 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..35a706596e8ea9b6979ef5c23cbe305018f17866 GIT binary patch literal 7491 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 Pz)$TlM5*kN#q<9It|AtL literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..4afb353bed3008f6a4dcf2daf99a89f2aae262db GIT binary patch literal 6020 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 Qfrq9fr!HG1ZT|It0AVg(;s5{u literal 0 HcmV?d00001 diff --git a/src-tauri/icons/android/values/ic_launcher_background.xml b/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 0000000..ea9c223 --- /dev/null +++ b/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #fff + \ No newline at end of file diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..43d4f2c28b19fa564e2637c7e482f4595e59290f GIT binary patch literal 61059 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

oVeV+5jRx1)Z*UT9RL?P*IkOe}{8JD*(H=%Too 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 Rwf(_r`-9c?|2tOOe*>v76u$rf literal 0 HcmV?d00001 diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..966300b835075b309903f9e04e94b5be31f9d9b3 GIT binary patch literal 12482 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 S^l#{!E6A?}$NKMo=l&Pe(Riu= literal 0 HcmV?d00001 diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..044918e53816ddaed71f9062e9d4629f3d652e71 GIT binary patch literal 11987 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 WA(OG%oOo~saP5-$#gac=@Bbec65cNW literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..5a435f3dba850da464ebdd6e4ee1c641ebc8d926 GIT binary patch literal 730 zcmV<00ww*4P)8rzL|*{9m@u2Els1hj(%1U z`24YTui97|eBXm(Z}tVKLIcnZMVLVwkik5+j>td~nej_x*gIgUxD2aShEfp$RcJ=W zVAFSB;K1l{0!MF93P?8?a1C#s+=6ZXfku()%$aM~us?G~z|5)(r|zL{+NjrT*cRXW za`B#Re8xUj)j|5uIMRpD!gWmHU$q5+L`JgEFB^zP4Gx|-1vxkP356A14GnAypv2&L zJ2>5$34@u{GCse12FI!4*r}@&K~By#*7I1)FCY|&W8&N_qOs&|k+zpWgBP<>!iR+i z^!*#9!V0e4doG+Uy`9Imk1r7$N?|NFjo4sP?A;?|A*if$U5QA8i3|#>?@``Z7vYMl zi^TN=QD71IhG1#vk+OIYd393lX!Z)J_!ar3r^J_w;nWfAJD9`wl~?4`xdBusBMaku z4kED;oSwRm#n*EZ&D8Zr_*qyMaIW1>OYc-)azJT2^$Jc--l23!ermDoZ_ajHU5_D$(~}z`rH*7pJQ*IRH-B&j0`b M07*qoM6N<$g0)~(cK`qY literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..97b23de1265b2c3d9dda99530b6ce29a854d4186 GIT binary patch literal 1300 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 Eg4&&MMF0Q* literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..1f109b0ef64d65642475e43112d62e8af8eec845 GIT binary patch literal 1094 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c18e4b67c53ef1f6a4098d5906d653d1075d6138 GIT binary patch literal 1786 zcmVoEf&Xbf=fy>CT4EW|FT77p(A07zB#aGRNWh?H|LYt#1Kp zE#QBI;)tM#bi~*5%Qx`z@dNld<_k2Es%!kPkWEfx?m~Oln+Ws(s)~&&qu3dR;mF}$ z%&ZNqYu3Zq$UPXmavC(Fn!MEf!V5VbvPn``k|a3*0`IDG`A>nWO8YUh0PD8w10z=B z>zHJr5aQ%ac06R0q^?4SFXR%~9AZnU;&LSf;*f&LjhqGD%QQ@5h%_^lOV{6b>pl+D1m2Y12P@Bn5f z(R@`w;mFJk!?yj0FwX73Tsh~(;mN~6fK~AbTsU(aPJaF_#HwoXJ13u&*>)Cc8`@y^ z2d6;O3~y~w&U#e20ZA$Ey~EO=*Tu7+0mtzfBtnv=WQ4B4dc?cm@q(MmuMwic`+l|} z@}Ftb(O3#@Ts{p0SN{MbDyKLcQdc7;;QHmWFxcOX?~_v`CZ=RUp#iF9h-J!EKPv;h z-A915c6zZ)&*kFT!<6{V@6vYxGNnPpr4u)>sMDHA7u=KrVMBw(|HR$g> zfznQT(pHtl8IkQtsdHgKY2@heApCj$E6}4P)f|JO607grG02Smi`VGnnfM+NP<12& zNyTupZJn>?!;g;TQ{TBSAUm4#@=^`KXAu0C2tL8!$xF9&ZU?0*M501Oz?v9^CpPSW z`sQ_5xQ{WI+k-uD71fj^iID;>U-%x?`4ZQVCSKpzvH@0izKr*dlq3^c6xF42sVdn7 z>8`yDlxs&kd%F+2nGX6Mdd~!x=djXP;K!=#nGXu0xDUogQ4Ot$acF;LH^h_841@~f zYnOh4JOA{P8&tnthx-i2(Viz~U(LITHXaZCmhsfh8x<+Y$jzG>t%MS}5t(HCv&1)keNH2qzRg zP_M)yT5>%~)wxexiOIB%k_Y-tN(h|E2$b={;s)(gVc2*D8kVKm@!a>oHri`rODC-A z*oM+{K*&n&rXf&L(|FQ=_RV|TP=hK>Cpo5c*BcOvOE;uIg|`je1Ogzvc`sBaA7c+p zJ(EPLg~N|E;a&rwYa7lRs9;t-QTI6PdjCfTN(M>Iq3W7OX8*+^u>XMr&DW=nf~Mtb z^I?)4m2ECwi$g1wda~`23W^6>7iub4%XIwNJ-k=q8gwe*Uw>THJe_72Z<1p4tdi# zw;8FaWh+S5HDiAKTD~S__7t_9vZhejC{dZywOW!S=i_KRuo=sFNN%O5XtZJ)H-$DL ziZBPc;Fwx$lc{3*Ok!b~k?^KJR9J=<{L)NphfvAq2UZpuR-!_gMqFt6gDMcE!q+Gz z;w^%jK87r#+Y&Jf?o;~N9VOv3;zEQLG3$PfsgNg6ne^4IxeI9M{oHEtnWm3|#A z%13FAhellDDV^Jcp|Dxuwki`T0|jvsE}TAyfK$H18U@F1^6U?WiT>Unc*G@9yOOC( zA<8?rL6RJ~a|3=q^(l0p_=w54*@j8P>=W3o2r11YCXc2++`)+QjmVMXA)6$1B}uxO zg(?>4gF}xw=4{>8UU_ww91q!K35OmhZPsn$M?*2h!Of~Uf?QAvIN9ulr3vgMpi4lP cfR^m|AHlQYpj@b~MgRZ+07*qoM6N<$f~n_dc>n+a literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e7a2c86acd13f8fd6a5b8ad5a4f55b9cbe30f085 GIT binary patch literal 2489 zcmV;q2}bsbP)O{GMw+N!F6^a528B&Y$BI5z_^2{n)yob0amKEpX@W_?-j+B=)IJ+u4&t5vM^ z?Ee10J#)@?t}*+z4%tv^RhF(GP~a+X)l%Rpa22>}DR33IHMq>GeUheuA3Jz(2@ z0GHs@b^2Q@Sh);rD;IchHBE9EIQY%0Bu(OM;NfI3{vqKgu;zzZ0$xk(jtHd zd1X>D%*H-PDiNdev_dz-6$?#8;A*;oOfrgaV>|ZU`z!`V4xz1U0HIKmsE6R{1RL2* z649BDF?sG4oIiOOv(amG&bEpG*1QI87}#{;2DAdP@ru4<}koz((h z81xJQH?B?N^*=q2D;NKPNaF_20DA(LRY4{d#m2rd9Qx5wQU^NH$&3K5X~8#U^8#!O zkfBJ3Egb&-^0T;d;kewXUI5m;7%$ebya7wB18kO*e@Kjgqibv9=@_*TGU=?mbnj!o zM00x&a@mw8A=1lN2MlHuL*IB>Q~^t@ZkVX$rRyf$sTr~-?b!XzpNO$@1gXm4GTmZU zz{ZPpEUSWgSbWZG8t5N>5DiV8boXYf3@+UY%ww5uF)Lu>RZBW{CEm;DZE5dC%Z6T3 z4e7F_*DTXj5Jk>Bmg$yVZ;2r~un{s4ZXknZ=T?j7YQg5QmHWi==eO4*wouc$VF}6# z-0DlN3OTBHt^!vr_#;@pg0Bvajh4sSE8@^z_7I-B2CTocb-^0n{FOiGhEU%T0kHqlxd!EC<@Ij1eCBF>#Wf~J

J21g#IpwmJ55&{dZdv>~A z8|cs*5NYZt4K6)c>OMOAH+=co3ux`=MwZmWa{8CSF~bI~|9=vb=U+oxN1uRYm(UG5 z2b<_Ux)~|-4uZ=T7z9=ktP)B+8FW5;{Kgb6oH`0V|4M$m11@l$BsLklF2M5VEWgj% zrA%~x#YY&dCIagS60t0$Tg+wLBUU*kVl%V2@V{fEcUwFKRq7Vp>8Z20JpKv+#&>aX z<77#c4*1~oUodlRf>ckVL&p@jEA!Ctw_k=3ixN6l=X@RovB%#&OcEX{$q*E{3k-`S z1{+#&b>a;i?3y=;zfvxP{cok;*v6PaKvscQL7Cfj%qJ zomnE?wue+qO@qt8b@z`g;+u{>ZN&%g|CRLVb<(HioIYJhzb;eZ249!g%X|%IZM!M; zZ#g=pe!-Q>9m=R~+w)y?-M)*Yb=C!$nLvb?z*Ss4b<8nrE)$riUq^&~F_?Tkzb{MD zzH#$jN}@g_^%d-q)i1d8$mP;#Y`Kk6_D>NwvvU&cfY|7O7`G(N?Rb19;zGZUE}~xo zF1gRcW8XuBa$Qy~yY5w&`uZ~imfkpaj6Q+pwoL*y!|s6FjBB5~Pjy?bQCxz9Eoq@& z6NU7P;}TS`(c0OEJN7;1)EDTw(z#B+CV|fG1`O``juf8g3xT2Ev*BW!CPf-W=$G!6 zS>3VsQM7k4&`J7w6u5H$v;En1;2DWS@MREUKLciGM#z55QXTzcJ+T5GY>Fml%qq3Oy)E0x`m z81CT6x6!$2n3zMtkwBNbCWv+&|HE^lpYwE$|7Byiv3D=}haMn_&-tom#aG`o-|kJV zA);kZiicKsdq;`)E{yL=9T(BBq5V%&teACl##dbfy59me8Pb_MN1t3o%ks+*hhd&Y zaJ_76#}@3^_qe<-ucsBbC8{YinH;Hw+cCKN&=Oj)ziw)WKy^j$6>LhiM^<5pC z?!R!OOUuZxEu>||9wYkIta<8DKrPmaRVKX#QY4jo zzV%~b4@1HZ>;=UTz1!~=snvbl_+t9GoZGf+>RfCi-up0?_UJ->@8@1))9>~2Y}`__ zR?)gIzc+E4L1;(_YR{XwW-|=(*1mwR^6OO%XW5P;q8TOj8MsTXyHzhMzu)HV_mtR=V;2c@ZyA-A)7AFsRvp|-+7?%~f9GgXIbi|f zycl0s4RHB$Hg6xJ;^whs_seGNV1qlKfV9N_Uk;RN9aN-~_Tu=&av5i>CVC>O44xnN zQ>{{p=lZ?(SAh$^^!}O^b&%Avjd>AdlqQ&iIWiQrJ~*>x}30hU~z6vr9vTA15dZq+-NQC+$CPja27 z-HB9H2A79Ma<-L6@bRQL9%$z0EI@UD%~38dL~)TM&b;{>ai`&-u|@Y{$arWZ&o<-{ zeEj8w!lpBQ)Q6okx2g*GT!v}LUHYFt{}eZ-Kct@?_tb$-)!tuxc8-dY zPg6zW{WK1$$qj0|oF$(eumi>h=8_tkblFE5Db18f;$O#}$HcpTq;jcMp5<6LER#p@ z@uWB&XvTB5r0qZ8i&+61FV?ZF3Rq$*8IoBsxOsrNR*Q`scZ%~sGoHH@Jh4SVZZ0aU zfQ=XHSWyKP8gK1Z{%;@-wpnX$ZI1C$=D?P;IB*i<>`c zZN#f@0ynkB7@$Iq3S0%QS_)hRu38FQ1+D^Dt)lBIdj5FBm_fh;00000NkvXXu0mjf DS}NVy literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..97b23de1265b2c3d9dda99530b6ce29a854d4186 GIT binary patch literal 1300 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5091d4ccd9cb54f166783375f1c52e91ba6d5eb8 GIT binary patch literal 2368 zcmV-G3BUGG=h+qeYcK)|uFfjA{Vph;V`QPQa4A@?@qQmK9DLm&FuH(I4nRaI4e zEp5|QtyHyA+m|XA5zI?6tD1VIE~1YiVU1YmST07d|Y^RO#%6Qxur2k#04cB=!R zQks9j&1K*|BHV)?H$1lXA` zJ~=;w6rI{?Lae!+UUPy6kS&iN+I!m;jPCpvZd87Mpi}2VZ_-AF^3`a4L&3Lq|;iKLJ4oy;D142;$MoQr!O{61E%-%W-w z=Z&jUJdhs534>!EyKT%%WgJOTbKaM zQI+1>ZeilpU*YiUKcU}yO?gyIpde}N{yjg&*8879BJL2^w0zzB*c-pW+y8h8E$uz> z+6}>-?vsdL#TOp^5yro`*Vnyk$O2t4x^(JO%*|dT#X2qJU{QID3io&3`XxrTKjUkF zT0nQ{J^KliX(}xfMb%_Qq(2eALc%~iPh`WDg_V2^RP@wTy|Z;*K0iq^(mzRy z!%bCT{##QEaQ4J2xbnYur9S6-l)Aq~N;6j$K~_&X@JSoj_2wK(ocxchbL!AWoILUe0uUtuv?xSQP5fR2&ma;)2mw?cIF3-6QlO#_W}8 zoH_nCvA&$^lvljg3xI4KUOl1!F2umzNg_16d$C9QY>&61b4Jpix?R??7Bw*%Vl5Yk+YnMqN1GjqZF=lCax|S(w2{ zr+{^%UqkQeF`2k$D>50k1E-GuiF#MElyzru=0ATeP#3<2>)Tap@4+1-UnNU~#Lx!h zfl68{+S;2ia_={<1JF>K>B}dnK0YXQ@3pJv3e<(Kak5El+Od}=`IV<7sybGrZy~s* ziLwlT{wYiz`~}gMIK5^{xfu1Q96j(ep(inE_;E~!1cBGtGl-$PA2-%#HE<=u0A`8w z^{g7ehAn#}F|cdr0sQFvF-%{YBI?pAmdVtGW8u*FW9aH#A&s34Ukg)vkGL{Z8UvJ5 zs7{RJwa~c9)TODVi>S+U1YYp-9pL~_v#6S*uBf}QcEbZ=h062G1zF0d*G!xT?;`3l zI82u4)I^Vtu8n4ISCp2~MUaIChSbXJvP_ocq~C?QY=4%zr{GOcsmKBsXJkXHhqpbA zR#IuW7n>)e_)H}}K}Yu*+%^6rSTo>j5w&&{k-0JsR`d-}Mt)8}?CM>QZaUjK`!ROk zi|Aa*)WxZ({zSXBdh?7G?*94@vE!j16o4|-NsAZzEHqYcOqoZFMZg=DWuXC^ib~}v)LaZgj>WXzba#GQjC-j_YqCy!FW%KhWiMbh}KSAakI~ee^)T#rwZ^J!b`mU#@0h(eED>9Vg>YrU_qdaUh!v}bB+=wu-;nP)^sU|^pCzcWFE9WzDVHXV9wuJ*FiJ18 za+gO*WM7>m+I-@{v00LM;9hB zf9;a&;;$hV%k^<;*wSyjM8nmY7rFWy&h&jizY)-GFv@a`6^?W!=% z7;1l}&(3R1=qHJ`7XJO_erjTRL#9ssHf{RewF920dGY;!$?g_whjvlF3b$gY&|;`* zsThyJvNVnz*pEZ6yd>>*OBs+_>K>|`op_FdcemgT8hDQZUM62ykD)@q^OVW9oG>`% zvD=E}Q3F%y)r%DZFfU25(!nmw8#HjMuIV5M57%R=!r7J+2FE;c+s|U_r~KLuF?D({ zH%HD;h7Ma&$?qA{L|v{TJCw5L literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..fcb3d853e22bae04a7245c34e1a27693b0654f7c GIT binary patch literal 3126 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%0DQt0dj*b$faio4r8f`w$xw6TW~0}*%vRV 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 IosJvkzlLKrxc~qF literal 0 HcmV?d00001 diff --git a/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..82ef37a2c5f4e68f2e21e429ad3f0a9db5a4798b GIT binary patch literal 4103 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 YsIZzV{|$+x8GL}D9z^$@_Jhd(0HB}auK)l5 literal 0 HcmV?d00001 diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs new file mode 100644 index 0000000..db38fba --- /dev/null +++ b/src-tauri/src/commands.rs @@ -0,0 +1,163 @@ +//! Tauri commands: the typed IPC surface bridging the web UI to `mtgc-core`. +//! +//! JS passes camelCase argument keys; Tauri maps them to these snake_case params. + +use std::path::PathBuf; +use std::sync::Mutex; + +use mtgc_core::{CardRef, Deck, DeckEntry, LibraryCard, LibraryFilter}; +use serde::Serialize; +use tauri::{AppHandle, Emitter, State}; + +use crate::state::AppState; + +type Db<'a> = State<'a, Mutex>; +type Res = Result; + +/// High-level status for the top bar / first-run gating. +#[derive(Serialize)] +pub struct Status { + pub library_path: String, + pub reference_path: String, + pub reference_ready: bool, + pub card_count: usize, +} + +fn status_of(s: &AppState) -> Status { + Status { + library_path: s.library_path.display().to_string(), + reference_path: s.reference_path.display().to_string(), + reference_ready: s.catalog.is_some(), + card_count: s.index.as_ref().map(|i| i.len()).unwrap_or(0), + } +} + +#[tauri::command] +pub fn get_status(db: Db) -> Res { + Ok(status_of(&db.lock().unwrap())) +} + +#[tauri::command] +pub fn set_library_path(db: Db, path: String) -> Res { + let mut s = db.lock().unwrap(); + s.library_path = PathBuf::from(path); + s.reopen()?; + Ok(status_of(&s)) +} + +/// Download Scryfall bulk data and (re)build reference.db, then reopen. +/// Runs on a blocking thread so the UI stays responsive and progress events flow. +#[tauri::command] +pub async fn update_card_data(app: AppHandle, db: Db<'_>) -> Res { + let reference_path = db.lock().unwrap().reference_path.clone(); + let app_for_progress = app.clone(); + + tauri::async_runtime::spawn_blocking(move || { + mtgc_core::ingest::build_reference_db(&reference_path, |msg| { + let _ = app_for_progress.emit("ingest-progress", msg.to_string()); + }) + }) + .await + .map_err(|e| e.to_string())? + .map_err(|e| e.to_string())?; + + let mut s = db.lock().unwrap(); + s.reopen()?; + Ok(status_of(&s)) +} + +#[tauri::command] +pub fn search_cards(db: Db, query: String, limit: usize) -> Res> { + let s = db.lock().unwrap(); + Ok(s.index()?.search(&query, limit)) +} + +#[tauri::command] +pub fn add_owned(db: Db, oracle_id: String, delta: i64) -> Res { + let s = db.lock().unwrap(); + s.catalog()?.add_owned(&oracle_id, delta).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_owned(db: Db, oracle_id: String, quantity: i64) -> Res { + let s = db.lock().unwrap(); + s.catalog()? + .set_owned(&oracle_id, quantity) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_location(db: Db, oracle_id: String, location: String) -> Res<()> { + let s = db.lock().unwrap(); + s.catalog()? + .set_location(&oracle_id, &location) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn deck_contents(db: Db, deck_id: i64) -> Res> { + let s = db.lock().unwrap(); + s.catalog()?.deck_contents(deck_id).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn search_library(db: Db, filter: LibraryFilter) -> Res> { + let s = db.lock().unwrap(); + s.catalog()?.search_library(&filter).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn list_decks(db: Db) -> Res> { + let s = db.lock().unwrap(); + s.catalog()?.list_decks().map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn create_deck(db: Db, name: String, commander_oracle_id: Option) -> Res { + let s = db.lock().unwrap(); + s.catalog()? + .create_deck(&name, commander_oracle_id.as_deref()) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn delete_deck(db: Db, deck_id: i64) -> Res<()> { + let s = db.lock().unwrap(); + s.catalog()?.delete_deck(deck_id).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_deck_format(db: Db, deck_id: i64, format: String) -> Res<()> { + let s = db.lock().unwrap(); + s.catalog()? + .set_deck_format(deck_id, &format) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_deck_commander(db: Db, deck_id: i64, oracle_id: Option) -> Res<()> { + let s = db.lock().unwrap(); + s.catalog()? + .set_deck_commander(deck_id, oracle_id.as_deref()) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_deck_card( + db: Db, + deck_id: i64, + oracle_id: String, + quantity: i64, + is_commander: bool, +) -> Res<()> { + let s = db.lock().unwrap(); + s.catalog()? + .set_deck_card(deck_id, &oracle_id, quantity, is_commander) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn card_decks(db: Db, oracle_id: String) -> Res> { + let s = db.lock().unwrap(); + s.catalog()?.card_decks(&oracle_id).map_err(|e| e.to_string()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..849259b --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,43 @@ +mod commands; +mod state; + +use std::sync::Mutex; + +use tauri::Manager; + +use state::AppState; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) + .setup(|app| { + let data_dir = app + .path() + .app_data_dir() + .expect("resolve app data dir"); + std::fs::create_dir_all(&data_dir).ok(); + app.manage(Mutex::new(AppState::new(data_dir))); + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ + commands::get_status, + commands::set_library_path, + commands::update_card_data, + commands::search_cards, + commands::add_owned, + commands::set_owned, + commands::set_location, + commands::search_library, + commands::list_decks, + commands::create_deck, + commands::delete_deck, + commands::set_deck_format, + commands::set_deck_commander, + commands::set_deck_card, + commands::deck_contents, + commands::card_decks, + ]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..43f9bed --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevent an extra console window on Windows in release. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + mtgc_app_lib::run() +} diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs new file mode 100644 index 0000000..2bb1d81 --- /dev/null +++ b/src-tauri/src/state.rs @@ -0,0 +1,58 @@ +//! App state: the open [`Catalog`] and fuzzy [`NameIndex`], plus the db paths. + +use std::path::PathBuf; + +use mtgc_core::{Catalog, NameIndex}; + +/// Managed application state, guarded by a `Mutex` in Tauri's state store. +pub struct AppState { + pub library_path: PathBuf, + pub reference_path: PathBuf, + pub catalog: Option, + pub index: Option, +} + +impl AppState { + /// Initialize with default paths under the app data dir and open the catalog + /// if a reference db already exists. + pub fn new(data_dir: PathBuf) -> Self { + let mut s = Self { + library_path: data_dir.join("library.db"), + reference_path: data_dir.join("reference.db"), + catalog: None, + index: None, + }; + // Best-effort open; failures just leave the catalog unloaded. + let _ = s.reopen(); + s + } + + /// (Re)open the catalog + rebuild the name index. Only opens when a reference + /// db exists — attaching a non-existent file would create an empty (tableless) + /// db and make every query fail. + pub fn reopen(&mut self) -> Result<(), String> { + if self.reference_path.exists() { + let catalog = Catalog::open(&self.library_path, &self.reference_path) + .map_err(|e| e.to_string())?; + let index = catalog.build_name_index().map_err(|e| e.to_string())?; + self.index = Some(index); + self.catalog = Some(catalog); + } else { + self.catalog = None; + self.index = None; + } + Ok(()) + } + + pub fn catalog(&self) -> Result<&Catalog, String> { + self.catalog + .as_ref() + .ok_or_else(|| "Card data not loaded yet — click 'Download card data'.".to_string()) + } + + pub fn index(&self) -> Result<&NameIndex, String> { + self.index + .as_ref() + .ok_or_else(|| "Card data not loaded yet — click 'Download card data'.".to_string()) + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..9f4bd0e --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "MTG Library", + "version": "0.1.0", + "identifier": "com.garrettmills.mtgc", + "build": { + "beforeDevCommand": "npm run dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "npm run build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "label": "main", + "title": "MTG Library", + "width": 1200, + "height": 800, + "resizable": true + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..453d6c8 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,170 @@ +import { useEffect, useState, useCallback, ReactNode } from "react"; +import { listen } from "@tauri-apps/api/event"; +import { open } from "@tauri-apps/plugin-dialog"; +import { api, Status } from "./api"; +import { HoverPreviewProvider, usePersistedState } from "./components"; +import { CardEntry } from "./views/CardEntry"; +import { LibraryBrowser } from "./views/LibraryBrowser"; +import { DeckView } from "./views/DeckView"; + +type Tab = "add" | "library" | "decks"; + +const svg = (children: ReactNode) => ( + + {children} + +); + +const NAV: { id: Tab; label: string; icon: ReactNode }[] = [ + { id: "add", label: "Add Cards", icon: svg() }, + { + id: "library", + label: "Library", + icon: svg( + <> + + + + + , + ), + }, + { + id: "decks", + label: "Decks", + icon: svg( + <> + + + + , + ), + }, +]; + +export default function App() { + const [status, setStatus] = useState(null); + const [tab, setTab] = usePersistedState("active-tab", "add"); + const [progress, setProgress] = useState(null); + const [busy, setBusy] = useState(false); + + const refreshStatus = useCallback(async () => { + setStatus(await api.getStatus()); + }, []); + + useEffect(() => { + refreshStatus(); + const un = listen("ingest-progress", (e) => setProgress(e.payload)); + return () => { + un.then((f) => f()); + }; + }, [refreshStatus]); + + async function updateCardData() { + setBusy(true); + setProgress("Starting…"); + try { + const s = await api.updateCardData(); + setStatus(s); + setProgress(`Loaded ${s.card_count.toLocaleString()} cards.`); + } catch (err) { + setProgress(`Failed: ${String(err)}`); + } finally { + setBusy(false); + } + } + + async function chooseLibrary() { + const path = await open({ + title: "Choose or create library.db (put it in a synced folder)", + filters: [{ name: "SQLite", extensions: ["db", "sqlite"] }], + }); + if (typeof path === "string") { + setStatus(await api.setLibraryPath(path)); + } + } + + const ready = status?.reference_ready ?? false; + + return ( + +

+ + +
+
+ {status && ( + + {status.library_path} + + )} + + {progress && {progress}} + + +
+ +
+ {!ready ? ( +
+
✦
+

Welcome to your MTG library

+

+ No card database yet. Click Download card data to fetch + the latest cards from Scryfall (a few hundred MB, about a minute). The + reference data stays on this device — only your library file is meant to + be synced. +

+ +
+ ) : tab === "add" ? ( + + ) : tab === "library" ? ( + + ) : ( + + )} +
+
+
+ + ); +} diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..99ee5e1 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,109 @@ +// Typed wrappers over the Tauri command bridge into mtgc-core. +import { invoke } from "@tauri-apps/api/core"; + +// --- Types (mirror the serde-serialized core structs) --------------------- + +export interface CardRef { + oracle_id: string; + name: string; + mana_value: number; + colors: number; // WUBRG bitmask + color_identity: number; // WUBRG bitmask + type_line: string; + image_uri: string; // Scryfall "normal" image URL, or "" if unavailable +} + +export interface LibraryCard extends CardRef { + quantity_owned: number; + decks: string[]; + quantity_assigned: number; + location: string; +} + +export interface DeckEntry extends CardRef { + quantity: number; + is_commander: boolean; + quantity_owned: number; +} + +export interface Deck { + id: number; + name: string; + format: string; + commander_oracle_id: string | null; + notes: string; +} + +export interface LibraryFilter { + text?: string | null; + colors_exact?: number | null; + colors_include?: number | null; + commander_identity?: number | null; + cmc_min?: number | null; + cmc_max?: number | null; + types?: string[]; + subtypes?: string[]; + keywords?: string[]; + in_deck?: number | null; +} + +export interface Status { + library_path: string; + reference_path: string; + reference_ready: boolean; + card_count: number; +} + +// --- WUBRG bitmask helpers ------------------------------------------------ + +export const COLORS: { bit: number; code: string; label: string }[] = [ + { bit: 1, code: "W", label: "White" }, + { bit: 2, code: "U", label: "Blue" }, + { bit: 4, code: "B", label: "Black" }, + { bit: 8, code: "R", label: "Red" }, + { bit: 16, code: "G", label: "Green" }, +]; + +export function maskToLetters(mask: number): string { + return COLORS.filter((c) => mask & c.bit) + .map((c) => c.code) + .join(""); +} + +// --- Commands ------------------------------------------------------------- + +export const api = { + getStatus: () => invoke("get_status"), + setLibraryPath: (path: string) => invoke("set_library_path", { path }), + updateCardData: () => invoke("update_card_data"), + + searchCards: (query: string, limit = 20) => + invoke("search_cards", { query, limit }), + + addOwned: (oracleId: string, delta: number) => + invoke("add_owned", { oracleId, delta }), + setOwned: (oracleId: string, quantity: number) => + invoke("set_owned", { oracleId, quantity }), + setLocation: (oracleId: string, location: string) => + invoke("set_location", { oracleId, location }), + + searchLibrary: (filter: LibraryFilter) => + invoke("search_library", { filter }), + + listDecks: () => invoke("list_decks"), + createDeck: (name: string, commanderOracleId: string | null) => + invoke("create_deck", { name, commanderOracleId }), + deleteDeck: (deckId: number) => invoke("delete_deck", { deckId }), + setDeckFormat: (deckId: number, format: string) => + invoke("set_deck_format", { deckId, format }), + setDeckCommander: (deckId: number, oracleId: string | null) => + invoke("set_deck_commander", { deckId, oracleId }), + setDeckCard: ( + deckId: number, + oracleId: string, + quantity: number, + isCommander = false, + ) => invoke("set_deck_card", { deckId, oracleId, quantity, isCommander }), + deckContents: (deckId: number) => invoke("deck_contents", { deckId }), + cardDecks: (oracleId: string) => invoke("card_decks", { oracleId }), +}; diff --git a/src/components.tsx b/src/components.tsx new file mode 100644 index 0000000..4c9cda4 --- /dev/null +++ b/src/components.tsx @@ -0,0 +1,455 @@ +import { + createContext, + ReactNode, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { api, CardRef, COLORS, maskToLetters } from "./api"; + +/** + * Inline editor for a card's free-text storage location (e.g. "Box 1"). + * Saves on blur; used for owned cards that aren't in any deck. + */ +export function LocationField({ + oracleId, + location, + onSaved, +}: { + oracleId: string; + location: string; + onSaved?: () => void; +}) { + const [val, setVal] = useState(location); + useEffect(() => setVal(location), [location]); + + const save = () => { + if (val !== location) api.setLocation(oracleId, val).then(() => onSaved?.()); + }; + return ( + setVal(e.target.value)} + onBlur={save} + onKeyDown={(e) => e.key === "Enter" && (e.target as HTMLInputElement).blur()} + /> + ); +} + +/** Render a color-identity/colors bitmask as WUBRG pips (colorless = a "C" pip). */ +export function ColorPips({ mask }: { mask: number }) { + const letters = maskToLetters(mask); + if (letters.length === 0) { + return ( + + C + + ); + } + return ( + + {letters.split("").map((c) => ( + + {c} + + ))} + + ); +} + +/** Small card-image thumbnail. Hovering it shows the full-size preview. */ +export function CardThumb({ card }: { card: CardRef }) { + const { handlers } = useHoverPreview(); + if (!card.image_uri) { + return ( + + 🂠 + + ); + } + return ( + {card.name} + ); +} + +/** + * A search input plus a results dropdown with keyboard navigation: + * ↑/↓ move the highlight, Enter picks, mouse hover/click also work. + * `value`/`onChange` are controlled by the caller (which runs the search). + */ +export function CardSearchBox({ + placeholder, + value, + onChange, + results, + onPick, + autoFocus, + big, +}: { + placeholder: string; + value: string; + onChange: (v: string) => void; + results: CardRef[]; + onPick: (card: CardRef) => void; + autoFocus?: boolean; + /** Larger, more prominent input (used for the primary Add Cards field). */ + big?: boolean; +}) { + const [sel, setSel] = useState(0); + const inputRef = useRef(null); + useEffect(() => setSel(0), [results]); + + // Keep focus in the box after a pick so cards can be added in a rapid streak. + function pick(card: CardRef) { + onPick(card); + inputRef.current?.focus(); + } + + function onKeyDown(e: React.KeyboardEvent) { + if (e.key === "ArrowDown") { + e.preventDefault(); + setSel((s) => Math.min(s + 1, results.length - 1)); + } else if (e.key === "ArrowUp") { + e.preventDefault(); + setSel((s) => Math.max(s - 1, 0)); + } else if (e.key === "Enter" && results[sel]) { + e.preventDefault(); + pick(results[sel]); + } + } + + return ( + <> + onChange(e.target.value)} + onKeyDown={onKeyDown} + /> + {results.length > 0 && ( +
    + {results.map((c, i) => ( +
  • setSel(i)} + onClick={() => pick(c)} + > + + {c.name} + {c.type_line} +
  • + ))} +
+ )} + + ); +} + +// --- Hover preview -------------------------------------------------------- + +interface HoverApi { + enter: (card: CardRef, e: React.MouseEvent) => void; + move: (e: React.MouseEvent) => void; + leave: () => void; +} +const HoverCtx = createContext(null); + +/** + * Provides a single floating card-image preview that appears while hovering a + * card and tracks the cursor. Wrap the app once; attach `handlers(card)` from + * [`useHoverPreview`] to any element that should trigger it. + */ +export function HoverPreviewProvider({ children }: { children: ReactNode }) { + const [state, setState] = useState<{ card: CardRef; x: number; y: number } | null>(null); + + const enter = useCallback((card: CardRef, e: React.MouseEvent) => { + if (card.image_uri) setState({ card, x: e.clientX, y: e.clientY }); + }, []); + const move = useCallback((e: React.MouseEvent) => { + setState((s) => (s ? { ...s, x: e.clientX, y: e.clientY } : s)); + }, []); + const leave = useCallback(() => setState(null), []); + + return ( + + {children} + {state && } + + ); +} + +/** Returns a `handlers(card)` factory to spread onto a row/cell for hover preview. */ +export function useHoverPreview() { + const api = useContext(HoverCtx); + return { + handlers: (card: CardRef) => + api + ? { + onMouseEnter: (e: React.MouseEvent) => api.enter(card, e), + onMouseMove: (e: React.MouseEvent) => api.move(e), + onMouseLeave: () => api.leave(), + } + : {}, + }; +} + +const PREVIEW_W = 320; +const PREVIEW_H = 446; // ~ card aspect for the normal image +const GAP = 24; +const PAD = 12; + +function FloatingCard({ card, x, y }: { card: CardRef; x: number; y: number }) { + // Prefer the right of the cursor; flip left if it would overflow. + let left = x + GAP; + if (left + PREVIEW_W + PAD > window.innerWidth) left = x - PREVIEW_W - GAP; + const top = Math.max(PAD, Math.min(y - PREVIEW_H / 2, window.innerHeight - PREVIEW_H - PAD)); + return ( + {card.name} + ); +} + +// --- Grouping ------------------------------------------------------------- + +export type GroupMode = "none" | "type" | "subtype" | "cmc" | "colors"; + +export const GROUP_OPTIONS: { value: GroupMode; label: string }[] = [ + { value: "none", label: "No grouping" }, + { value: "type", label: "Card type" }, + { value: "subtype", label: "Subtype" }, + { value: "cmc", label: "Mana value" }, + { value: "colors", label: "Color identity" }, +]; + +// Priority for choosing a card's primary type from its (multi-type) type line. +const TYPE_PRIORITY = [ + "Creature", + "Planeswalker", + "Battle", + "Land", + "Artifact", + "Enchantment", + "Instant", + "Sorcery", +]; + +function primaryType(typeLine: string): string { + const left = typeLine.split("—")[0]; + for (const t of TYPE_PRIORITY) if (left.includes(t)) return t; + const words = left.trim().split(/\s+/); + return words[words.length - 1] || "Other"; +} + +function firstSubtype(typeLine: string): string { + const parts = typeLine.split("—"); + if (parts.length < 2) return "No subtype"; + return parts[1].trim().split(/\s+/)[0] || "No subtype"; +} + +function colorLabel(mask: number): string { + const letters = maskToLetters(mask); + return letters === "" ? "Colorless" : letters; +} + +/** Group cards by the chosen attribute, returning ordered groups. */ +export function groupCards( + cards: T[], + mode: GroupMode, +): { label: string; cards: T[] }[] { + if (mode === "none") return [{ label: "", cards }]; + + const keyOf = (c: T) => + mode === "type" + ? primaryType(c.type_line) + : mode === "subtype" + ? firstSubtype(c.type_line) + : mode === "cmc" + ? `Mana value ${c.mana_value}` + : colorLabel(c.color_identity); + + const map = new Map(); + for (const c of cards) { + const k = keyOf(c); + const arr = map.get(k); + if (arr) arr.push(c); + else map.set(k, [c]); + } + + const groups = [...map.entries()].map(([label, cs]) => ({ label, cards: cs })); + if (mode === "type") { + const rank = (l: string) => { + const i = TYPE_PRIORITY.indexOf(l); + return i === -1 ? TYPE_PRIORITY.length : i; + }; + groups.sort((a, b) => rank(a.label) - rank(b.label) || a.label.localeCompare(b.label)); + } else if (mode === "cmc") { + groups.sort((a, b) => a.cards[0].mana_value - b.cards[0].mana_value); + } else { + groups.sort((a, b) => a.label.localeCompare(b.label)); + } + return groups; +} + +/** Dropdown for choosing a [`GroupMode`]. */ +export function GroupBySelect({ + value, + onChange, +}: { + value: GroupMode; + onChange: (m: GroupMode) => void; +}) { + return ( + + ); +} + +// --- Persistence ---------------------------------------------------------- + +/** useState that mirrors its value to localStorage (JSON), so it survives + * remounts and app restarts. */ +export function usePersistedState(key: string, initial: T): [T, (v: T) => void] { + const [val, setVal] = useState(() => { + const raw = localStorage.getItem(key); + if (raw === null) return initial; + try { + return JSON.parse(raw) as T; + } catch { + return initial; + } + }); + const set = useCallback( + (v: T) => { + localStorage.setItem(key, JSON.stringify(v)); + setVal(v); + }, + [key], + ); + return [val, set]; +} + +// --- View mode (list vs. image tiles) ------------------------------------ + +export type ViewMode = "list" | "tiles"; + +/** View-mode state persisted to localStorage so it survives tab switches. */ +export function useViewMode(key: string): [ViewMode, (m: ViewMode) => void] { + const [mode, setMode] = useState( + () => (localStorage.getItem(key) as ViewMode) || "list", + ); + const set = useCallback( + (m: ViewMode) => { + localStorage.setItem(key, m); + setMode(m); + }, + [key], + ); + return [mode, set]; +} + +/** Segmented control switching between the list and image-tile layouts. */ +export function ViewToggle({ + mode, + onChange, +}: { + mode: ViewMode; + onChange: (m: ViewMode) => void; +}) { + return ( + + + + + ); +} + +/** A single card rendered as a full image tile, with optional corner + footer. */ +export function CardTile({ + card, + corner, + footer, +}: { + card: CardRef; + corner?: ReactNode; + footer?: ReactNode; +}) { + return ( +
+
+ {card.image_uri ? ( + {card.name} + ) : ( +
{card.name}
+ )} + {corner &&
{corner}
} +
+ {footer &&
{footer}
} +
+ ); +} + +/** Clickable WUBRG toggles that edit a bitmask value. */ +export function ColorToggles({ + value, + onChange, +}: { + value: number; + onChange: (mask: number) => void; +}) { + return ( + + {COLORS.map((c) => ( + onChange(value ^ c.bit)} + > + {c.code} + + ))} + + ); +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..81da346 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./styles.css"; + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + + + , +); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..c206999 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,715 @@ +/* ============================================================ + mtgc — design system + Terracotta accent (#C8553D) on a layered, Discord-inspired dark UI. + ============================================================ */ + +:root { + /* Layered surfaces (darkest → lightest) */ + --bg-rail: #16171a; + --bg-app: #1a1b1f; + --bg-topbar: #1e1f24; + --bg-content: #212228; + --bg-panel: #282a31; + --bg-input: #2f313a; + --bg-hover: #363841; + --bg-elev: #3a3c46; + + --border: #34363e; + --border-soft: rgba(255, 255, 255, 0.055); + + --text: #dbdee4; + --text-strong: #f5f6f8; + --muted: #9a9ea8; + --faint: #71757f; + + /* Terracotta accent */ + --accent: #c8553d; + --accent-hover: #d86e56; + --accent-active: #a8412d; + --accent-subtle: rgba(200, 85, 61, 0.15); + --accent-glow: rgba(200, 85, 61, 0.35); + + /* Complementary / status */ + --info: #46a6bd; /* teal complement */ + --gold: #e0b24a; + --success: #4bb073; + --warn: #e0a34a; + --danger: #d8503f; + + --radius: 8px; + --radius-lg: 12px; + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 6px 20px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 14px 44px rgba(0, 0, 0, 0.55); + + color-scheme: dark; +} + +* { + box-sizing: border-box; +} + +html, +body, +#root { + height: 100%; +} + +body { + margin: 0; + font-family: "Inter", "Segoe UI", system-ui, -apple-system, Roboto, sans-serif; + background: var(--bg-app); + color: var(--text); + font-size: 14px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +::selection { + background: var(--accent-subtle); +} + +/* Thin, unobtrusive scrollbars */ +*::-webkit-scrollbar { + width: 11px; + height: 11px; +} +*::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.35); + border-radius: 8px; + border: 3px solid transparent; + background-clip: padding-box; +} +*::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.5); + background-clip: padding-box; +} +*::-webkit-scrollbar-track { + background: transparent; +} + +/* -------- Controls -------- */ + +button { + background: var(--bg-input); + color: var(--text); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 7px 13px; + cursor: pointer; + font-size: 13px; + font-weight: 500; + font-family: inherit; + transition: background 0.12s, border-color 0.12s, transform 0.05s, filter 0.12s; +} +button:hover { + background: var(--bg-hover); + border-color: var(--bg-elev); +} +button:active { + transform: translateY(1px); +} +button:disabled { + opacity: 0.5; + cursor: default; + transform: none; +} +button.primary { + background: linear-gradient(180deg, var(--accent-hover), var(--accent)); + border: none; + color: #fff; + box-shadow: var(--shadow-sm); +} +button.primary:hover { + filter: brightness(1.07); + background: linear-gradient(180deg, var(--accent-hover), var(--accent)); +} +button.primary:active { + background: var(--accent-active); +} +button.big { + padding: 11px 22px; + font-size: 15px; + border-radius: 10px; +} + +input, +select { + background: var(--bg-input); + color: var(--text); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 8px 11px; + font-size: 13px; + font-family: inherit; + transition: border-color 0.12s, box-shadow 0.12s; +} +input::placeholder { + color: var(--faint); +} +input:focus, +select:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-subtle); +} + +/* ============================================================ + App shell: nav rail + main + ============================================================ */ + +.app { + display: flex; + height: 100vh; + overflow: hidden; +} + +.nav-rail { + width: 220px; + flex-shrink: 0; + background: var(--bg-rail); + border-right: 1px solid var(--border-soft); + display: flex; + flex-direction: column; + padding: 14px 10px 10px; +} +.brand { + display: flex; + align-items: center; + gap: 11px; + padding: 4px 8px 16px; +} +.brand-mark { + width: 34px; + height: 34px; + border-radius: 10px; + background: linear-gradient(150deg, var(--accent-hover), var(--accent-active)); + color: #fff; + font-weight: 800; + font-size: 18px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: var(--shadow-sm); +} +.brand-text { + font-weight: 700; + font-size: 15px; + color: var(--text-strong); + letter-spacing: 0.2px; +} +.nav-items { + display: flex; + flex-direction: column; + gap: 3px; +} +.nav-item { + position: relative; + display: flex; + align-items: center; + gap: 12px; + width: 100%; + text-align: left; + padding: 9px 12px; + border: none; + border-radius: var(--radius); + background: transparent; + color: var(--muted); + font-size: 14px; + font-weight: 550; +} +.nav-item:hover { + background: rgba(255, 255, 255, 0.04); + color: var(--text); +} +.nav-item.active { + background: var(--accent-subtle); + color: var(--text-strong); +} +.nav-item.active::before { + content: ""; + position: absolute; + left: -10px; + top: 9px; + bottom: 9px; + width: 4px; + border-radius: 0 4px 4px 0; + background: var(--accent); +} +.nav-icon { + display: inline-flex; + color: inherit; +} +.nav-foot { + margin-top: auto; + display: flex; + align-items: center; + gap: 8px; + padding: 12px 10px 6px; + font-size: 12px; + color: var(--muted); + border-top: 1px solid var(--border-soft); +} +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; +} +.status-dot.on { + background: var(--success); + box-shadow: 0 0 6px var(--success); +} +.status-dot.off { + background: var(--faint); +} + +.main { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + background: var(--bg-content); +} +.topbar { + height: 52px; + flex-shrink: 0; + display: flex; + align-items: center; + gap: 10px; + padding: 0 18px; + background: var(--bg-topbar); + border-bottom: 1px solid var(--border-soft); +} +.path { + font-size: 12px; + font-family: ui-monospace, "SF Mono", Menlo, monospace; + color: var(--faint); + background: rgba(0, 0, 0, 0.25); + padding: 4px 10px; + border-radius: 6px; + max-width: 460px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.progress { + color: var(--accent-hover); + font-size: 12px; + font-weight: 500; +} +.spacer { + flex: 1; +} +.content { + flex: 1; + overflow: auto; + padding: 22px 26px; +} + +/* Empty / welcome state */ +.empty-state { + max-width: 480px; + margin: 9vh auto 0; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; +} +.empty-mark { + width: 72px; + height: 72px; + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + font-size: 34px; + color: #fff; + background: linear-gradient(150deg, var(--accent-hover), var(--accent-active)); + box-shadow: 0 10px 30px var(--accent-glow); +} +.empty-state h2 { + margin: 4px 0 0; +} + +/* ============================================================ + Typography helpers + ============================================================ */ + +h2 { + margin: 2px 0 14px; + font-size: 19px; + font-weight: 700; + color: var(--text-strong); + letter-spacing: -0.2px; +} +h3 { + margin: 18px 0 10px; + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--faint); + border-bottom: 1px solid var(--border-soft); + padding-bottom: 8px; +} +h4 { + margin: 14px 0 7px; + font-size: 13px; + font-weight: 600; + color: var(--accent-hover); +} +.muted { + color: var(--muted); +} +.hint { + color: var(--muted); + font-size: 13px; + line-height: 1.65; +} +.row { + display: flex; + gap: 8px; + align-items: center; +} +.badge { + display: inline-flex; + align-items: center; + background: var(--accent-subtle); + color: var(--accent-hover); + border-radius: 999px; + padding: 1px 9px; + font-size: 11px; + font-weight: 700; +} + +/* ============================================================ + Lists (search results, deck list) + ============================================================ */ + +.result-list { + list-style: none; + margin: 8px 0 0; + padding: 0; + border: 1px solid var(--border-soft); + border-radius: var(--radius-lg); + overflow: hidden; + background: var(--bg-panel); +} +.result-list li { + display: flex; + align-items: center; + gap: 10px; + padding: 9px 12px; + border-bottom: 1px solid var(--border-soft); + cursor: pointer; + transition: background 0.1s; +} +.result-list li:last-child { + border-bottom: none; +} +.result-list li:hover { + background: var(--bg-hover); +} +.card-name { + font-weight: 600; + color: var(--text-strong); +} +.card-type { + color: var(--muted); + font-size: 12px; +} + +/* ============================================================ + Color pips (WUBRG) + ============================================================ */ + +.pips { + display: inline-flex; + gap: 3px; +} +.pip { + width: 18px; + height: 18px; + border-radius: 50%; + font-size: 10px; + font-weight: 800; + display: inline-flex; + align-items: center; + justify-content: center; + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25); +} +.pip.W { background: #f4efdd; color: #4a3f2a; } +.pip.U { background: #3a8fd0; color: #fff; } +.pip.B { background: #38343d; color: #cbc3d4; } +.pip.R { background: var(--accent); color: #fff; } +.pip.G { background: #479b63; color: #fff; } +.pip.C { background: #9aa0ab; color: #23242a; } + +.color-toggles { + display: flex; + gap: 5px; +} +.color-toggles .pip { + cursor: pointer; + opacity: 0.32; + transition: opacity 0.1s, box-shadow 0.1s; +} +.color-toggles .pip.on { + opacity: 1; + box-shadow: 0 0 0 2px var(--accent-hover); +} + +/* ============================================================ + Tables + ============================================================ */ + +table { + width: 100%; + border-collapse: collapse; +} +th { + text-align: left; + padding: 8px 12px; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--faint); + position: sticky; + top: 0; + background: var(--bg-content); + border-bottom: 1px solid var(--border); + z-index: 1; +} +td { + padding: 8px 12px; + border-bottom: 1px solid var(--border-soft); + vertical-align: middle; +} +tbody tr { + transition: background 0.08s; +} +tbody tr:hover { + background: rgba(255, 255, 255, 0.022); +} +.over-assigned { + color: var(--warn); + font-weight: 600; +} +.deck-chip { + display: inline-flex; + align-items: center; + background: var(--bg-input); + border: 1px solid var(--border); + border-radius: 6px; + padding: 2px 8px; + font-size: 11px; + color: var(--text); + margin-right: 4px; +} + +/* ============================================================ + Filters panel + ============================================================ */ + +.filters { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); + gap: 14px; + background: var(--bg-panel); + border: 1px solid var(--border-soft); + border-radius: var(--radius-lg); + padding: 16px; + margin-bottom: 16px; +} +.field { + display: flex; + flex-direction: column; + gap: 6px; +} +.field > label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: var(--faint); +} +.location-input { + padding: 4px 8px; + font-size: 12px; + max-width: 160px; +} + +/* ============================================================ + Card images: thumbnails, hover preview, tiles + ============================================================ */ + +.card-thumb { + width: 40px; + height: 56px; + object-fit: cover; + border-radius: 5px; + display: block; + background: var(--bg-elev); + cursor: zoom-in; + box-shadow: var(--shadow-sm); + transition: transform 0.1s, box-shadow 0.1s; +} +.card-thumb:hover { + transform: scale(1.05); + box-shadow: 0 0 0 2px var(--accent-hover), var(--shadow-md); +} +.thumb-empty { + display: inline-flex; + width: 40px; + height: 56px; + align-items: center; + justify-content: center; + color: var(--faint); + border: 1px dashed var(--border); + border-radius: 5px; + font-size: 18px; +} +.hover-card { + position: fixed; + z-index: 200; + border-radius: 14px; + box-shadow: var(--shadow-lg); + border: 1px solid rgba(255, 255, 255, 0.08); + pointer-events: none; +} + +.tile-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + gap: 16px; +} +.tile { + display: flex; + flex-direction: column; + gap: 7px; +} +.tile-img-wrap { + position: relative; +} +.tile-img { + width: 100%; + display: block; + border-radius: 12px; + box-shadow: var(--shadow-sm); + transition: transform 0.12s, box-shadow 0.12s; +} +.tile:hover .tile-img { + transform: translateY(-3px); + box-shadow: var(--shadow-md); +} +.tile-noimg { + aspect-ratio: 488 / 680; + border: 1px solid var(--border); + border-radius: 12px; + background: var(--bg-panel); + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding: 14px; + font-weight: 600; + color: var(--muted); +} +.tile-corner { + position: absolute; + top: 8px; + right: 8px; + display: flex; + gap: 6px; +} +.tile-badge { + background: rgba(0, 0, 0, 0.78); + color: #fff; + border-radius: 8px; + padding: 2px 9px; + font-size: 12px; + font-weight: 700; + backdrop-filter: blur(2px); +} +.tile-badge.warn { + background: var(--warn); + color: #241700; +} +.tile-remove { + background: rgba(0, 0, 0, 0.78); + border: none; + color: #fff; + border-radius: 50%; + width: 24px; + height: 24px; + padding: 0; + line-height: 1; + font-size: 13px; +} +.tile-remove:hover { + background: var(--danger); +} +.tile-footer { + display: flex; + flex-wrap: wrap; + gap: 4px; + align-items: center; +} + +/* ============================================================ + Toolbar controls: group-by + view toggle + ============================================================ */ + +.group-by { + display: inline-flex; + align-items: center; + gap: 7px; + font-size: 12px; + color: var(--muted); +} +.view-toggle { + display: inline-flex; + background: var(--bg-input); + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} +.view-toggle button { + border: none; + border-radius: 0; + background: transparent; + font-size: 12px; + padding: 5px 12px; + color: var(--muted); +} +.view-toggle button:hover { + background: rgba(255, 255, 255, 0.04); +} +.view-toggle button.active { + background: var(--accent); + color: #fff; +} + +/* ============================================================ + Deck metadata (format + commander) + ============================================================ */ + +.deck-meta { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 20px; + background: var(--bg-panel); + border: 1px solid var(--border-soft); + border-radius: var(--radius-lg); + padding: 14px 16px; +} +.commander-box { + display: flex; + flex-direction: column; + gap: 8px; + min-width: 280px; + flex: 1; +} diff --git a/src/views/CardEntry.tsx b/src/views/CardEntry.tsx new file mode 100644 index 0000000..026ba9e --- /dev/null +++ b/src/views/CardEntry.tsx @@ -0,0 +1,83 @@ +import { useEffect, useState } from "react"; +import { api, CardRef } from "../api"; +import { CardSearchBox } from "../components"; + +/** Rapid card entry: fuzzy search-as-you-type, ↑/↓ + Enter (or click) adds one copy. */ +export function CardEntry() { + const [query, setQuery] = useState(""); + const [results, setResults] = useState([]); + const [recent, setRecent] = useState<{ name: string; qty: number; location: string }[]>([]); + // Persisted so a "box" location sticks while entering a stack of cards. + const [location, setLocation] = useState(() => localStorage.getItem("add-location") || ""); + + useEffect(() => { + localStorage.setItem("add-location", location); + }, [location]); + + // Debounced fuzzy search. + useEffect(() => { + const q = query.trim(); + if (!q) { + setResults([]); + return; + } + const t = setTimeout(async () => { + try { + setResults(await api.searchCards(q, 20)); + } catch { + setResults([]); + } + }, 80); + return () => clearTimeout(t); + }, [query]); + + async function add(card: CardRef) { + const qty = await api.addOwned(card.oracle_id, 1); + const loc = location.trim(); + if (loc) await api.setLocation(card.oracle_id, loc); + setRecent((r) => [{ name: card.name, qty, location: loc }, ...r].slice(0, 12)); + setQuery(""); + setResults([]); + } + + return ( +
+

Add Cards

+ +
+ + setLocation(e.target.value)} + /> +
+ + + + {recent.length > 0 && ( + <> +

Just added

+
    + {recent.map((r, i) => ( +
  • + {r.name} + {r.location && {r.location}} + + owned ×{r.qty} +
  • + ))} +
+ + )} +
+ ); +} diff --git a/src/views/DeckView.tsx b/src/views/DeckView.tsx new file mode 100644 index 0000000..5d98cfd --- /dev/null +++ b/src/views/DeckView.tsx @@ -0,0 +1,377 @@ +import { useEffect, useState, useCallback } from "react"; +import { api, CardRef, Deck, DeckEntry } from "../api"; +import { + CardSearchBox, + CardThumb, + CardTile, + ColorPips, + GroupBySelect, + GroupMode, + ViewToggle, + groupCards, + usePersistedState, + useViewMode, +} from "../components"; + +const FORMATS = [ + "", + "Commander", + "Brawl", + "Standard", + "Pioneer", + "Modern", + "Legacy", + "Vintage", + "Pauper", + "Other", +]; +const COMMANDER_FORMATS = ["Commander", "Brawl"]; + +/** + * Deck editor. A deck's cards are split into two groups — "In your library" + * (owned) and "Not owned" — each with its own search box, and each groupable by + * card attribute. Deck-level format + commander metadata sit at the top. + */ +export function DeckView() { + const [decks, setDecks] = useState([]); + const [selected, setSelected] = usePersistedState("deck-selected", null); + const [newName, setNewName] = useState(""); + const [contents, setContents] = useState([]); + const [view, setView] = useViewMode("deck-view"); + const [groupBy, setGroupBy] = usePersistedState("deck-group", "none"); + + const [libQuery, setLibQuery] = useState(""); + const [libResults, setLibResults] = useState([]); + const [anyQuery, setAnyQuery] = useState(""); + const [anyResults, setAnyResults] = useState([]); + const [cmdQuery, setCmdQuery] = useState(""); + const [cmdResults, setCmdResults] = useState([]); + + const deck = decks.find((d) => d.id === selected) ?? null; + + const loadDecks = useCallback(async () => { + const d = await api.listDecks(); + setDecks(d); + // Keep the persisted selection if it still exists, else fall back to the first. + if (d.length && (selected === null || !d.some((x) => x.id === selected))) { + setSelected(d[0].id); + } + }, [selected, setSelected]); + + useEffect(() => { + loadDecks(); + }, [loadDecks]); + + const loadContents = useCallback(async () => { + if (selected === null) { + setContents([]); + return; + } + setContents(await api.deckContents(selected)); + }, [selected]); + + useEffect(() => { + loadContents(); + }, [loadContents]); + + useEffect(() => { + const q = libQuery.trim(); + if (!q) return setLibResults([]); + const t = setTimeout(() => api.searchLibrary({ text: q }).then(setLibResults), 100); + return () => clearTimeout(t); + }, [libQuery]); + + useEffect(() => { + const q = anyQuery.trim(); + if (!q) return setAnyResults([]); + const t = setTimeout(() => api.searchCards(q, 12).then(setAnyResults), 80); + return () => clearTimeout(t); + }, [anyQuery]); + + useEffect(() => { + const q = cmdQuery.trim(); + if (!q) return setCmdResults([]); + const t = setTimeout(() => api.searchCards(q, 12).then(setCmdResults), 80); + return () => clearTimeout(t); + }, [cmdQuery]); + + async function createDeck() { + if (!newName.trim()) return; + const d = await api.createDeck(newName.trim(), null); + setNewName(""); + await loadDecks(); + setSelected(d.id); + } + + async function addToDeck(card: CardRef) { + if (selected === null) return; + const existing = contents.find((e) => e.oracle_id === card.oracle_id); + const qty = existing ? existing.quantity + 1 : 1; + await api.setDeckCard(selected, card.oracle_id, qty, false); + setLibQuery(""); + setLibResults([]); + setAnyQuery(""); + setAnyResults([]); + await loadContents(); + } + + async function setQty(oracleId: string, qty: number) { + if (selected === null) return; + await api.setDeckCard(selected, oracleId, qty, false); + await loadContents(); + } + + async function removeDeck(id: number) { + await api.deleteDeck(id); + if (selected === id) setSelected(null); + await loadDecks(); + } + + async function changeFormat(format: string) { + if (selected === null) return; + await api.setDeckFormat(selected, format); + await loadDecks(); + } + + async function setCommander(card: CardRef) { + if (selected === null) return; + // Ensure the commander is in the deck, then flag it. + if (!contents.find((e) => e.oracle_id === card.oracle_id)) { + await api.setDeckCard(selected, card.oracle_id, 1, true); + } + await api.setDeckCommander(selected, card.oracle_id); + setCmdQuery(""); + setCmdResults([]); + await loadContents(); + await loadDecks(); + } + + async function clearCommander() { + if (selected === null) return; + await api.setDeckCommander(selected, null); + await loadContents(); + await loadDecks(); + } + + const owned = contents.filter((e) => e.quantity_owned > 0); + const unowned = contents.filter((e) => e.quantity_owned === 0); + const commanderName = + deck?.commander_oracle_id && + (contents.find((e) => e.oracle_id === deck.commander_oracle_id)?.name ?? + deck.commander_oracle_id); + + function deckTiles(entries: DeckEntry[]) { + return ( +
+ {entries.map((e) => ( + setQty(e.oracle_id, 0)} + > + ✕ + + } + footer={ + + {e.is_commander ? "★ " : ""}in deck ×{e.quantity} + + } + /> + ))} +
+ ); + } + + function deckTable(entries: DeckEntry[]) { + return ( + + + + + + + + + + + + + + {entries.map((e) => ( + + + + + + + + + + ))} + +
ColorsNameTypeIn deckOwned
+ + + + + {e.is_commander && ★ } + {e.name} + {e.type_line} + setQty(e.oracle_id, Number(ev.target.value))} + /> + {e.quantity_owned} + +
+ ); + } + + function cardsGroup(entries: DeckEntry[]) { + if (entries.length === 0) return

None yet.

; + return groupCards(entries, groupBy).map((g) => ( +
+ {g.label && ( +

+ {g.label} {g.cards.length} +

+ )} + {view === "tiles" ? deckTiles(g.cards) : deckTable(g.cards)} +
+ )); + } + + return ( +
+
+

Decks

+
+ setNewName(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && createDeck()} + /> + +
+
    + {decks.map((d) => ( +
  • setSelected(d.id)} + > + {d.name} + {d.format && {d.format}} + + +
  • + ))} + {decks.length === 0 && ( +
  • + No decks yet. +
  • + )} +
+
+ +
+ {selected === null || !deck ? ( +

Select or create a deck.

+ ) : ( + <> +
+

{deck.name}

+
+ + +
+
+ + {/* Deck metadata: format + (for Commander/Brawl) the commander card. */} +
+ + + {COMMANDER_FORMATS.includes(deck.format) && ( +
+
+ Commander: + {commanderName || "none"} + {commanderName && ( + + )} +
+ +
+ )} +
+ +
+

+ In your library {owned.length} +

+ + {cardsGroup(owned)} +
+ +
+

+ Not owned {unowned.length} +

+ + {cardsGroup(unowned)} +
+ + )} +
+
+ ); +} diff --git a/src/views/LibraryBrowser.tsx b/src/views/LibraryBrowser.tsx new file mode 100644 index 0000000..a3df1eb --- /dev/null +++ b/src/views/LibraryBrowser.tsx @@ -0,0 +1,235 @@ +import { useEffect, useState } from "react"; +import { api, Deck, LibraryCard, LibraryFilter } from "../api"; +import { + CardThumb, + CardTile, + ColorPips, + ColorToggles, + GroupBySelect, + GroupMode, + LocationField, + ViewToggle, + groupCards, + usePersistedState, + useViewMode, +} from "../components"; + +/** Browse & filter the owned library by name and card criteria. */ +export function LibraryBrowser() { + const [text, setText] = useState(""); + const [commander, setCommander] = useState(0); + const [colorsInclude, setColorsInclude] = useState(0); + const [cmcMin, setCmcMin] = useState(""); + const [cmcMax, setCmcMax] = useState(""); + const [type, setType] = useState(""); + const [subtype, setSubtype] = useState(""); + const [keyword, setKeyword] = useState(""); + const [inDeck, setInDeck] = useState(""); + + const [decks, setDecks] = useState([]); + const [rows, setRows] = useState([]); + const [view, setView] = useViewMode("library-view"); + const [groupBy, setGroupBy] = usePersistedState("library-group", "none"); + const [refreshKey, setRefreshKey] = useState(0); + + useEffect(() => { + api.listDecks().then(setDecks); + }, []); + + useEffect(() => { + const filter: LibraryFilter = { + text: text.trim() || null, + commander_identity: commander || null, + colors_include: colorsInclude || null, + cmc_min: cmcMin === "" ? null : Number(cmcMin), + cmc_max: cmcMax === "" ? null : Number(cmcMax), + types: splitCsv(type), + subtypes: splitCsv(subtype), + keywords: splitCsv(keyword), + in_deck: inDeck === "" ? null : Number(inDeck), + }; + const t = setTimeout(() => { + api.searchLibrary(filter).then(setRows).catch(() => setRows([])); + }, 120); + return () => clearTimeout(t); + }, [text, commander, colorsInclude, cmcMin, cmcMax, type, subtype, keyword, inDeck, refreshKey]); + + const deckOrLocation = (c: LibraryCard) => + c.decks.length === 0 ? ( + setRefreshKey((k) => k + 1)} + /> + ) : ( + c.decks.map((d) => ( + + {d} + + )) + ); + + function libTable(cards: LibraryCard[]) { + return ( + + + + + + + + + + + + + + {cards.map((c) => ( + + + + + + + + + + ))} + +
ColorsNameTypeMVOwnedDecks / Location
+ + + + {c.name}{c.type_line}{c.mana_value} c.quantity_owned ? "over-assigned" : ""}> + {c.quantity_owned} + {c.quantity_assigned > c.quantity_owned && ( + + {" "} + ⚠ {c.quantity_assigned} assigned + + )} + {deckOrLocation(c)}
+ ); + } + + function libTiles(cards: LibraryCard[]) { + return ( +
+ {cards.map((c) => ( + c.quantity_owned ? "warn" : ""}`} + title="Copies owned" + > + ×{c.quantity_owned} + {c.quantity_assigned > c.quantity_owned ? " ⚠" : ""} + + } + footer={deckOrLocation(c)} + /> + ))} +
+ ); + } + + const groups = groupCards(rows, groupBy); + + return ( +
+
+

+ Library {rows.length} +

+
+ + +
+
+ +
+
+ + setText(e.target.value)} placeholder="e.g. bolt" /> +
+
+ + +
+
+ + +
+
+ +
+ setCmcMin(e.target.value)} + /> + – + setCmcMax(e.target.value)} + /> +
+
+
+ + setType(e.target.value)} placeholder="Creature, Legendary" /> +
+
+ + setSubtype(e.target.value)} placeholder="Elf" /> +
+
+ + setKeyword(e.target.value)} placeholder="Flying" /> +
+
+ + +
+
+ + {rows.length === 0 ? ( +

+ No matching cards owned. Add some in the Add Cards tab, or loosen the filters. +

+ ) : ( + groups.map((g) => ( +
+ {g.label && ( +

+ {g.label} {g.cards.length} +

+ )} + {view === "list" ? libTable(g.cards) : libTiles(g.cards)} +
+ )) + )} +
+ ); +} + +function splitCsv(s: string): string[] { + return s + .split(",") + .map((x) => x.trim()) + .filter(Boolean); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3934b8f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..5dc2a3e --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// Vite config tuned for Tauri: fixed port, no clearing the terminal Tauri owns. +export default defineConfig({ + plugins: [react()], + clearScreen: false, + server: { + port: 1420, + strictPort: true, + watch: { + // Don't watch the Rust side; Tauri handles that. + ignored: ["**/src-tauri/**"], + }, + }, +});