From 18ac5adef546dc893e1f37e3350e802ed86c3135 Mon Sep 17 00:00:00 2001 From: funman300 Date: Wed, 29 Apr 2026 00:30:55 +0000 Subject: [PATCH] =?UTF-8?q?feat(engine):=20art=20pass=20=E2=80=94=20PNG=20?= =?UTF-8?q?assets,=20custom=20font,=20and=20keyring=20v4=20upgrade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Art pass (Phase 4): - Generate placeholder PNG assets: face.png, back_0–4.png, bg_0–4.png via solitaire_assetgen gen_art binary (16×16 RGBA, embedded via include_bytes!) - Add FiraMono-Medium font (assets/fonts/main.ttf) embedded at compile time - Add FontPlugin: loads font at startup, exposes FontResource; gracefully falls back to default handle when Assets absent (MinimalPlugins tests) - Wire CardImageSet into card_plugin: face/back PNGs replace solid-colour sprites when available; tests continue using colour fallback via MinimalPlugins - Wire BackgroundImageSet into table_plugin: bg PNGs replace solid-colour background; empty set inserted when Assets absent in tests - Fix hint highlight system (input_plugin): tint sprite.color directly instead of replacing the whole Sprite (which would discard the image handle) - Export FontPlugin, FontResource, CardImageSet from solitaire_engine::lib - Register FontPlugin in solitaire_app before other plugins Dependency upgrades (latest releases): - keyring "2" → keyring "4" + keyring-core "1" (v4 split architecture into separate core library crate) - auth_tokens.rs: Entry::new now returns Result; delete_password → delete_credential; NoDefaultStore error variant handled - solitaire_app: add keyring::use_native_store(true) at startup for Linux Secret Service / macOS Keychain / Windows Credential Store selection ARCHITECTURE.md: fix Edition 2025→2021, update asset pipeline section, add FontPlugin/CardImageSet/BackgroundImageSet to plugin and resource tables, update Section 14 to reflect actual include_bytes!() rendering approach, add Decision Log entries for embedded PNG and font decisions Co-Authored-By: Claude Sonnet 4.6 --- ARCHITECTURE.md | 82 +- Cargo.lock | 2183 ++++++++++++++++++++----- Cargo.toml | 3 +- assets/backgrounds/bg_0.png | Bin 0 -> 451 bytes assets/backgrounds/bg_1.png | Bin 0 -> 218 bytes assets/backgrounds/bg_2.png | Bin 0 -> 247 bytes assets/backgrounds/bg_3.png | Bin 0 -> 559 bytes assets/backgrounds/bg_4.png | Bin 0 -> 675 bytes assets/cards/backs/back_0.png | Bin 0 -> 221 bytes assets/cards/backs/back_1.png | Bin 0 -> 943 bytes assets/cards/backs/back_2.png | Bin 0 -> 344 bytes assets/cards/backs/back_3.png | Bin 0 -> 337 bytes assets/cards/backs/back_4.png | Bin 0 -> 299 bytes assets/cards/faces/face.png | Bin 0 -> 213 bytes assets/fonts/main.ttf | Bin 0 -> 173516 bytes solitaire_app/Cargo.toml | 1 + solitaire_app/src/main.rs | 15 +- solitaire_assetgen/Cargo.toml | 10 +- solitaire_assetgen/src/bin/gen_art.rs | 229 +++ solitaire_data/Cargo.toml | 2 +- solitaire_data/src/auth_tokens.rs | 25 +- solitaire_engine/src/card_plugin.rs | 169 +- solitaire_engine/src/font_plugin.rs | 36 + solitaire_engine/src/hud_plugin.rs | 9 +- solitaire_engine/src/input_plugin.rs | 14 +- solitaire_engine/src/lib.rs | 6 +- solitaire_engine/src/table_plugin.rs | 110 +- 27 files changed, 2405 insertions(+), 489 deletions(-) create mode 100644 assets/backgrounds/bg_0.png create mode 100644 assets/backgrounds/bg_1.png create mode 100644 assets/backgrounds/bg_2.png create mode 100644 assets/backgrounds/bg_3.png create mode 100644 assets/backgrounds/bg_4.png create mode 100644 assets/cards/backs/back_0.png create mode 100644 assets/cards/backs/back_1.png create mode 100644 assets/cards/backs/back_2.png create mode 100644 assets/cards/backs/back_3.png create mode 100644 assets/cards/backs/back_4.png create mode 100644 assets/cards/faces/face.png create mode 100644 assets/fonts/main.ttf create mode 100644 solitaire_assetgen/src/bin/gen_art.rs create mode 100644 solitaire_engine/src/font_plugin.rs diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 23222a6..8691bb9 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -3,7 +3,7 @@ > **Version:** 1.1 > **Language:** Rust (Edition 2021) > **Engine:** Bevy (latest stable) -> **Last Updated:** 2026-04-20 +> **Last Updated:** 2026-04-29 --- @@ -67,18 +67,19 @@ solitaire_quest/ ├── Dockerfile # Multi-stage server build ├── docker-compose.yml # Server + Caddy reverse proxy │ -├── assets/ # Audio embedded at compile time via include_bytes!() -│ ├── cards/ # Reserved for future art pass (currently unused) -│ │ ├── faces/ -│ │ └── backs/ -│ ├── backgrounds/ # Reserved for future art pass (currently unused) -│ ├── fonts/ # Reserved for future art pass (currently unused) +├── assets/ # Assets embedded at compile time via include_bytes!() +│ ├── cards/ +│ │ ├── faces/face.png # placeholder (16×16 cream/ivory) +│ │ └── backs/back_0.png – back_4.png # placeholder patterns +│ ├── backgrounds/bg_0.png – bg_4.png # placeholder textures +│ ├── fonts/main.ttf # FiraMono-Medium (170K, OFL) │ └── audio/ │ ├── card_deal.wav │ ├── card_flip.wav │ ├── card_place.wav │ ├── card_invalid.wav -│ └── win_fanfare.wav +│ ├── win_fanfare.wav +│ └── ambient_loop.wav │ ├── solitaire_core/ # Pure Rust game logic — zero external deps beyond rand/serde ├── solitaire_sync/ # Shared API types — used by client and server @@ -143,6 +144,7 @@ Owns: - All Bevy UI screens (Home, Stats, Achievements, Settings, Profile) - Audio playback systems - Sync status display +- Card, background, and font asset loading (embedded via `include_bytes!()` — no `AssetServer` dependency) ### `solitaire_server` **Dependencies:** `solitaire_sync`, `axum`, `sqlx`, `jsonwebtoken`, `bcrypt`, `tower-governor`, `tracing`, `tokio`, `dotenvy`. @@ -237,6 +239,7 @@ Done |---|---|---| | `CardPlugin` | — | Card entity spawning, sprite management, drag-and-drop | | `TablePlugin` | — | Pile markers, background, layout calculation | +| `FontPlugin` | — | Embeds FiraMono-Medium font at compile time; exposes `FontResource` handle | | `AnimationPlugin` | — | Slide, flip, win cascade, toast animations | | `FeedbackAnimPlugin` | — | Shake, settle, and deal-stagger animations | | `AutoCompletePlugin` | Enter | Executes auto-complete when the HUD badge is lit | @@ -286,6 +289,20 @@ struct StatsResource(StatsSnapshot); struct ProgressResource(PlayerProgress); struct AchievementsResource(Vec); struct SettingsResource(Settings); + +// Pre-loaded card face and back PNG handles +struct CardImageSet { + face: Handle, // shared face image for all face-up cards + backs: [Handle; 5], // indexed by selected_card_back setting +} + +// Project-wide font handle (FiraMono-Medium embedded at compile time) +struct FontResource(Handle); + +// Pre-loaded background PNG handles +struct BackgroundImageSet { + handles: Vec>, // indices 0–4 match selected_background setting +} ``` ### Key Bevy Events @@ -743,7 +760,7 @@ Audio uses `bevy_kira_audio`. All sound files are `.wav`. | `card_place.wav` | Valid card placement | | `card_invalid.wav` | Invalid move attempt | | `win_fanfare.wav` | Game won | -| `ambient_loop` | Looping background music — uses `card_flip.wav` looped at very low volume as a placeholder until a dedicated track is added | +| `ambient_loop.wav` | Looping background music | Volume is controlled by two independent sliders in Settings (`sfx_volume`, `music_volume`), each stored in `Settings` and applied as `bevy_kira_audio` channel volumes. @@ -755,30 +772,49 @@ Audio systems listen for Bevy events and never block the game thread. ### Rendering approach -Cards, backgrounds, and UI are rendered **procedurally** — no image files are used. Cards are Bevy `Sprite` entities (colored rectangles) with `Text` children showing rank and suit symbols. Card back colors and background colors are selected by index from compile-time color tables in `card_plugin.rs` and `table_plugin.rs`. All UI text uses Bevy's built-in default font. +Cards are Bevy `Sprite` entities with `Handle` from `CardImageSet`. Face-up cards use `face.png` (a single shared image). Face-down cards use `backs/back_N.png` indexed by `settings.selected_card_back`. `Text2d` labels are still overlaid for rank and suit symbols. `CardImageSet` is populated at startup from `include_bytes!()` — no `AssetServer`. -This means the `assets/cards/`, `assets/backgrounds/`, and `assets/fonts/` directories are reserved for a future art pass and are currently empty (`.gitkeep` only). +Backgrounds are Bevy `Sprite` entities with `Handle` from `BackgroundImageSet`. `BackgroundImageSet` is populated at startup from `include_bytes!()`. + +The font `FiraMono-Medium` is embedded via `include_bytes!()` at startup by `FontPlugin` and exposed as `FontResource` for use by all UI and text systems. + +The `assets/` directory layout: + +``` +assets/ +├── cards/ +│ ├── faces/face.png # placeholder (16×16 cream/ivory) +│ └── backs/back_0.png – back_4.png # placeholder patterns +├── backgrounds/bg_0.png – bg_4.png # placeholder textures +├── fonts/main.ttf # FiraMono-Medium (170K, OFL) +└── audio/ + ├── card_deal.wav + ├── card_flip.wav + ├── card_place.wav + ├── card_invalid.wav + ├── win_fanfare.wav + └── ambient_loop.wav +``` ### Audio -All five sound effect WAV files are embedded at compile time via `include_bytes!()` in `audio_plugin.rs`. There is no runtime asset loading — the binary is fully self-contained. +All sound effect WAV files are embedded at compile time via `include_bytes!()` in `audio_plugin.rs`. There is no runtime asset loading — the binary is fully self-contained. -| File | Size | +| File | Trigger | |---|---| -| `card_deal.wav` | SFX | -| `card_flip.wav` | SFX | -| `card_place.wav` | SFX | -| `card_invalid.wav` | SFX | -| `win_fanfare.wav` | SFX | - -The ambient music loop reuses `card_flip.wav` at very low volume as a placeholder; a dedicated `ambient_loop.wav` can be dropped into `assets/audio/` and wired into `audio_plugin.rs` when ready. +| `card_deal.wav` | New game deal animation | +| `card_flip.wav` | Card flips face-up | +| `card_place.wav` | Valid card placement | +| `card_invalid.wav` | Invalid move attempt | +| `win_fanfare.wav` | Game won | +| `ambient_loop.wav` | Looping background music | ### Future art pass -When image-based card art is added, the recommended approach is: -- Embed assets via `bevy::asset::embedded_asset!()` macro (keeps the binary self-contained) +The placeholder PNG files can be replaced with real artwork without any code changes — just drop in new PNGs and rebuild. The texture atlas approach described below is still the recommended upgrade path for card faces: - Use a texture atlas (`assets/cards/atlas.png` + layout descriptor) for card faces - Individual PNGs for card backs and backgrounds (5 each) +- All assets remain embedded via `include_bytes!()` to keep the binary self-contained --- @@ -975,3 +1011,5 @@ Using `axum::test` and an in-memory SQLite database: | `SyncProvider` trait, not `SyncBackend` match arms | `SyncPlugin` stays backend-agnostic and testable; new backends can be added without touching the plugin | 2026-04-20 | | Dropped WebDAV backend | Redundant once the self-hosted server exists; removing it reduces surface area and simplifies settings UI | 2026-04-20 | | Dropped GPGS backend | Redundant with the self-hosted server; adds JNI complexity for no user-visible benefit on the target platforms | 2026-04-28 | +| PNG assets embedded via `include_bytes!()` | Using `Image::from_buffer()` in startup systems rather than `AssetServer::load()` keeps the binary self-contained and eliminates runtime file-not-found errors | 2026-04-29 | +| FiraMono-Medium font embedded via `include_bytes!()` | Exposed through `FontResource`; avoids runtime font loading errors on headless systems and ensures consistent text rendering across all platforms | 2026-04-29 | diff --git a/Cargo.lock b/Cargo.lock index a0b198c..a689f84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,6 +81,26 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aegis" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78412fa53e6da95324e8902c3641b3ff32ab45258582ea997eb9169c68ffa219" +dependencies = [ + "cc", + "softaes", +] + [[package]] name = "aes" version = "0.8.4" @@ -92,6 +112,20 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.12" @@ -174,6 +208,24 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "android-native-keyring-store" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c6349ddff23194f8fdce2ea8849380f5a4868c1648965b70e801e104cba9b3" +dependencies = [ + "base64", + "jni 0.21.1", + "keyring-core", + "log", + "ndk-context", + "regex", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", +] + [[package]] name = "android-properties" version = "0.2.2" @@ -195,12 +247,89 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "antithesis_sdk" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18dbd97a5b6c21cc9176891cf715f7f0c273caf3959897f43b9bd1231939e675" +dependencies = [ + "libc", + "libloading", + "linkme", + "once_cell", + "rand 0.8.6", + "rustc_version_runtime", + "serde", + "serde_json", +] + [[package]] name = "anyhow" version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "apple-native-keyring-store" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7be2f067ccd8d4b4d4a66ddafe0f32a5dff31732f32dbff85fefc40929b1f72" +dependencies = [ + "keyring-core", + "log", + "security-framework", +] + [[package]] name = "approx" version = "0.5.1" @@ -210,6 +339,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -245,18 +383,14 @@ checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] -name = "async-broadcast" -version = "0.5.1" +name = "assoc" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" -dependencies = [ - "event-listener 2.5.3", - "futures-core", -] +checksum = "bfdc70193dadb9d7287fa4b633f15f90c876915b31f6af17da307fc59c9859a8" [[package]] name = "async-broadcast" @@ -264,7 +398,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -290,53 +424,21 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.4.1", - "futures-lite 2.6.1", + "fastrand", + "futures-lite", "pin-project-lite", "slab", ] -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - [[package]] name = "async-fs" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" dependencies = [ - "async-lock 3.4.2", + "async-lock", "blocking", - "futures-lite 2.6.1", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.28", - "slab", - "socket2 0.4.10", - "waker-fn", + "futures-lite", ] [[package]] @@ -349,49 +451,41 @@ dependencies = [ "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "parking", - "polling 3.11.0", + "polling", "rustix 1.1.4", "slab", "windows-sys 0.61.2", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-process" -version = "1.8.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", + "async-channel", + "async-io", + "async-lock", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.44", - "windows-sys 0.48.0", + "event-listener", + "futures-lite", + "rustix 1.1.4", ] [[package]] @@ -402,7 +496,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -411,8 +505,8 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" dependencies = [ - "async-io 2.6.0", - "async-lock 3.4.2", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", @@ -440,7 +534,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -660,7 +754,7 @@ checksum = "7cf35516d0e7ac9ec25df533be1bf8cbaa20596a8e65f36838a3f7803a267d6d" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -714,11 +808,11 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29f86fed15972b9fb1a3f7b092cf0390e67131caaedab15a2707c043e3a3c886" dependencies = [ - "async-broadcast 0.7.2", + "async-broadcast", "async-channel", - "async-fs 2.2.0", - "async-io 2.6.0", - "async-lock 3.4.2", + "async-fs", + "async-io", + "async-lock", "atomicow", "bevy_android", "bevy_app", @@ -737,7 +831,7 @@ dependencies = [ "downcast-rs 2.0.2", "either", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "futures-util", "js-sys", "ron", @@ -760,7 +854,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -861,7 +955,7 @@ checksum = "318ee0532c3da93749859d18f89a889c638fbc56aabac4d866583df7b951d103" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -948,7 +1042,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1035,7 +1129,7 @@ checksum = "6960ea308d7e94adcac5c712553ff86614bba6b663511f3f3812f6bec028b51e" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1069,7 +1163,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f37fb52655d0439656ca0a1db027d46926e463c81d893d4b1639668e5d7f1c1" dependencies = [ - "async-lock 3.4.2", + "async-lock", "base64", "bevy_animation", "bevy_app", @@ -1115,7 +1209,7 @@ dependencies = [ "bevy_utils", "bitflags 2.11.1", "bytemuck", - "futures-lite 2.6.1", + "futures-lite", "guillotiere", "half", "image", @@ -1264,7 +1358,7 @@ checksum = "3b147843b81a7ec548876ff97fa7bfdc646ef2567cb465566259237b39664438" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "toml_edit 0.23.10+spec-1.0.0", ] @@ -1475,7 +1569,7 @@ dependencies = [ "indexmap", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "uuid", ] @@ -1538,7 +1632,7 @@ dependencies = [ "bevy_macro_utils", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1661,7 +1755,7 @@ checksum = "73d32f90f9cfcef5a44401db7ce206770daaa1707b0fb95eb7a96a6933f54f1b" dependencies = [ "bevy_macro_utils", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1678,7 +1772,7 @@ dependencies = [ "concurrent-queue", "crossbeam-queue", "derive_more", - "futures-lite 2.6.1", + "futures-lite", "heapless", "pin-project", ] @@ -1891,6 +1985,42 @@ dependencies = [ "winit", ] +[[package]] +name = "bigdecimal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.11.1", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", + "which", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -1906,7 +2036,7 @@ dependencies = [ "regex", "rustc-hash 2.1.2", "shlex", - "syn 2.0.117", + "syn", ] [[package]] @@ -1940,6 +2070,27 @@ dependencies = [ "serde_core", ] +[[package]] +name = "bitpacking" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a7139abd3d9cebf8cd6f920a389cf3dc9576172e32f4563f188cae3c3eb019" +dependencies = [ + "crunchy", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.8.5" @@ -2005,7 +2156,7 @@ dependencies = [ "async-channel", "async-task", "futures-io", - "futures-lite 2.6.1", + "futures-lite", "piper", ] @@ -2019,6 +2170,40 @@ dependencies = [ "cipher", ] +[[package]] +name = "bon" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "branches" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e426eb5cc1900033930ec955317b302e68f19f326cc7bb0c8a86865a826cdf0c" +dependencies = [ + "rustc_version", +] + [[package]] name = "bumpalo" version = "3.20.2" @@ -2042,7 +2227,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2071,7 +2256,7 @@ checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ "bitflags 2.11.1", "log", - "polling 3.11.0", + "polling", "rustix 0.38.44", "slab", "thiserror 1.0.69", @@ -2110,6 +2295,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "census" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" + [[package]] name = "cesu8" version = "1.1.0" @@ -2137,6 +2328,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cfg_block" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18758054972164c3264f7c8386f5fc6da6114cb46b619fd365d4e3b2dc3ae487" + [[package]] name = "chrono" version = "0.4.44" @@ -2172,6 +2369,46 @@ dependencies = [ "libloading", ] +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "cmake" version = "0.1.58" @@ -2192,6 +2429,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "combine" version = "4.6.7" @@ -2308,7 +2551,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", "core-graphics-types 0.1.3", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -2374,7 +2617,7 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6" dependencies = [ - "bindgen", + "bindgen 0.72.1", ] [[package]] @@ -2487,6 +2730,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" +[[package]] +name = "crc32c" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a47af21622d091a8f0fb295b88bc886ac74efcc613efc19f5d0b21de5c89e47" +dependencies = [ + "rustc_version", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -2511,6 +2763,25 @@ 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-queue" version = "0.3.12" @@ -2520,6 +2791,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-skiplist" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df29de440c58ca2cc6e587ec3d22347551a32435fbde9d2bff64e78a9ffa151b" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -2551,9 +2832,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "ctrlc" version = "3.5.2" @@ -2595,7 +2886,41 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", +] + +[[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", +] + +[[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", ] [[package]] @@ -2610,6 +2935,7 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core", + "serde", ] [[package]] @@ -2624,6 +2950,71 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +[[package]] +name = "datasketches" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c286de4e81ea2590afc24d754e0f83810c566f50a1388fa75ebd57928c0d9745" + +[[package]] +name = "db-keystore" +version = "0.4.2-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6d682fd95a70cd0de8dd61a95cbd5fe16c9411cdd335864bcce58552f3df6c4" +dependencies = [ + "anyhow", + "clap", + "futures", + "keyring-core", + "log", + "regex", + "serde", + "serde_json", + "turso", + "uuid", + "zeroize", +] + +[[package]] +name = "dbus" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "dbus-secret-service" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" +dependencies = [ + "aes", + "block-padding", + "cbc", + "dbus", + "fastrand", + "hkdf", + "num", + "once_cell", + "openssl", + "sha2", + "zeroize", +] + +[[package]] +name = "dbus-secret-service-keyring-store" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d8f54da401bb5eb2a4d873ac4b359f4a599df2ca8634bb5b8c045e5ee78757" +dependencies = [ + "dbus-secret-service", + "keyring-core", +] + [[package]] name = "der" version = "0.7.10" @@ -2636,14 +3027,13 @@ dependencies = [ ] [[package]] -name = "derivative" -version = "2.2.0" +name = "deranged" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "powerfmt", + "serde_core", ] [[package]] @@ -2665,7 +3055,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn", "unicode-xid", ] @@ -2728,7 +3118,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2881,7 +3271,7 @@ checksum = "c8bad72d8308f7a382de2391ec978ddd736e0103846b965d7e2a63a75768af30" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2893,6 +3283,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + [[package]] name = "enumflags2" version = "0.7.12" @@ -2911,7 +3307,26 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", +] + +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "env_filter", + "log", ] [[package]] @@ -2961,23 +3376,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - [[package]] name = "event-listener" version = "5.4.1" @@ -2995,7 +3393,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] @@ -3006,14 +3404,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" [[package]] -name = "fastrand" -version = "1.9.0" +name = "fallible-iterator" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fastbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" dependencies = [ - "instant", + "getrandom 0.3.4", + "libm", + "siphasher", ] +[[package]] +name = "fastdivide" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" + [[package]] name = "fastrand" version = "2.4.1" @@ -3137,6 +3549,15 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -3144,7 +3565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -3155,9 +3576,15 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -3183,12 +3610,43 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "fs4" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" +dependencies = [ + "rustix 1.1.4", + "windows-sys 0.59.0", +] + [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.32" @@ -3233,28 +3691,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.4.1", + "fastrand", "futures-core", "futures-io", "parking", @@ -3269,7 +3712,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3296,6 +3739,7 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -3306,6 +3750,36 @@ dependencies = [ "slab", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + +[[package]] +name = "generator" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link 0.2.1", + "windows-result 0.4.1", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -3367,6 +3841,16 @@ dependencies = [ "wasip3", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gilrs" version = "0.11.1" @@ -3473,7 +3957,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3713,12 +4197,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -3775,6 +4253,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "htmlescape" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" + [[package]] name = "http" version = "1.4.0" @@ -3887,7 +4371,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2", "tokio", "tower-service", "tracing", @@ -4005,6 +4489,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +[[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" @@ -4036,7 +4526,7 @@ dependencies = [ "byteorder-lite", "moxcms", "num-traits", - "png", + "png 0.18.1", ] [[package]] @@ -4088,12 +4578,12 @@ dependencies = [ ] [[package]] -name = "instant" -version = "0.1.13" +name = "intrusive-collections" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" dependencies = [ - "cfg-if", + "memoffset", ] [[package]] @@ -4106,14 +4596,14 @@ dependencies = [ ] [[package]] -name = "io-lifetimes" -version = "1.0.11" +name = "io-uring" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +checksum = "4d09b98f7eace8982db770e4408e7470b028ce513ac28fecdc6bf4c30fe92b62" dependencies = [ - "hermit-abi 0.3.9", + "bitflags 2.11.1", + "cfg-if", "libc", - "windows-sys 0.48.0", ] [[package]] @@ -4132,6 +4622,21 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -4199,7 +4704,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.117", + "syn", ] [[package]] @@ -4227,7 +4732,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4275,16 +4780,37 @@ dependencies = [ [[package]] name = "keyring" -version = "2.3.3" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "363387f0019d714aa60cc30ab4fe501a747f4c08fc58f069dd14be971bd495a0" +checksum = "dc17b941cdab9063edc0f241dbe10ba9895a8b3f92aa908fd4b3533fea2ae841" dependencies = [ - "byteorder", - "lazy_static", - "linux-keyutils", - "secret-service", - "security-framework 2.11.1", - "windows-sys 0.52.0", + "android-native-keyring-store", + "apple-native-keyring-store", + "base64", + "clap", + "db-keystore", + "dbus-secret-service-keyring-store", + "keyring-core", + "linux-keyutils-keyring-store", + "rpassword", + "rprompt", + "windows-native-keyring-store", + "zbus-secret-service-keyring-store", +] + +[[package]] +name = "keyring-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e621458ca9c51aa110bd0339d4751a056b9576bf1253aee1aa560dda0fc9d" +dependencies = [ + "chrono", + "dashmap", + "log", + "regex", + "ron", + "serde", + "uuid", ] [[package]] @@ -4339,12 +4865,24 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "levenshtein_automata" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" + [[package]] name = "lewton" version = "0.10.2" @@ -4362,6 +4900,16 @@ 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 = [ + "cc", + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.9" @@ -4378,6 +4926,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libmimalloc-sys" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1eacfa31c33ec25e873c136ba5669f00f9866d0688bea7be4d3f7e43067df6" +dependencies = [ + "cc", +] + [[package]] name = "libredox" version = "0.1.16" @@ -4417,6 +4974,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" +[[package]] +name = "linkme" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83272d46373fb8decca684579ac3e7c8f3d71d4cc3aa693df8759e260ae41cf" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d59e20403c7d08fe62b4376edfe5c7fb2ef1e6b1465379686d0f21c8df444b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "linux-keyutils" version = "0.2.5" @@ -4428,10 +5005,14 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.3.8" +name = "linux-keyutils-keyring-store" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "39fbed79f71dc21eb21d3d07c0e908a3c58ff9a1fdbf5cf44230fb3deb6d994b" +dependencies = [ + "keyring-core", + "linux-keyutils", +] [[package]] name = "linux-raw-sys" @@ -4472,12 +5053,40 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "lz4_flex" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a" + [[package]] name = "mach2" version = "0.4.3" @@ -4530,6 +5139,15 @@ dependencies = [ "digest", ] +[[package]] +name = "measure_time" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51c55d61e72fc3ab704396c5fa16f4c184db37978ae4e94ca8959693a235fc0e" +dependencies = [ + "log", +] + [[package]] name = "memchr" version = "2.8.0" @@ -4545,15 +5163,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -4572,12 +5181,43 @@ dependencies = [ "bitflags 2.11.1", "block", "core-graphics-types 0.2.0", - "foreign-types", + "foreign-types 0.5.0", "log", "objc", "paste", ] +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "cfg-if", + "miette-derive", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mimalloc" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3627c4272df786b9260cabaa46aec1d59c93ede723d4c3ef646c503816b0640" +dependencies = [ + "libmimalloc-sys", +] + [[package]] name = "mime" version = "0.3.17" @@ -4627,6 +5267,12 @@ dependencies = [ "pxfm", ] +[[package]] +name = "murmurhash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" + [[package]] name = "naga" version = "27.0.3" @@ -4724,18 +5370,6 @@ dependencies = [ "jni-sys 0.3.1", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", -] - [[package]] name = "nix" version = "0.30.1" @@ -4855,6 +5489,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + [[package]] name = "num-derive" version = "0.4.2" @@ -4863,7 +5503,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4923,10 +5563,10 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5283,12 +5923,78 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oneshot" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269bca4c2591a28585d6bf10d9ed0332b7d76900a1b02bec41bdc3a2cdcda107" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-src" +version = "300.6.0+3.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -5314,6 +6020,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -5333,6 +6048,21 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "ownedbytes" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fbd56f7631767e61784dc43f8580f403f4475bd4aaa4da003e6295e1bab4a7e" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "p256" version = "0.13.2" @@ -5357,6 +6087,15 @@ dependencies = [ "sha2", ] +[[package]] +name = "pack1" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e7cd9bd638dc2c831519a0caa1c006cab771a92b1303403a8322773c5b72d6" +dependencies = [ + "bytemuck", +] + [[package]] name = "parking" version = "2.2.1" @@ -5443,7 +6182,7 @@ checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5459,7 +6198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.4.1", + "fastrand", "futures-io", ] @@ -5496,6 +6235,19 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[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" @@ -5509,22 +6261,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - [[package]] name = "polling" version = "3.11.0" @@ -5533,12 +6269,24 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", "rustix 1.1.4", "windows-sys 0.61.2", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.13.1" @@ -5563,6 +6311,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pp-rs" version = "0.2.1" @@ -5594,7 +6348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn", ] [[package]] @@ -5606,16 +6360,6 @@ dependencies = [ "elliptic-curve", ] -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5640,6 +6384,29 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +[[package]] +name = "prost" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pxfm" version = "0.1.29" @@ -5683,7 +6450,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.6.3", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -5721,7 +6488,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2", "tracing", "windows-sys 0.60.2", ] @@ -5747,6 +6514,12 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "radsort" version = "0.1.1" @@ -5822,6 +6595,15 @@ dependencies = [ "rand 0.9.4", ] +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.4", +] + [[package]] name = "range-alloc" version = "0.1.5" @@ -5834,6 +6616,15 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rustversion", +] + [[package]] name = "raw-cpuid" version = "11.6.0" @@ -5849,6 +6640,26 @@ 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 = "read-fonts" version = "0.36.0" @@ -6011,6 +6822,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "roaring" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9" +dependencies = [ + "bytemuck", + "byteorder", +] + [[package]] name = "rodio" version = "0.20.1" @@ -6041,6 +6862,27 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" +[[package]] +name = "rpassword" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3bc4a3dd492cd6974dd3f32d6626ba9f36e5122e3df3b083474b104aebd139b" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.61.2", +] + +[[package]] +name = "rprompt" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69abf524bb9ccb7c071f7231441288d74b48d176cb309eb00e6f77d186c6e035" +dependencies = [ + "rtoolbox", + "windows-sys 0.59.0", +] + [[package]] name = "rsa" version = "0.9.10" @@ -6061,12 +6903,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rtoolbox" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "rtrb" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" +[[package]] +name = "rust-stemmers" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" +dependencies = [ + "serde", + "serde_derive", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -6089,17 +6951,13 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "0.37.28" +name = "rustc_version_runtime" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" +checksum = "2dd18cd2bae1820af0b6ad5e54f4a51d0f3fcc53b05f845675074efcc7af071d" dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "rustc_version", + "semver", ] [[package]] @@ -6152,7 +7010,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.7.0", + "security-framework", ] [[package]] @@ -6180,7 +7038,7 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.7.0", + "security-framework", "security-framework-sys", "webpki-root-certs", "windows-sys 0.61.2", @@ -6284,36 +7142,23 @@ dependencies = [ [[package]] name = "secret-service" -version = "3.1.0" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5204d39df37f06d1944935232fd2dfe05008def7ca599bf28c0800366c8a8f9" +checksum = "9a62d7f86047af0077255a29494136b9aaaf697c76ff70b8e49cded4e2623c14" dependencies = [ "aes", "cbc", "futures-util", "generic-array", + "getrandom 0.2.17", "hkdf", "num", "once_cell", - "rand 0.8.6", "serde", "sha2", "zbus", ] -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.1", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - [[package]] name = "security-framework" version = "3.7.0" @@ -6382,7 +7227,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -6417,7 +7262,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -6443,6 +7288,12 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.9" @@ -6469,6 +7320,26 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shuttle" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab17edba38d63047f46780cf7360acf7467fec2c048928689a5c1dd1c2b4e31" +dependencies = [ + "assoc", + "bitvec", + "cfg-if", + "generator", + "hex", + "owo-colors", + "rand 0.8.6", + "rand_core 0.6.4", + "rand_pcg", + "scoped-tls", + "smallvec", + "tracing", +] + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -6511,6 +7382,30 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simsimd" +version = "6.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fb3bc3cdce07a7d7d4caa4c54f8aa967f6be41690482b54b24100a2253fa70" +dependencies = [ + "cc", +] + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "sketches-ddsketch" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e40b6cf54d988dc1a2223531b969c9a9e30906ad90ef64890c27b4bfbb46ea" +dependencies = [ + "serde", +] + [[package]] name = "skrifa" version = "0.39.0" @@ -6589,16 +7484,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.6.3" @@ -6609,11 +7494,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "softaes" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef461faaeb36c340b6c887167a9054a034f6acfc50a014ead26a02b4356b3de" + [[package]] name = "solitaire_app" version = "0.1.0" dependencies = [ "bevy", + "keyring", "solitaire_data", "solitaire_engine", ] @@ -6621,6 +7513,9 @@ dependencies = [ [[package]] name = "solitaire_assetgen" version = "0.1.0" +dependencies = [ + "png 0.17.16", +] [[package]] name = "solitaire_core" @@ -6639,7 +7534,7 @@ dependencies = [ "async-trait", "chrono", "dirs", - "keyring", + "keyring-core", "reqwest", "serde", "serde_json", @@ -6767,7 +7662,7 @@ dependencies = [ "crc", "crossbeam-queue", "either", - "event-listener 5.4.1", + "event-listener", "futures-core", "futures-intrusive", "futures-io", @@ -6802,7 +7697,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.117", + "syn", ] [[package]] @@ -6825,7 +7720,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.117", + "syn", "tokio", "url", ] @@ -6971,6 +7866,34 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" @@ -6994,6 +7917,12 @@ dependencies = [ "zeno", ] +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + [[package]] name = "symphonia" version = "0.5.5" @@ -7115,17 +8044,6 @@ dependencies = [ "symphonia-metadata", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.117" @@ -7154,7 +8072,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7192,13 +8110,167 @@ dependencies = [ "slotmap", ] +[[package]] +name = "tantivy" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edde6a10743fff00a4e1a8c9ef020bf5f3cbad301b7d2d39f2b07f123c4eac07" +dependencies = [ + "aho-corasick", + "arc-swap", + "base64", + "bitpacking", + "bon", + "byteorder", + "census", + "crc32fast", + "crossbeam-channel", + "datasketches", + "downcast-rs 2.0.2", + "fastdivide", + "fnv", + "fs4", + "htmlescape", + "itertools 0.14.0", + "levenshtein_automata", + "log", + "lru", + "lz4_flex", + "measure_time", + "memmap2", + "once_cell", + "oneshot", + "rayon", + "regex", + "rust-stemmers", + "rustc-hash 2.1.2", + "serde", + "serde_json", + "sketches-ddsketch", + "smallvec", + "tantivy-bitpacker", + "tantivy-columnar", + "tantivy-common", + "tantivy-fst", + "tantivy-query-grammar", + "tantivy-stacker", + "tantivy-tokenizer-api", + "tempfile", + "thiserror 2.0.18", + "time", + "typetag", + "uuid", + "winapi", +] + +[[package]] +name = "tantivy-bitpacker" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fed3d674429bcd2de5d0a6d1aa5495fed8afd9c5ecce993019caf7615f53fa4" +dependencies = [ + "bitpacking", +] + +[[package]] +name = "tantivy-columnar" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57166f5bcfd478f370ab8445afb4678dce44801fa5ce5c451aaf8595583c5dc" +dependencies = [ + "downcast-rs 2.0.2", + "fastdivide", + "itertools 0.14.0", + "serde", + "tantivy-bitpacker", + "tantivy-common", + "tantivy-sstable", + "tantivy-stacker", +] + +[[package]] +name = "tantivy-common" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf10915aa75da3c3b0d58b58853d2e889efbaf32d4982a4c3715dde6bba23e5" +dependencies = [ + "async-trait", + "byteorder", + "ownedbytes", + "serde", + "time", +] + +[[package]] +name = "tantivy-fst" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18" +dependencies = [ + "byteorder", + "regex-syntax", + "utf8-ranges", +] + +[[package]] +name = "tantivy-query-grammar" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfadb8526b6da90704feb293b0701a6aae62ea14983143344be2dc5ce30f1d82" +dependencies = [ + "fnv", + "nom", + "ordered-float 5.3.0", + "serde", + "serde_json", +] + +[[package]] +name = "tantivy-sstable" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2cfc3ac5164cbadc28965ffb145a8f47582a60ae5897859ad8d4316596c606" +dependencies = [ + "futures-util", + "itertools 0.14.0", + "tantivy-bitpacker", + "tantivy-common", + "tantivy-fst", + "zstd", +] + +[[package]] +name = "tantivy-stacker" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbb051742da9d53ca9e8fff43a9b10e319338b24e2c0e15d0372df19ffeb951" +dependencies = [ + "murmurhash32", + "tantivy-common", +] + +[[package]] +name = "tantivy-tokenizer-api" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac258c2c6390673f2685813afeeafcb8c4e0ee7de8dd3fc46838dcc37263f98" +dependencies = [ + "serde", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand", "getrandom 0.4.2", "once_cell", "rustix 1.1.4", @@ -7240,7 +8312,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7251,7 +8323,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7263,6 +8335,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -7325,7 +8428,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -7338,7 +8441,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7375,12 +8478,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" - [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -7399,17 +8496,6 @@ 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", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.23.10+spec-1.0.0" @@ -7462,7 +8548,7 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "socket2 0.6.3", + "socket2", "sync_wrapper", "tokio", "tokio-stream", @@ -7550,6 +8636,19 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" +dependencies = [ + "crossbeam-channel", + "symlink", + "thiserror 2.0.18", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.31" @@ -7558,7 +8657,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7647,11 +8746,200 @@ dependencies = [ "core_maths", ] +[[package]] +name = "turso" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c331e6a1f17f7864b71b2f3c009e9c2c2624bc549ff2025831fcb3be6fa2062f" +dependencies = [ + "mimalloc", + "thiserror 2.0.18", + "tracing", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sync_sdk_kit", +] + +[[package]] +name = "turso_core" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4803a83babd2a8869f1c62468d32d4bc3300b9e4287e08a7d91cc5deea2bbf13" +dependencies = [ + "aegis", + "aes", + "aes-gcm", + "antithesis_sdk", + "arc-swap", + "bigdecimal", + "bitflags 2.11.1", + "branches", + "bumpalo", + "bytemuck", + "cfg_aliases", + "cfg_block", + "chrono", + "crc32c", + "crossbeam-skiplist", + "either", + "fallible-iterator", + "fastbloom", + "hex", + "intrusive-collections", + "io-uring", + "libc", + "libloading", + "libm", + "loom", + "miette", + "num-bigint", + "num-traits", + "pack1", + "parking_lot", + "pastey", + "polling", + "rand 0.9.4", + "rapidhash", + "regex", + "regex-syntax", + "roaring", + "rustc-hash 2.1.2", + "rustix 1.1.4", + "ryu", + "serde_json", + "shuttle", + "simsimd", + "smallvec", + "strum", + "strum_macros", + "tantivy", + "tempfile", + "thiserror 2.0.18", + "tracing", + "tracing-subscriber", + "turso_ext", + "turso_macros", + "turso_parser", + "twox-hash", + "uncased", + "uuid", + "windows-sys 0.61.2", +] + +[[package]] +name = "turso_ext" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823ee9910bc2f59e6ce9a045b9a11c812fa67e6b8242690ff7ae9e0ac42d1782" +dependencies = [ + "chrono", + "getrandom 0.3.4", + "turso_macros", +] + +[[package]] +name = "turso_macros" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be58b0098c329a7a80d9684c896bab5c8e1bbf4e94ec0a45d5ce89b12e41a751" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "turso_parser" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab02213f1e7115b7a657c886bf369bf84d4cffb7ca2ba219941b1d3bd60bd92" +dependencies = [ + "bitflags 2.11.1", + "memchr", + "miette", + "strum", + "strum_macros", + "thiserror 2.0.18", + "turso_macros", +] + +[[package]] +name = "turso_sdk_kit" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b1563ab83a79ed936935e1f3d1ebb8775c16036fd58c3d99acdd4e777a6f61" +dependencies = [ + "bindgen 0.69.5", + "env_logger", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit_macros", +] + +[[package]] +name = "turso_sdk_kit_macros" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482301b24cf8d81e87bb83c6f4efc1efdc935a0489052c0d00e2f5b4e432c10a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "turso_sync_engine" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f187919622f8ee861637bd5ce4d779b86becf18e7446f901c28d2a49399d2b7f" +dependencies = [ + "base64", + "bytes", + "genawaiter", + "http", + "libc", + "prost", + "roaring", + "serde", + "serde_json", + "thiserror 2.0.18", + "tracing", + "turso_core", + "turso_parser", + "uuid", +] + +[[package]] +name = "turso_sync_sdk_kit" +version = "0.6.0-pre.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5f0bf6f8ca34c9148e1d799b3750c9154627a40c357761250e1930c7212128b" +dependencies = [ + "bindgen 0.69.5", + "env_logger", + "genawaiter", + "parking_lot", + "tracing", + "tracing-appender", + "tracing-subscriber", + "turso_core", + "turso_sdk_kit", + "turso_sdk_kit_macros", + "turso_sync_engine", +] + [[package]] name = "twox-hash" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" +dependencies = [ + "rand 0.9.4", +] [[package]] name = "typeid" @@ -7665,6 +8953,30 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "typetag" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2212c8a9b9bcfca32024de14998494cf9a5dfa59ea1b829de98bac374b86bf" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typewit" version = "1.15.2" @@ -7677,11 +8989,20 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "windows-sys 0.61.2", ] +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -7739,6 +9060,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -7757,12 +9088,24 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8-ranges" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" + [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.23.1" @@ -7772,6 +9115,7 @@ dependencies = [ "getrandom 0.4.2", "js-sys", "serde_core", + "sha1_smol", "wasm-bindgen", ] @@ -7789,7 +9133,7 @@ checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -7810,12 +9154,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -7907,7 +9245,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-shared", ] @@ -8227,7 +9565,7 @@ dependencies = [ "ndk-sys 0.6.0+11769913", "objc", "once_cell", - "ordered-float", + "ordered-float 4.6.0", "parking_lot", "portable-atomic", "portable-atomic-util", @@ -8259,6 +9597,18 @@ dependencies = [ "web-sys", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "whoami" version = "1.6.1" @@ -8442,7 +9792,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -8453,7 +9803,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -8464,7 +9814,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -8475,7 +9825,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -8490,6 +9840,19 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-native-keyring-store" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5fd986f648459dd29aa252ed3a5ad11a60c0b1251bf81625fb03a86c69d274e" +dependencies = [ + "byteorder", + "keyring-core", + "regex", + "windows-sys 0.61.2", + "zeroize", +] + [[package]] name = "windows-numerics" version = "0.2.0" @@ -8941,15 +10304,6 @@ dependencies = [ "xkbcommon-dl", ] -[[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" @@ -9004,7 +10358,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn 2.0.117", + "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -9020,7 +10374,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -9068,6 +10422,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -9106,16 +10469,6 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xkbcommon-dl" version = "0.4.2" @@ -9166,73 +10519,79 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] [[package]] name = "zbus" -version = "3.15.2" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" dependencies = [ - "async-broadcast 0.5.1", + "async-broadcast", "async-executor", - "async-fs 1.6.0", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io", + "async-lock", "async-process", "async-recursion", "async-task", "async-trait", "blocking", - "byteorder", - "derivative", "enumflags2", - "event-listener 2.5.3", + "event-listener", "futures-core", - "futures-sink", - "futures-util", + "futures-lite", "hex", - "nix 0.26.4", - "once_cell", + "libc", "ordered-stream", - "rand 0.8.6", + "rustix 1.1.4", "serde", "serde_repr", - "sha1", - "static_assertions", "tracing", "uds_windows", - "winapi", - "xdg-home", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.2", "zbus_macros", "zbus_names", "zvariant", ] [[package]] -name = "zbus_macros" -version = "3.15.2" +name = "zbus-secret-service-keyring-store" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +checksum = "4ccede190ba363386a24e8021c7f3848393976609ec9f5d1f8c6c09ef37075b4" dependencies = [ - "proc-macro-crate 1.3.1", + "keyring-core", + "secret-service", + "zbus", +] + +[[package]] +name = "zbus_macros" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" +dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", - "regex", - "syn 1.0.109", + "syn", + "zbus_names", + "zvariant", "zvariant_utils", ] [[package]] name = "zbus_names" -version = "2.6.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", - "static_assertions", + "winnow 1.0.2", "zvariant", ] @@ -9259,7 +10618,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -9279,7 +10638,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -9288,6 +10647,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zerotrie" @@ -9319,7 +10692,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -9329,39 +10702,69 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] -name = "zvariant" -version = "3.15.2" +name = "zstd" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ - "byteorder", + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "zvariant" +version = "5.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db0ecb8987cf5e92653c57c098f7f0e39a03112edb796f4fe089fb7eaa14ff" +dependencies = [ + "endi", "enumflags2", - "libc", "serde", - "static_assertions", + "winnow 1.0.2", "zvariant_derive", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "3.15.2" +version = "5.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" +checksum = "5b949b639ab1b4bed763aa7481ba0e368af68d8b55532f8ed4bec86a59f2ca98" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "1.0.1" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "serde", + "syn", + "winnow 1.0.2", ] diff --git a/Cargo.toml b/Cargo.toml index d8003ff..762769f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,8 @@ rand = "0.8" async-trait = "0.1" tokio = { version = "1", features = ["full"] } dirs = "6" -keyring = "2" +keyring = "4" +keyring-core = "1" reqwest = { version = "0.13", features = ["json", "rustls", "rustls-native-certs"], default-features = false } solitaire_core = { path = "solitaire_core" } diff --git a/assets/backgrounds/bg_0.png b/assets/backgrounds/bg_0.png new file mode 100644 index 0000000000000000000000000000000000000000..8848cb69997c8e1639363b38bfd4f2b162dbb57e GIT binary patch literal 451 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKVC?dAaSW+oe0zX-flB1GV;}#Q zzw145W_gi!^19sH+n^}3XWlfg_4AC3mEQd<+n!amtlc{Ms{gk9hxhqz?t6M{`<{=I zdl<_9y_w6u>!WUd4cmG7O|_?c^D7@4S1`W&bJJSBv zKR1`#y#Q;qsXy^e*L>g8-t!MwcYIENUvmMX_1}cI(RnqerRN{yE_iUPt$!46 zZ@=?nnz>!G_=9uv_kMPSX#MHDEq^E2cKri$?W>h3--$M{(2U!H?{7T$7%$Dk;CBW>gTe~DWM4f5OVRl literal 0 HcmV?d00001 diff --git a/assets/backgrounds/bg_1.png b/assets/backgrounds/bg_1.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d24dd750f018f051b1b6b24f240d72b9d47c72 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3q4&NLn;{G9$;Re5;^VI$N%N; zdQY5LUgVv;F8B5}D9Y@aH_dDPJR@VJcR$OvXH_k0x6Z!mzs>)1etX<+)jWg$WhcI6 z{(E~Nd;byMXZQJT+MV4mTlIGOj)Q-0E8brIbG!5Uze46S>aA0LoBS_(u{Hh?8&GG? ze4x_3-@NZ6{Chj$?d3nW7hbRHls>axc2n)Fdh0K@^b4DRZhtJ#xJW_eq;Sa6$3Vw2 Nc)I$ztaD0e0su?DYJC6z literal 0 HcmV?d00001 diff --git a/assets/backgrounds/bg_2.png b/assets/backgrounds/bg_2.png new file mode 100644 index 0000000000000000000000000000000000000000..8501a49283bed9b4b1a250d86631f168dae3b5a8 GIT binary patch literal 247 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%``#fD7Ln;{G9$;Re5;^VI$N%N; zdQY5LUgVv;F8B5}D9Y@aH_dDPJR@VJcR$OvXH_k0x6Z!mzin^vv(O61Z|D9ATBpag ziZkTSuS@-$-@eU%|E8bYoo_FXzxn6(!nc>#=lpwn;oHk_v;SoozrSeDuD9M&`)lgi z{j!_h&NW_th}d6&%9|~>*pC6E4}+!wmqwAS-W-iRsU`Im8a(#pMSvmUDf^e^0S{vDv@+uh|ZaBcK&Jl_j7hHbj#1# zG>bogvzZ-02HG4r|88#Wh3I#4D%tdr*x?Jd->}P__Y$rgS!;7T*fE%H0lUja{fpS` x<$pIi(aqwxBL8n-TskI_yTs@7Z0D!%-j3*WV+m+2kvv8?z~g=dh&T2ByAJl zUgod+pnUFA3=UPjzAR{;oxTgZ me<9l3MuP=489+l*p7A#4;f0@fJ`V&YJ_b)$KbLh*2~7Z}mu}$z literal 0 HcmV?d00001 diff --git a/assets/cards/backs/back_0.png b/assets/cards/backs/back_0.png new file mode 100644 index 0000000000000000000000000000000000000000..e49bed3eaa543c939021f4c71874ac00aa1272f9 GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`OFUg1Ln;{G9$;Re5;^VI$N%N; zdQY5LUgVv;F8B5}D9Y@aH_dDPJR@VJcR$OvXH_k0x6Z!mzfJz^e%VcbGyc83pnU() pX&|TSNp#K5pWB_OD)U&L@$@83&1HG}^?~kX@O1TaS?83{1OQ{qZuI~F literal 0 HcmV?d00001 diff --git a/assets/cards/backs/back_1.png b/assets/cards/backs/back_1.png new file mode 100644 index 0000000000000000000000000000000000000000..db1e94bf780467227eff8d3ac53c5cc5a612714a GIT binary patch literal 943 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU@r4?aSW+oe0zX-flB1GV;}#Q zzw145W_gi!^19sH+n^}3XWlfg_4AC3mEQd<+n!amtlc{Ms{b~5-VdVY_kL6~^zZ(B z?l?ou(Oz>qxd)Hm?fGoT5MTJXx1asT4(sR6KsnnDYA~7E!x*=d9(KaT@gN zlXRZJf#Y{8pC>cydvr`%p8rE(`MsYK4eNJ&Ja(M9=FZ%5JGKYfd3!#7WRNd^KDVFo z!u0zE&Gz_B#p1lw-xslnGU9Ze!#4T9r1&0e&|>}l581Fe`bU>^evJ(le=^kmGM_V3N#Ie9Ms zu8F_j)}EgGrshKLo&5LF?^2#O=RKKu;T!wCgnw^QsR_C7QB>7c-!D?PmM>BMe*5<{ z>zh9pNLQ+D`P*ndquv^oO1X`qYUllX+wYt_$A0(3-*5Y#`jy$eaGS@OTmOLf*?m4# z>gG2TRqwWc*Dc?K;`nr@+wv97Kes!hP>zopr03~a* A5&!@I literal 0 HcmV?d00001 diff --git a/assets/cards/backs/back_3.png b/assets/cards/backs/back_3.png new file mode 100644 index 0000000000000000000000000000000000000000..0532928239de1f3f928721fb4805385a3f78ae62 GIT binary patch literal 337 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV3hE5aSW+oe0zX-flB1GV;}#Q zzw145W_gi!^19sH+n^}3XWlfg_4AC3mEQd<+n!amtlc{Ms{i|5Si};Ls>o0oC_aE3JU|;bqxbnJj{0Z&`u!Q&n_VaaG->X4FpBuqK z&mV(?&cBU4U!!=Qb?@ny`UdBpf`s%>?4I}2Nsa+5#Q&i=-F|BQNs!PyPF$+aALpO< zH)P&zuqtk_%izopr04*DCr~m)} literal 0 HcmV?d00001 diff --git a/assets/fonts/main.ttf b/assets/fonts/main.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1e95ced4c44be8648f1555ab8c135a6799f8c5a5 GIT binary patch literal 173516 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

>(@<@>0tc& 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 z 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

*N&HECA^t4B6n_zy#b3o&;&0-L_`CR8{6kz7 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 H_|N|V7~3XI literal 0 HcmV?d00001 diff --git a/solitaire_app/Cargo.toml b/solitaire_app/Cargo.toml index a442a4b..5e1fb49 100644 --- a/solitaire_app/Cargo.toml +++ b/solitaire_app/Cargo.toml @@ -12,3 +12,4 @@ path = "src/main.rs" bevy = { workspace = true } solitaire_engine = { workspace = true } solitaire_data = { workspace = true } +keyring = { workspace = true } diff --git a/solitaire_app/src/main.rs b/solitaire_app/src/main.rs index fb2d4c9..992c934 100644 --- a/solitaire_app/src/main.rs +++ b/solitaire_app/src/main.rs @@ -3,12 +3,24 @@ use solitaire_data::{load_settings_from, provider_for_backend, settings_file_pat use solitaire_engine::{ AchievementPlugin, AnimationPlugin, AudioPlugin, AutoCompletePlugin, CardAnimationPlugin, CardPlugin, ChallengePlugin, CursorPlugin, DailyChallengePlugin, FeedbackAnimPlugin, - GamePlugin, HelpPlugin, HomePlugin, HudPlugin, InputPlugin, LeaderboardPlugin, + FontPlugin, GamePlugin, HelpPlugin, HomePlugin, HudPlugin, InputPlugin, LeaderboardPlugin, OnboardingPlugin, PausePlugin, ProfilePlugin, ProgressPlugin, SelectionPlugin, SettingsPlugin, StatsPlugin, SyncPlugin, TablePlugin, TimeAttackPlugin, WeeklyGoalsPlugin, WinSummaryPlugin, }; fn main() { + // Initialise the platform keyring store before any token operations. + // On Linux this uses the Secret Service (GNOME Keyring / KWallet); on + // macOS it uses the Keychain; on Windows it uses the Credential store. + // If the platform has no OS keyring (e.g. a headless CI box), keyring + // operations will fail gracefully with TokenError::KeychainUnavailable. + if let Err(e) = keyring::use_native_store(true) { + eprintln!( + "warn: could not initialise OS keyring ({e}); \ + server sync login will be unavailable" + ); + } + // Load settings before building the app so we can construct the right // sync provider. Falls back to defaults if no settings file exists yet. let settings: Settings = settings_file_path() @@ -32,6 +44,7 @@ fn main() { ..default() }), ) + .add_plugins(FontPlugin) .add_plugins(GamePlugin) .add_plugins(TablePlugin) .add_plugins(CardPlugin) diff --git a/solitaire_assetgen/Cargo.toml b/solitaire_assetgen/Cargo.toml index 1151841..2b69fd0 100644 --- a/solitaire_assetgen/Cargo.toml +++ b/solitaire_assetgen/Cargo.toml @@ -5,9 +5,17 @@ license.workspace = true edition.workspace = true publish = false -# Dev-only utility: synthesizes placeholder SFX WAV files into `assets/audio/`. +# Dev-only utility: synthesizes placeholder SFX WAV files into `assets/audio/` +# and placeholder PNG images into `assets/cards/` and `assets/backgrounds/`. # Not depended on by any other workspace crate. +[dependencies] +png = "0.17" + [[bin]] name = "gen_sfx" path = "src/bin/gen_sfx.rs" + +[[bin]] +name = "gen_art" +path = "src/bin/gen_art.rs" diff --git a/solitaire_assetgen/src/bin/gen_art.rs b/solitaire_assetgen/src/bin/gen_art.rs new file mode 100644 index 0000000..551e1a1 --- /dev/null +++ b/solitaire_assetgen/src/bin/gen_art.rs @@ -0,0 +1,229 @@ +//! Generates placeholder PNG assets for card faces, card backs, and table +//! backgrounds. All images are 16×16 pixels — Bevy's Sprite scales them via +//! `custom_size`, so small files keep the repository lightweight. +//! +//! Run with: +//! ``` +//! cargo run -p solitaire_assetgen --bin gen_art +//! ``` + +use std::fs::File; +use std::io::BufWriter; +use std::path::Path; + +// --------------------------------------------------------------------------- +// PNG helper +// --------------------------------------------------------------------------- + +/// Write a 16×16 RGBA image to `path`. `pixels` is a flat `[R,G,B,A, ...]` +/// byte array with exactly 16 * 16 * 4 = 1024 bytes. +fn save_png(path: &Path, pixels: &[u8; 1024]) { + let file = File::create(path) + .unwrap_or_else(|e| panic!("cannot create {}: {e}", path.display())); + let mut w = BufWriter::new(file); + let mut encoder = png::Encoder::new(&mut w, 16, 16); + encoder.set_color(png::ColorType::Rgba); + encoder.set_depth(png::BitDepth::Eight); + let mut writer = encoder + .write_header() + .unwrap_or_else(|e| panic!("png header error for {}: {e}", path.display())); + writer + .write_image_data(pixels) + .unwrap_or_else(|e| panic!("png data error for {}: {e}", path.display())); +} + +/// Build a flat 16×16 RGBA pixel array using a per-pixel closure. +fn make_image [u8; 4]>(f: F) -> [u8; 1024] { + let mut pixels = [0u8; 1024]; + for y in 0u32..16 { + for x in 0u32..16 { + let rgba = f(x, y); + let i = ((y * 16 + x) * 4) as usize; + pixels[i..i + 4].copy_from_slice(&rgba); + } + } + pixels +} + +// --------------------------------------------------------------------------- +// Card face +// --------------------------------------------------------------------------- + +/// Cream/ivory solid fill — represents a blank card face. +fn make_face() -> [u8; 1024] { + make_image(|_, _| [0xF8, 0xF8, 0xF0, 0xFF]) +} + +// --------------------------------------------------------------------------- +// Card backs (match the colours used in card_plugin.rs `card_back_colour()`) +// --------------------------------------------------------------------------- + +/// back_0 — blue base with semi-transparent white horizontal stripes every 4 px. +fn make_back_0() -> [u8; 1024] { + make_image(|_, y| { + if y % 4 < 2 { + [0xFF, 0xFF, 0xFF, 40] + } else { + [0x26, 0x4D, 0x8C, 0xFF] + } + }) +} + +/// back_1 — red base with semi-transparent white diagonal stripes. +fn make_back_1() -> [u8; 1024] { + make_image(|x, y| { + if (x + y) % 4 < 2 { + [0xFF, 0xFF, 0xFF, 40] + } else { + [0x8C, 0x1A, 0x1A, 0xFF] + } + }) +} + +/// back_2 — green base with white dots at every 4-px grid intersection. +fn make_back_2() -> [u8; 1024] { + make_image(|x, y| { + if x % 4 == 0 && y % 4 == 0 { + [0xFF, 0xFF, 0xFF, 0xFF] + } else { + [0x0D, 0x66, 0x1A, 0xFF] + } + }) +} + +/// back_3 — purple base with a white diamond centred at (8, 8). +fn make_back_3() -> [u8; 1024] { + make_image(|x, y| { + let dx = (x as i32 - 8).unsigned_abs(); + let dy = (y as i32 - 8).unsigned_abs(); + if dx + dy <= 4 { + [0xFF, 0xFF, 0xFF, 0xFF] + } else { + [0x59, 0x14, 0x85, 0xFF] + } + }) +} + +/// back_4 — teal base with a 1-px white border. +fn make_back_4() -> [u8; 1024] { + make_image(|x, y| { + if x == 0 || x == 15 || y == 0 || y == 15 { + [0xFF, 0xFF, 0xFF, 0xFF] + } else { + [0x0D, 0x66, 0x6B, 0xFF] + } + }) +} + +// --------------------------------------------------------------------------- +// Backgrounds +// --------------------------------------------------------------------------- + +/// bg_0 — dark green felt with very faint lighter grid lines every 8 px. +fn make_bg_0() -> [u8; 1024] { + make_image(|x, y| { + if x % 8 == 0 || y % 8 == 0 { + [0xFF, 0xFF, 0xFF, 30] + } else { + [0x1A, 0x4D, 0x1A, 0xFF] + } + }) +} + +/// bg_1 — dark wood brown with faint horizontal grain lines every 2 px. +fn make_bg_1() -> [u8; 1024] { + make_image(|_, y| { + if y % 2 == 0 { + [0xFF, 0xFF, 0xFF, 20] + } else { + [0x40, 0x2D, 0x1A, 0xFF] + } + }) +} + +/// bg_2 — navy with faint star/dot pattern (offset rows) every 8 px. +fn make_bg_2() -> [u8; 1024] { + make_image(|x, y| { + let row_offset: u32 = if (y / 4) % 2 == 0 { 0 } else { 4 }; + if (x + row_offset) % 8 == 0 && y % 8 == 0 { + [0xFF, 0xFF, 0xFF, 0xFF] + } else { + [0x0D, 0x14, 0x38, 0xFF] + } + }) +} + +/// bg_3 — burgundy with a faint diamond-grid pattern. +fn make_bg_3() -> [u8; 1024] { + make_image(|x, y| { + if (x + y) % 8 == 0 { + [0xFF, 0xFF, 0xFF, 30] + } else { + [0x4D, 0x0D, 0x14, 0xFF] + } + }) +} + +/// bg_4 — charcoal with faint pixel noise (alternating pixels every 3 columns). +fn make_bg_4() -> [u8; 1024] { + make_image(|x, y| { + if (x + y) % 2 == 0 && x % 3 == 0 { + [0xFF, 0xFF, 0xFF, 20] + } else { + [0x1F, 0x1F, 0x24, 0xFF] + } + }) +} + +// --------------------------------------------------------------------------- +// Entry point +// --------------------------------------------------------------------------- + +fn workspace_root() -> std::path::PathBuf { + let crate_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + crate_dir.parent().unwrap().to_path_buf() +} + +fn main() { + let root = workspace_root(); + + // Ensure output directories exist. + std::fs::create_dir_all(root.join("assets/cards/faces")).unwrap(); + std::fs::create_dir_all(root.join("assets/cards/backs")).unwrap(); + std::fs::create_dir_all(root.join("assets/backgrounds")).unwrap(); + + // Card face. + let path = root.join("assets/cards/faces/face.png"); + save_png(&path, &make_face()); + println!("wrote {}", path.display()); + + // Card backs. + let backs = [ + make_back_0(), + make_back_1(), + make_back_2(), + make_back_3(), + make_back_4(), + ]; + for (i, pixels) in backs.iter().enumerate() { + let path = root.join(format!("assets/cards/backs/back_{i}.png")); + save_png(&path, pixels); + println!("wrote {}", path.display()); + } + + // Backgrounds. + let bgs = [ + make_bg_0(), + make_bg_1(), + make_bg_2(), + make_bg_3(), + make_bg_4(), + ]; + for (i, pixels) in bgs.iter().enumerate() { + let path = root.join(format!("assets/backgrounds/bg_{i}.png")); + save_png(&path, pixels); + println!("wrote {}", path.display()); + } + + println!("gen_art: all placeholder PNG assets generated successfully."); +} diff --git a/solitaire_data/Cargo.toml b/solitaire_data/Cargo.toml index de1d173..98eb690 100644 --- a/solitaire_data/Cargo.toml +++ b/solitaire_data/Cargo.toml @@ -13,6 +13,6 @@ chrono = { workspace = true } thiserror = { workspace = true } async-trait = { workspace = true } dirs = { workspace = true } -keyring = { workspace = true } +keyring-core = { workspace = true } reqwest = { workspace = true } tokio = { workspace = true } diff --git a/solitaire_data/src/auth_tokens.rs b/solitaire_data/src/auth_tokens.rs index fbfaee6..653f22a 100644 --- a/solitaire_data/src/auth_tokens.rs +++ b/solitaire_data/src/auth_tokens.rs @@ -8,9 +8,15 @@ //! [`TokenError::KeychainUnavailable`] — callers should fall back to prompting //! the user to log in again. //! +//! Before calling any function in this module the application must initialise +//! the default keyring store exactly once at startup by calling +//! `keyring::use_native_store` (e.g. in `solitaire_app::main` before building +//! the Bevy `App`). If no default store is set, all operations in this module +//! will return [`TokenError::KeychainUnavailable`]. +//! //! # Note: no unit tests — requires live OS keychain. -use keyring::Entry; +use keyring_core::Entry; use thiserror::Error; /// Errors that can occur when reading or writing tokens in the OS keychain. @@ -30,12 +36,13 @@ pub enum TokenError { /// Service name used to namespace all keychain entries for this application. const SERVICE: &str = "solitaire_quest_server"; -/// Map a `keyring::Error` to the appropriate `TokenError`. -fn map_keyring_err(err: keyring::Error, username: &str) -> TokenError { +/// Map a `keyring_core::Error` to the appropriate `TokenError`. +fn map_keyring_err(err: keyring_core::Error, username: &str) -> TokenError { let msg = err.to_string(); match err { - keyring::Error::NoStorageAccess(_) => TokenError::KeychainUnavailable(msg), - keyring::Error::NoEntry => TokenError::NotFound(username.to_string()), + keyring_core::Error::NoStorageAccess(_) => TokenError::KeychainUnavailable(msg), + keyring_core::Error::NoDefaultStore => TokenError::KeychainUnavailable(msg), + keyring_core::Error::NoEntry => TokenError::NotFound(username.to_string()), _ => TokenError::Keyring(msg), } } @@ -88,17 +95,17 @@ pub fn load_refresh_token(username: &str) -> Result { pub fn delete_tokens(username: &str) -> Result<(), TokenError> { match Entry::new(SERVICE, &format!("{username}_access")) .map_err(|e| map_keyring_err(e, username))? - .delete_password() + .delete_credential() { - Ok(()) | Err(keyring::Error::NoEntry) => {} + Ok(()) | Err(keyring_core::Error::NoEntry) => {} Err(e) => return Err(map_keyring_err(e, username)), } match Entry::new(SERVICE, &format!("{username}_refresh")) .map_err(|e| map_keyring_err(e, username))? - .delete_password() + .delete_credential() { - Ok(()) | Err(keyring::Error::NoEntry) => {} + Ok(()) | Err(keyring_core::Error::NoEntry) => {} Err(e) => return Err(map_keyring_err(e, username)), } diff --git a/solitaire_engine/src/card_plugin.rs b/solitaire_engine/src/card_plugin.rs index 1e02376..6be71d9 100644 --- a/solitaire_engine/src/card_plugin.rs +++ b/solitaire_engine/src/card_plugin.rs @@ -47,6 +47,19 @@ pub const CARD_FACE_COLOUR: Color = Color::srgb(0.98, 0.98, 0.95); pub const RED_SUIT_COLOUR: Color = Color::srgb(0.78, 0.12, 0.15); pub const BLACK_SUIT_COLOUR: Color = Color::srgb(0.08, 0.08, 0.08); +/// Pre-loaded [`Handle`]s for card face and back PNG textures. +/// +/// Loaded once at startup by [`load_card_images`]. When this resource is +/// present, card sprites use the PNG artwork; otherwise they fall back to +/// solid-colour sprites (used in tests with `MinimalPlugins`). +#[derive(Resource)] +pub struct CardImageSet { + /// Shared face image used for all face-up cards. + pub face: Handle, + /// One handle per unlockable card-back design (indices 0–4). + pub backs: [Handle; 5], +} + /// Alternative face tint for red-suit cards in color-blind mode — a subtle /// blue wash that distinguishes them from black-suit cards without colour alone. const CARD_FACE_COLOUR_RED_CBM: Color = Color::srgba(0.85, 0.92, 1.0, 1.0); @@ -160,6 +173,7 @@ impl Plugin for CardPlugin { .add_message::() .add_message::() .add_message::() + .add_systems(Startup, load_card_images) .add_systems(PostStartup, (sync_cards_startup, update_stock_empty_indicator_startup)) .add_systems( Update, @@ -180,6 +194,81 @@ impl Plugin for CardPlugin { } } +/// Loads card face and back PNGs at startup and inserts [`CardImageSet`]. +/// +/// The PNGs are embedded at compile time via `include_bytes!()`. Missing +/// files are compile errors, not runtime panics. Under `MinimalPlugins` +/// (tests) this system is still registered but `Assets` is unavailable, +/// so it does nothing and the plugin falls back to solid-colour sprites. +fn load_card_images(images: Option>>, mut commands: Commands) { + let Some(mut images) = images else { + // Assets is absent (e.g. MinimalPlugins in tests) — skip so + // tests can still run. The plugin falls back to solid-colour sprites. + return; + }; + use bevy::asset::RenderAssetUsages; + use bevy::image::{CompressedImageFormats, ImageSampler, ImageType}; + + let load = |bytes: &[u8]| { + Image::from_buffer( + bytes, + ImageType::Extension("png"), + CompressedImageFormats::NONE, + true, + ImageSampler::default(), + RenderAssetUsages::RENDER_WORLD, + ) + .expect("valid card PNG") + }; + + let face = images.add(load(include_bytes!("../../assets/cards/faces/face.png"))); + let backs = [ + images.add(load(include_bytes!("../../assets/cards/backs/back_0.png"))), + images.add(load(include_bytes!("../../assets/cards/backs/back_1.png"))), + images.add(load(include_bytes!("../../assets/cards/backs/back_2.png"))), + images.add(load(include_bytes!("../../assets/cards/backs/back_3.png"))), + images.add(load(include_bytes!("../../assets/cards/backs/back_4.png"))), + ]; + commands.insert_resource(CardImageSet { face, backs }); +} + +/// Builds the [`Sprite`] for a card, using PNG artwork when [`CardImageSet`] is +/// available and falling back to a solid-colour sprite in tests. +fn card_sprite( + card: &Card, + card_size: Vec2, + back_colour: Color, + color_blind: bool, + card_images: Option<&CardImageSet>, + selected_back: usize, +) -> Sprite { + if let Some(set) = card_images { + let image = if card.face_up { + set.face.clone() + } else { + let idx = selected_back.min(set.backs.len() - 1); + set.backs[idx].clone() + }; + Sprite { + image, + color: Color::WHITE, + custom_size: Some(card_size), + ..default() + } + } else { + let body_colour = if card.face_up { + face_colour(card, color_blind) + } else { + back_colour + }; + Sprite { + color: body_colour, + custom_size: Some(card_size), + ..default() + } + } +} + /// When card-back selection changes in Settings, re-render all cards so the /// new back colour is applied immediately (without waiting for a state change). fn resync_cards_on_settings_change( @@ -201,14 +290,14 @@ fn sync_cards_startup( slide_dur: Option>, settings: Option>, entities: Query<(Entity, &CardEntity, &Transform)>, + card_images: Option>, ) { if let Some(layout) = layout { let slide_secs = slide_dur.map_or(0.15, |d| d.slide_secs); - let back_colour = settings - .as_ref() - .map_or_else(|| card_back_colour(0), |s| card_back_colour(s.0.selected_card_back)); + let selected_back = settings.as_ref().map_or(0, |s| s.0.selected_card_back); + let back_colour = card_back_colour(selected_back); let color_blind = settings.as_ref().is_some_and(|s| s.0.color_blind_mode); - sync_cards(commands, &game.0, &layout.0, slide_secs, back_colour, color_blind, &entities); + sync_cards(commands, &game.0, &layout.0, slide_secs, back_colour, color_blind, &entities, card_images.as_deref(), selected_back); } } @@ -220,17 +309,17 @@ fn sync_cards_on_change( slide_dur: Option>, settings: Option>, entities: Query<(Entity, &CardEntity, &Transform)>, + card_images: Option>, ) { if events.read().next().is_none() { return; } if let Some(layout) = layout { let slide_secs = slide_dur.map_or(0.15, |d| d.slide_secs); - let back_colour = settings - .as_ref() - .map_or_else(|| card_back_colour(0), |s| card_back_colour(s.0.selected_card_back)); + let selected_back = settings.as_ref().map_or(0, |s| s.0.selected_card_back); + let back_colour = card_back_colour(selected_back); let color_blind = settings.as_ref().is_some_and(|s| s.0.color_blind_mode); - sync_cards(commands, &game.0, &layout.0, slide_secs, back_colour, color_blind, &entities); + sync_cards(commands, &game.0, &layout.0, slide_secs, back_colour, color_blind, &entities, card_images.as_deref(), selected_back); } } @@ -242,6 +331,8 @@ fn sync_cards( back_colour: Color, color_blind: bool, entities: &Query<(Entity, &CardEntity, &Transform)>, + card_images: Option<&CardImageSet>, + selected_back: usize, ) { let positions = card_positions(game, layout); @@ -266,10 +357,10 @@ fn sync_cards( Some(&(entity, cur)) => { update_card_entity( &mut commands, entity, card, position, z, layout, - slide_secs, back_colour, color_blind, cur, + slide_secs, back_colour, color_blind, cur, card_images, selected_back, ) } - None => spawn_card_entity(&mut commands, card, position, z, layout, back_colour, color_blind), + None => spawn_card_entity(&mut commands, card, position, z, layout, back_colour, color_blind, card_images, selected_back), } } } @@ -358,21 +449,23 @@ fn face_colour(card: &Card, color_blind: bool) -> Color { } } -fn spawn_card_entity(commands: &mut Commands, card: &Card, pos: Vec2, z: f32, layout: &Layout, back_colour: Color, color_blind: bool) { - let body_colour = if card.face_up { - face_colour(card, color_blind) - } else { - back_colour - }; +fn spawn_card_entity( + commands: &mut Commands, + card: &Card, + pos: Vec2, + z: f32, + layout: &Layout, + back_colour: Color, + color_blind: bool, + card_images: Option<&CardImageSet>, + selected_back: usize, +) { + let sprite = card_sprite(card, layout.card_size, back_colour, color_blind, card_images, selected_back); commands .spawn(( CardEntity { card_id: card.id }, - Sprite { - color: body_colour, - custom_size: Some(layout.card_size), - ..default() - }, + sprite, Transform::from_xyz(pos.x, pos.y, z), Visibility::default(), )) @@ -405,21 +498,13 @@ fn update_card_entity( back_colour: Color, color_blind: bool, cur: Vec3, + card_images: Option<&CardImageSet>, + selected_back: usize, ) { - let body_colour = if card.face_up { - face_colour(card, color_blind) - } else { - back_colour - }; - let target = Vec3::new(pos.x, pos.y, z); // Always refresh the visual appearance. - commands.entity(entity).insert(Sprite { - color: body_colour, - custom_size: Some(layout.card_size), - ..default() - }); + commands.entity(entity).insert(card_sprite(card, layout.card_size, back_colour, color_blind, card_images, selected_back)); // Slide to the new position when it differs meaningfully; snap otherwise. if (cur.truncate() - target.truncate()).length() > 1.0 && slide_secs > 0.0 { @@ -653,20 +738,24 @@ fn tick_hint_highlight( mut query: Query<(Entity, &mut HintHighlight, &mut Sprite, &CardEntity)>, game: Res, settings: Option>, + card_images: Option>, ) { let back_idx = settings.as_ref().map_or(0, |s| s.0.selected_card_back); + let use_images = card_images.is_some(); for (entity, mut hint, mut sprite, card_entity) in query.iter_mut() { hint.remaining -= time.delta_secs(); if hint.remaining <= 0.0 { - // Restore normal face-up colour. - let is_face_up = game.0.piles.values() - .flat_map(|p| p.cards.iter()) - .find(|c| c.id == card_entity.card_id) - .is_some_and(|c| c.face_up); - sprite.color = if is_face_up { - CARD_FACE_COLOUR + // Restore the normal sprite colour. + // When image-based rendering is active, WHITE is the neutral tint; + // otherwise restore the solid colour appropriate to the card state. + sprite.color = if use_images { + Color::WHITE } else { - card_back_colour(back_idx) + let is_face_up = game.0.piles.values() + .flat_map(|p| p.cards.iter()) + .find(|c| c.id == card_entity.card_id) + .is_some_and(|c| c.face_up); + if is_face_up { CARD_FACE_COLOUR } else { card_back_colour(back_idx) } }; commands .entity(entity) diff --git a/solitaire_engine/src/font_plugin.rs b/solitaire_engine/src/font_plugin.rs new file mode 100644 index 0000000..22e588b --- /dev/null +++ b/solitaire_engine/src/font_plugin.rs @@ -0,0 +1,36 @@ +// Register FontPlugin in solitaire_engine/src/lib.rs before use. + +//! Embeds FiraMono-Medium as the project font and exposes it via [`FontResource`]. + +use bevy::prelude::*; + +/// Holds the project-wide [`Handle`] loaded at startup. +#[derive(Resource)] +pub struct FontResource(pub Handle); + +/// Loads FiraMono-Medium at startup and inserts [`FontResource`]. +pub struct FontPlugin; + +impl Plugin for FontPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, load_font); + } +} + +fn load_font(fonts: Option>>, mut commands: Commands) { + let Some(mut fonts) = fonts else { + // Assets absent (e.g. MinimalPlugins in tests) — insert default. + commands.insert_resource(FontResource(Handle::default())); + return; + }; + let bytes: &'static [u8] = include_bytes!("../../assets/fonts/main.ttf"); + match Font::try_from_bytes(bytes.to_vec()) { + Ok(font) => { + commands.insert_resource(FontResource(fonts.add(font))); + } + Err(e) => { + warn!("failed to load main.ttf: {e}; falling back to Bevy default font"); + commands.insert_resource(FontResource(Handle::default())); + } + } +} diff --git a/solitaire_engine/src/hud_plugin.rs b/solitaire_engine/src/hud_plugin.rs index 4d0d54a..5996026 100644 --- a/solitaire_engine/src/hud_plugin.rs +++ b/solitaire_engine/src/hud_plugin.rs @@ -14,6 +14,7 @@ use solitaire_core::pile::PileType; use crate::auto_complete_plugin::AutoCompleteState; use crate::daily_challenge_plugin::DailyChallengeResource; use crate::events::InfoToastEvent; +use crate::font_plugin::FontResource; use crate::game_plugin::GameMutation; use crate::resources::GameStateResource; use crate::selection_plugin::SelectionState; @@ -98,9 +99,13 @@ impl Plugin for HudPlugin { } } -fn spawn_hud(mut commands: Commands) { +fn spawn_hud(font_res: Option>, mut commands: Commands) { let white = TextColor(Color::srgba(1.0, 1.0, 1.0, 0.80)); - let font = TextFont { font_size: 18.0, ..default() }; + let font = TextFont { + font: font_res.as_ref().map(|f| f.0.clone()).unwrap_or_default(), + font_size: 18.0, + ..default() + }; commands .spawn(( Node { diff --git a/solitaire_engine/src/input_plugin.rs b/solitaire_engine/src/input_plugin.rs index 390fe26..fa156b3 100644 --- a/solitaire_engine/src/input_plugin.rs +++ b/solitaire_engine/src/input_plugin.rs @@ -252,7 +252,7 @@ fn handle_keyboard_hint( mut confirm: ResMut, mut hint_cycle: ResMut, mut commands: Commands, - card_entities: Query<(Entity, &CardEntity, &Sprite)>, + mut card_entities: Query<(Entity, &CardEntity, &mut Sprite)>, mut info_toast: MessageWriter, mut hint_visual: MessageWriter, ) { @@ -308,16 +308,14 @@ fn handle_keyboard_hint( .and_then(|p| p.cards.last().filter(|c| c.face_up)) .map(|c| c.id); if let Some(card_id) = top_card_id { - for (entity, card_entity, _sprite) in card_entities.iter() { + for (entity, card_entity, mut sprite) in card_entities.iter_mut() { if card_entity.card_id == card_id { + // Tint the card gold without replacing the Sprite (which would + // discard the image handle set by CardImageSet). + sprite.color = Color::srgba(1.0, 1.0, 0.4, 1.0); commands.entity(entity) .insert(HintHighlight { remaining: 2.0 }) - .insert(HintHighlightTimer(2.0)) - .insert(Sprite { - color: Color::srgba(1.0, 1.0, 0.4, 1.0), - custom_size: Some(layout_res.0.card_size), - ..default() - }); + .insert(HintHighlightTimer(2.0)); break; } } diff --git a/solitaire_engine/src/lib.rs b/solitaire_engine/src/lib.rs index db39bf9..79859eb 100644 --- a/solitaire_engine/src/lib.rs +++ b/solitaire_engine/src/lib.rs @@ -6,6 +6,7 @@ pub mod animation_plugin; pub mod auto_complete_plugin; pub mod audio_plugin; pub mod card_plugin; +pub mod font_plugin; pub mod feedback_anim_plugin; pub mod challenge_plugin; pub mod cursor_plugin; @@ -59,9 +60,10 @@ pub use feedback_anim_plugin::{ pub use auto_complete_plugin::AutoCompletePlugin; pub use audio_plugin::{AudioPlugin, AudioState, SoundLibrary}; pub use card_plugin::{ - CardEntity, CardLabel, CardPlugin, HintHighlight, HintHighlightTimer, RightClickHighlight, - RightClickHighlightTimer, + CardEntity, CardImageSet, CardLabel, CardPlugin, HintHighlight, HintHighlightTimer, + RightClickHighlight, RightClickHighlightTimer, }; +pub use font_plugin::{FontPlugin, FontResource}; pub use cursor_plugin::CursorPlugin; pub use events::{ AchievementUnlockedEvent, CardFaceRevealedEvent, CardFlippedEvent, DrawRequestEvent, diff --git a/solitaire_engine/src/table_plugin.rs b/solitaire_engine/src/table_plugin.rs index 053c845..49ab16b 100644 --- a/solitaire_engine/src/table_plugin.rs +++ b/solitaire_engine/src/table_plugin.rs @@ -11,9 +11,21 @@ use solitaire_core::pile::PileType; use solitaire_data::settings::Theme; use crate::events::HintVisualEvent; -use crate::layout::{compute_layout, Layout, LayoutResource, TABLE_COLOUR}; +use crate::layout::{compute_layout, Layout, LayoutResource}; +#[cfg(test)] +use crate::layout::TABLE_COLOUR; use crate::settings_plugin::{SettingsChangedEvent, SettingsResource}; +/// Holds pre-loaded [`Handle`]s for the 5 selectable table backgrounds. +/// +/// Loaded once at startup by [`load_background_images`]. Index 0 is the +/// default; indices 1–4 are unlockable. +#[derive(Resource)] +pub struct BackgroundImageSet { + /// One handle per background slot (indices 0–4). + pub handles: Vec>, +} + /// Z-depth used for the background — below everything. const Z_BACKGROUND: f32 = -10.0; /// Z-depth used for pile markers — below cards (which start at 0) but above @@ -50,6 +62,7 @@ impl Plugin for TablePlugin { app.add_message::() .add_message::() .add_message::() + .add_systems(Startup, load_background_images.before(setup_table)) .add_systems(Startup, setup_table) .add_systems( Update, @@ -63,7 +76,50 @@ impl Plugin for TablePlugin { } } +/// Loads the 5 background PNG files at startup and stores their +/// [`Handle`]s in [`BackgroundImageSet`]. +/// +/// The PNGs are embedded at compile time via `include_bytes!()`. If a file +/// is missing the build will fail with a clear error rather than a runtime +/// panic. +fn load_background_images(images: Option>>, mut commands: Commands) { + let Some(mut images) = images else { + // Assets is absent (e.g. MinimalPlugins in tests) — insert an + // empty set so setup_table can proceed using a default handle. + commands.insert_resource(BackgroundImageSet { handles: Vec::new() }); + return; + }; + const BG_BYTES: [&[u8]; 5] = [ + include_bytes!("../../assets/backgrounds/bg_0.png"), + include_bytes!("../../assets/backgrounds/bg_1.png"), + include_bytes!("../../assets/backgrounds/bg_2.png"), + include_bytes!("../../assets/backgrounds/bg_3.png"), + include_bytes!("../../assets/backgrounds/bg_4.png"), + ]; + let handles = BG_BYTES + .iter() + .map(|bytes| { + use bevy::image::{CompressedImageFormats, ImageSampler, ImageType}; + let image = Image::from_buffer( + bytes, + ImageType::Extension("png"), + CompressedImageFormats::NONE, + true, + ImageSampler::default(), + bevy::asset::RenderAssetUsages::RENDER_WORLD, + ) + .expect("valid background PNG"); + images.add(image) + }) + .collect(); + commands.insert_resource(BackgroundImageSet { handles }); +} + /// Returns the felt colour for a given theme. +/// +/// Only used in tests — the runtime path now picks a PNG image via +/// [`BackgroundImageSet`] rather than a solid colour. +#[cfg(test)] fn theme_colour(theme: &Theme) -> Color { match theme { Theme::Green => Color::srgb(TABLE_COLOUR[0], TABLE_COLOUR[1], TABLE_COLOUR[2]), @@ -74,6 +130,10 @@ fn theme_colour(theme: &Theme) -> Color { /// Effective table background colour: unlocked background index overrides the /// Theme when `selected_background > 0`. +/// +/// Only used in tests — the runtime path now picks a PNG image via +/// [`BackgroundImageSet`] rather than a solid colour. +#[cfg(test)] fn effective_background_colour(theme: &Theme, selected_background: usize) -> Color { match selected_background { 0 => theme_colour(theme), @@ -93,6 +153,7 @@ fn setup_table( windows: Query<&Window>, existing_camera: Query<(), With>, settings: Option>, + bg_images: Option>, ) { // Only spawn a camera if one does not already exist (e.g. a parent app // may have added one in tests). @@ -107,23 +168,34 @@ fn setup_table( .unwrap_or(Vec2::new(1280.0, 800.0)); let layout = compute_layout(window_size); - let initial_colour = settings + let selected_bg = settings .as_ref() - .map(|s| effective_background_colour(&s.0.theme, s.0.selected_background)) - .unwrap_or_else(|| Color::srgb(TABLE_COLOUR[0], TABLE_COLOUR[1], TABLE_COLOUR[2])); + .map(|s| s.0.selected_background) + .unwrap_or(0); - spawn_background(&mut commands, window_size, initial_colour); + let image_handle = bg_images + .as_ref() + .and_then(|set| set.handles.get(selected_bg).cloned()) + .unwrap_or_default(); + + spawn_background(&mut commands, window_size, image_handle); spawn_pile_markers(&mut commands, &layout); commands.insert_resource(LayoutResource(layout)); } -fn spawn_background(commands: &mut Commands, window_size: Vec2, color: Color) { - // Spawn a felt-coloured rectangle that always covers the window. We give - // it the window size plus headroom so resizing up doesn't expose edges - // before the resize handler runs. +/// Spawns the felt background sprite using a PNG image handle. +/// +/// The sprite covers the window at twice the window size so brief resize gaps +/// are never visible. The image is tinted `Color::WHITE` (no tint) so the PNG +/// pixel data is rendered as-is. +fn spawn_background(commands: &mut Commands, window_size: Vec2, image: Handle) { + // Spawn a sprite covering the window. We give it the window size plus + // headroom so resizing up doesn't expose edges before the resize handler + // runs. commands.spawn(( Sprite { - color, + image, + color: Color::WHITE, custom_size: Some(window_size * 2.0), ..default() }, @@ -132,16 +204,30 @@ fn spawn_background(commands: &mut Commands, window_size: Vec2, color: Color) { )); } +/// Reacts to settings changes by updating the background sprite's image handle. +/// +/// When [`BackgroundImageSet`] is available the selected PNG handle is applied +/// directly (color is kept at `Color::WHITE` so the PNG pixel data shows +/// unmodified). If the resource is not yet ready the sprite is left unchanged. fn apply_theme_on_settings_change( mut events: MessageReader, mut backgrounds: Query<&mut Sprite, With>, + bg_images: Option>, ) { let Some(ev) = events.read().last() else { return; }; - let colour = effective_background_colour(&ev.0.theme, ev.0.selected_background); + let Some(set) = bg_images else { + // BackgroundImageSet not ready yet — leave sprite unchanged. + return; + }; + let selected = ev.0.selected_background; + let Some(handle) = set.handles.get(selected).cloned() else { + return; + }; for mut sprite in &mut backgrounds { - sprite.color = colour; + sprite.image = handle.clone(); + sprite.color = Color::WHITE; } }