diff --git a/.gitignore b/.gitignore index 3736e04..fa83e0e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,3 @@ profile.json bot/models client_web/dist var - -deploy -clients/**/dist diff --git a/Cargo.lock b/Cargo.lock index ebd3764..94e366c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,54 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -11,6 +59,95 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +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 = "any_spawner" version = "0.2.0" @@ -24,20 +161,88 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] -name = "argon2" -version = "0.5.3" +name = "approx" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ - "base64ct", - "blake2", - "cpufeatures 0.2.17", - "password-hash", + "num-traits", +] + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "arimaa_engine_step" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c6726d7896a539a62e157b05fa4b7308ffb7872f2b4a2a592d5adb19837861" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "log", + "regex", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", ] [[package]] @@ -59,7 +264,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -77,6 +282,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atomic_float" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628d228f918ac3b82fe590352cc719d30664a0c13ca3a60266fe02c7132d480a" + [[package]] name = "attribute-derive" version = "0.10.5" @@ -88,7 +299,7 @@ dependencies = [ "manyhow", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -104,7 +315,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -114,17 +325,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "axum" -version = "0.8.9" +name = "av-scenechange" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec 0.7.6", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec 0.7.6", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" +dependencies = [ + "arrayvec 0.7.6", +] + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ "axum-core", "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", - "http 1.4.0", + "http", "http-body", "http-body-util", "hyper", @@ -157,7 +411,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http", "http-body", "http-body-util", "mime", @@ -168,25 +422,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-login" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964ea6eb764a227baa8c3368e45c94d23b6863cc7b880c6c9e341c143c5a5ff7" -dependencies = [ - "axum", - "form_urlencoded", - "serde", - "subtle", - "thiserror 2.0.18", - "tower-cookies", - "tower-layer", - "tower-service", - "tower-sessions", - "tracing", - "urlencoding", -] - [[package]] name = "backbone-lib" version = "0.1.0" @@ -202,6 +437,21 @@ dependencies = [ "web-time", ] +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + [[package]] name = "base64" version = "0.21.7" @@ -221,20 +471,95 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] -name = "bitflags" -version = "2.11.1" +name = "bincode" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] [[package]] -name = "blake2" -version = "0.10.6" +name = "bincode" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" dependencies = [ - "digest 0.10.7", + "serde", + "unty", ] +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn 2.0.114", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitstream-io" +version = "4.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +dependencies = [ + "core2", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -245,19 +570,553 @@ dependencies = [ ] [[package]] -name = "block-buffer" -version = "0.12.0" +name = "board-game" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "647fc8459363368aae04df3d21da37094430c57dd993d09be2792133d5365e3e" dependencies = [ - "hybrid-array", + "arimaa_engine_step", + "cast_trait", + "chess", + "decorum", + "internal-iterator", + "itertools 0.10.5", + "lazy_static", + "nohash-hasher", + "nom 7.1.3", + "num-traits", + "once_cell", + "rand 0.8.5", + "rand_xoshiro", + "rayon", + "static_assertions", ] [[package]] -name = "bumpalo" -version = "3.20.2" +name = "bstr" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata 0.4.14", + "serde", +] + +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "burn" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b78ff10ed98b73e1d477ea6e6e1ec1b9cf9f71a17afc3fea9f4dca482d43dcd4" +dependencies = [ + "burn-autodiff", + "burn-candle", + "burn-collective", + "burn-core", + "burn-cpu", + "burn-cuda", + "burn-ndarray", + "burn-nn", + "burn-optim", + "burn-remote", + "burn-rocm", + "burn-router", + "burn-store", + "burn-tch", + "burn-train", + "burn-wgpu", +] + +[[package]] +name = "burn-autodiff" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f04955e9b4acd5e6a6229f80217dae79742975a97dc2253003f226333ad307" +dependencies = [ + "burn-backend", + "burn-std", + "derive-new", + "hashbrown 0.16.1", + "log", + "num-traits", + "parking_lot", + "portable-atomic", + "spin 0.10.0", + "tracing", +] + +[[package]] +name = "burn-backend" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a724a5d8d5865a1f6b304f629eb19f51489760689501c583b3e1f4209f067357" +dependencies = [ + "burn-std", + "bytemuck", + "cubecl", + "derive-new", + "hashbrown 0.16.1", + "num-traits", + "rand 0.9.2", + "rand_distr", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "burn-candle" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21c752d5008923eb9299783da5edae3242b94afdb956e88d2b37b025244b5071" +dependencies = [ + "burn-backend", + "burn-std", + "candle-core", + "derive-new", +] + +[[package]] +name = "burn-collective" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5b2199984291a0f3828d5ac04039fb864c56c3bdd9ee4172a63e457b668e4c" +dependencies = [ + "burn-communication", + "burn-std", + "burn-tensor", + "bytes", + "futures", + "log", + "rmp-serde", + "serde", + "tokio", + "tokio-util", +] + +[[package]] +name = "burn-communication" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585526cd672adc35918d8aea9bcb50669067904f36090caaffa9573d22fc7ade" +dependencies = [ + "axum", + "burn-std", + "burn-tensor", + "bytes", + "derive-new", + "futures", + "futures-util", + "log", + "rmp-serde", + "serde", + "serde_bytes", + "tokio", + "tokio-tungstenite", + "tokio-util", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "burn-core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3634c3ba84397bcf2977ce746954d7e0a40e2d862e92362dd694c29e18df62" +dependencies = [ + "ahash", + "bincode 2.0.1", + "burn-dataset", + "burn-derive", + "burn-std", + "burn-tensor", + "data-encoding", + "derive-new", + "flate2", + "half", + "hashbrown 0.16.1", + "log", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rand 0.9.2", + "regex", + "rmp-serde", + "serde", + "serde_json", + "spin 0.10.0", + "uuid", +] + +[[package]] +name = "burn-cpu" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60aa53c4536719f1c91c250d4b4348daca473c44cf0c45b81096785f5510c192" +dependencies = [ + "burn-backend", + "burn-cubecl", + "burn-fusion", + "cubecl", +] + +[[package]] +name = "burn-cubecl" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d6d13aff03fec966da4300459688883f8a1d741dddbf19d1bfc2562656a9a9b" +dependencies = [ + "burn-backend", + "burn-cubecl-fusion", + "burn-fusion", + "burn-ir", + "burn-std", + "cubecl", + "cubek", + "derive-new", + "futures-lite", + "log", + "serde", + "text_placeholder", +] + +[[package]] +name = "burn-cubecl-fusion" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d25b2e9fb805931401f79782aabd92462d65e60bc207035a3e554de8d7cd9f" +dependencies = [ + "burn-backend", + "burn-fusion", + "burn-ir", + "burn-std", + "cubecl", + "cubek", + "derive-new", + "serde", +] + +[[package]] +name = "burn-cuda" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d0c68cf653eb9c27dcbe046bb7b04cc18c6b33afda4c09317c102e6f4ae7cb6" +dependencies = [ + "burn-backend", + "burn-cubecl", + "burn-fusion", + "cubecl", +] + +[[package]] +name = "burn-dataset" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e87741e2ff9015845ed2b41b47f9e82795cf274bf2328a29619a2e6f662495c" +dependencies = [ + "csv", + "derive-new", + "dirs", + "gix-tempfile", + "image", + "r2d2", + "r2d2_sqlite", + "rand 0.9.2", + "rmp-serde", + "rusqlite", + "sanitize-filename", + "serde", + "serde_json", + "serde_rusqlite", + "strum 0.27.2", + "tempfile", + "thiserror 2.0.18", +] + +[[package]] +name = "burn-derive" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "102d7e2f705b0cda2f89dd0e55e9bbfc6184029929d53487beb606c3303b29a5" +dependencies = [ + "derive-new", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "burn-fusion" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea83d7f8574bcc07967291c5bb679ddc0a655c8db0642eca62755e2fffc8047" +dependencies = [ + "burn-backend", + "burn-ir", + "derive-new", + "hashbrown 0.16.1", + "log", + "serde", + "spin 0.10.0", + "tracing", +] + +[[package]] +name = "burn-ir" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd2b1b37a7289bd85438800deaaebde50507336429b80f96a71730794db5bc31" +dependencies = [ + "burn-backend", + "hashbrown 0.16.1", + "serde", +] + +[[package]] +name = "burn-ndarray" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96be578991cecef163e41a73bf985d8d7eb7fb8ef7bececf8d48523c481ecddf" +dependencies = [ + "atomic_float", + "burn-autodiff", + "burn-backend", + "burn-ir", + "burn-std", + "bytemuck", + "const-random", + "itertools 0.14.0", + "libm", + "macerator", + "matrixmultiply", + "ndarray 0.17.2", + "num-traits", + "paste", + "portable-atomic", + "portable-atomic-util", + "rand 0.9.2", + "rayon", + "seq-macro", +] + +[[package]] +name = "burn-nn" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b8c6c14b94e5b1dddd68f8e6d669f20bac8f99fcb2e4f1a480212d1b598133" +dependencies = [ + "burn-core", + "num-traits", +] + +[[package]] +name = "burn-optim" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8c376d835d92ea363c05c6f48ac19bb687b683c7958c310a716ef8d5d77ba3" +dependencies = [ + "burn-core", + "derive-new", + "hashbrown 0.16.1", + "log", + "num-traits", + "serde", +] + +[[package]] +name = "burn-remote" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7238df1d59dcbb6880fe92ca0aa7b02b64d02394815e618da3d7933950bdaf39" +dependencies = [ + "async-channel", + "axum", + "burn-communication", + "burn-ir", + "burn-router", + "burn-std", + "burn-tensor", + "bytes", + "derive-new", + "futures-util", + "log", + "rmp-serde", + "serde", + "serde_bytes", + "tokio", + "tokio-tungstenite", + "tokio-util", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "burn-rl" +version = "0.1.0" +source = "git+https://github.com/yunjhongwu/burn-rl-examples.git#bf76ba61fdc89837cbf259a6f554a1fcec238690" +dependencies = [ + "burn", + "gym-rs", + "rand 0.8.5", + "ringbuffer", + "serde", +] + +[[package]] +name = "burn-rocm" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2abda6ee63bdcb730f1a335349a9ff83f03048130d405b6ecdccd2df3ff23" +dependencies = [ + "burn-backend", + "burn-cubecl", + "burn-fusion", + "cubecl", +] + +[[package]] +name = "burn-router" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "823ccb88484736a2861d53dc7f67db375ef050b0446bb02dd7cb8783ac6b69a2" +dependencies = [ + "burn-backend", + "burn-ir", + "burn-std", + "hashbrown 0.16.1", + "log", + "spin 0.10.0", +] + +[[package]] +name = "burn-std" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a9ed8e34a4a49d3754586f306075d6b55a5e08343ac75c06f47e7d9f825271" +dependencies = [ + "bytemuck", + "bytes", + "cubecl", + "cubecl-common", + "half", + "num-traits", + "serde", +] + +[[package]] +name = "burn-store" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be80a7b084a19901dc1d0a2e9b77e226c5c575879fe66de891c67062db41a6d" +dependencies = [ + "burn-core", + "burn-nn", + "burn-tensor", + "byteorder", + "bytes", + "half", + "hashbrown 0.16.1", + "memmap2", + "regex", + "safetensors 0.7.0", + "textdistance", +] + +[[package]] +name = "burn-tch" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061cd3df7b4126949561185454b9b67623f6885657361cad2a07a2340b3b3108" +dependencies = [ + "burn-backend", + "cc", + "libc", + "log", + "tch", + "torch-sys", +] + +[[package]] +name = "burn-tensor" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3720e52e00ed0155ced4f8681d0e8a362e699cee36494ec5b97ad44fcc5194c0" +dependencies = [ + "burn-backend", + "burn-std", + "colored", + "derive-new", + "num-traits", + "serde", +] + +[[package]] +name = "burn-train" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3128c7571992c382a5ad057c72654c1048ea4dcf138d1f394313047e69803a" +dependencies = [ + "async-channel", + "burn-core", + "burn-ndarray", + "burn-optim", + "derive-new", + "log", + "nvml-wrapper", + "ratatui", + "rstest", + "serde", + "sysinfo 0.37.2", + "systemstat", + "thiserror 2.0.18", + "tracing-appender", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "burn-wgpu" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df78d62afc9b9fbb8ee4e49b72006485bb64f778a790e185a2d919479bcfc008" +dependencies = [ + "burn-backend", + "burn-cubecl", + "burn-fusion", + "cubecl", +] + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] [[package]] name = "byteorder" @@ -265,11 +1124,52 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "bytesize" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "c_vec" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8" [[package]] name = "calendrical_calculations" @@ -287,6 +1187,44 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +[[package]] +name = "candle-core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c15b675b80d994b2eadb20a4bbe434eabeb454eac3ee5e2b4cf6f147ee9be091" +dependencies = [ + "byteorder", + "float8 0.6.1", + "gemm", + "half", + "libm", + "memmap2", + "num-traits", + "num_cpus", + "rand 0.9.2", + "rand_distr", + "rayon", + "safetensors 0.7.0", + "thiserror 2.0.18", + "yoke 0.8.1", + "zip 7.4.0", +] + +[[package]] +name = "caseless" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6fd507454086c8edfd769ca6ada439193cdb209c7681712ef6275cccbfe5d8" +dependencies = [ + "unicode-normalization", +] + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + [[package]] name = "cast" version = "0.3.0" @@ -294,15 +1232,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] -name = "cc" -version = "1.2.61" +name = "cast_trait" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "c4f8d981c476baadf74cd52897866a1d279d3e14e2d5e2d9af045210e0ae6128" + +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -310,21 +1274,147 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "chacha20" -version = "0.10.0" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.1", + "cipher", + "cpufeatures", ] [[package]] -name = "cmov" -version = "0.5.3" +name = "chacha20poly1305" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + +[[package]] +name = "chess" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed299b171ec34f372945ad6726f7bc1d2afd5f59fb8380f64f48e2bab2f0ec8" +dependencies = [ + "arrayvec 0.5.2", + "failure", + "nodrop", + "rand 0.7.3", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" + +[[package]] +name = "client_web" +version = "0.1.0" +dependencies = [ + "backbone-lib", + "futures", + "getrandom 0.3.4", + "gloo-storage", + "leptos", + "leptos_i18n", + "rand 0.9.2", + "serde", + "serde_json", + "trictrac-store", + "wasm-bindgen-futures", +] + +[[package]] +name = "cmake" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +dependencies = [ + "cc", +] [[package]] name = "cobs" @@ -346,12 +1436,83 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width 0.2.0", +] + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width 0.2.0", +] + [[package]] name = "collection_literals" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2550f75b8cfac212855f6b1885455df8eaee8fe8e246b647d69146142e016084" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "comrak" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fefab951771fc3beeed0773ce66a4f7b706273fc6c4c95b08dd1615744abcf5" +dependencies = [ + "caseless", + "entities", + "memchr", + "slug", + "typed-arena", + "unicode_categories", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -363,31 +1524,56 @@ dependencies = [ [[package]] name = "config" -version = "0.15.22" +version = "0.15.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68cfe19cd7d23ffde002c24ffa5cda73931913ef394d5eaaa32037dc940c0c" +checksum = "b30fa8254caad766fc03cb0ccae691e14bf3bd72bfff27f72802ce729551b3d6" dependencies = [ "convert_case 0.6.0", "pathdiff", "serde_core", - "toml 1.1.2+spec-1.1.0", - "winnow 1.0.2", + "toml 0.9.11+spec-1.1.0", + "winnow", ] [[package]] -name = "const-oid" -version = "0.10.2" +name = "confy" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +checksum = "f29222b549d4e3ded127989d523da9e928918d0d0d7f7c1690b439d0d538bae9" +dependencies = [ + "directories", + "serde", + "thiserror 2.0.18", + "toml 0.8.23", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] [[package]] name = "const_format" -version = "0.2.36" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", - "konst", ] [[package]] @@ -407,6 +1593,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f67855af358fcb20fac58f9d714c94e2b228fe5694c1c9b4ead4a366343eda1b" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constcat" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d3e02915a2cea4d74caa8681e2d44b1c3254bdbf17d11d41d587ff858832c" + [[package]] name = "convert_case" version = "0.6.0" @@ -425,6 +1623,24 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -436,6 +1652,42 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "libc", +] + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "core_maths" version = "0.1.1" @@ -455,12 +1707,48 @@ dependencies = [ ] [[package]] -name = "cpufeatures" -version = "0.3.0" +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "libc", + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", ] [[package]] @@ -469,12 +1757,71 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.10.0", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix 0.38.44", + "signal-hook 0.3.18", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "crypto-common" version = "0.1.7" @@ -482,25 +1829,515 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] [[package]] -name = "crypto-common" -version = "0.2.1" +name = "csv" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ - "hybrid-array", + "csv-core", + "itoa", + "ryu", + "serde_core", ] [[package]] -name = "ctutils" -version = "0.4.2" +name = "csv-core" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ - "cmov", + "memchr", +] + +[[package]] +name = "cubecl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053856efd5436224775b9423d43d86f53d5b1d3af9a6b9983d9a313a0922638f" +dependencies = [ + "cubecl-core", + "cubecl-cpu", + "cubecl-cuda", + "cubecl-hip", + "cubecl-ir", + "cubecl-runtime", + "cubecl-std", + "cubecl-wgpu", + "half", +] + +[[package]] +name = "cubecl-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60bf8aaeb572c8cf2f2ffd07fa9bb1a2cf9336d1aa11ecd4d9a2f2e30c4be706" +dependencies = [ + "backtrace", + "bytemuck", + "bytes", + "cfg-if", + "cfg_aliases", + "derive-new", + "derive_more", + "dirs", + "embassy-futures", + "embassy-time", + "float4", + "float8 0.4.2", + "futures-lite", + "half", + "hashbrown 0.15.5", + "log", + "num-traits", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "rand 0.9.2", + "sanitize-filename", + "serde", + "serde_bytes", + "serde_json", + "spin 0.10.0", + "tracing", + "wasm-bindgen-futures", + "web-time", +] + +[[package]] +name = "cubecl-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98374a31d2b68b55709891169832ccf205408c201c5e023964482441f213d0b9" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "cubecl-common", + "cubecl-ir", + "cubecl-macros", + "cubecl-runtime", + "derive-new", + "derive_more", + "enumset", + "float-ord", + "half", + "hashbrown 0.15.5", + "log", + "num-traits", + "paste", + "serde", + "serde_json", + "tracing", + "variadics_please", +] + +[[package]] +name = "cubecl-cpp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb24d96c1ff84ab4def0a529e384311a15cb771310aaf2b640c312384c3bca23" +dependencies = [ + "bytemuck", + "cubecl-common", + "cubecl-core", + "cubecl-opt", + "cubecl-runtime", + "derive-new", + "half", + "itertools 0.14.0", + "log", +] + +[[package]] +name = "cubecl-cpu" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152588a6e16b6bda5e8216af7a6fad3d7de4697294b6ce0f6acbe3a9029ff674" +dependencies = [ + "bytemuck", + "cubecl-common", + "cubecl-core", + "cubecl-opt", + "cubecl-runtime", + "cubecl-std", + "derive-new", + "half", + "log", + "paste", + "serde", + "sysinfo 0.36.1", + "tracel-llvm", + "tracel-llvm-bundler", +] + +[[package]] +name = "cubecl-cuda" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f74a5750c45090d1fc5ddf6a19fea9a099aa1f6800b78f1167a2d60182d1d96" +dependencies = [ + "bytemuck", + "cubecl-common", + "cubecl-core", + "cubecl-cpp", + "cubecl-runtime", + "cubecl-zspace", + "cudarc", + "derive-new", + "half", + "log", + "serde", + "tracing", +] + +[[package]] +name = "cubecl-hip" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbae9bc7ee6093d0d7a549c05873dff3478f9087b59eb09b223a97d642c849aa" +dependencies = [ + "bytemuck", + "cubecl-common", + "cubecl-core", + "cubecl-cpp", + "cubecl-hip-sys", + "cubecl-runtime", + "cubecl-zspace", + "derive-new", + "half", + "log", + "paste", + "serde", + "tracing", +] + +[[package]] +name = "cubecl-hip-sys" +version = "7.0.5183101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ef087b59445fda47d2177370886351eb923ad1a541086d4919268574cd9558" +dependencies = [ + "libc", + "regex", +] + +[[package]] +name = "cubecl-ir" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361b608ff9f05024c7a7e381852689acd95b6af5af956d68734692b27d5f75ef" +dependencies = [ + "cubecl-common", + "cubecl-macros-internal", + "derive-new", + "derive_more", + "enumset", + "float-ord", + "fnv", + "half", + "hashbrown 0.15.5", + "num-traits", + "portable-atomic", + "serde", + "variadics_please", +] + +[[package]] +name = "cubecl-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c9a872d16207c6a27ed45942fd311a281394dd384b14a21f72131db1556a977" +dependencies = [ + "cubecl-common", + "darling 0.21.3", + "derive-new", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "cubecl-macros-internal" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa3fa0626cdf28b9c49084c2bb51493bfde44378e22d90624aacaafb81da3588" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "cubecl-opt" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdcff25fdcbd82ea4277c30a81e162722859f57c6ae105c0a3c53f8bb91154f6" +dependencies = [ + "cubecl-common", + "cubecl-core", + "cubecl-ir", + "float-ord", + "log", + "num", + "petgraph", + "smallvec", + "stable-vec", + "type-map", +] + +[[package]] +name = "cubecl-runtime" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b02e28997a8d75311afae4d2cea7b593eb125312f845874118a59d78c7a6b34c" +dependencies = [ + "async-channel", + "bytemuck", + "cfg-if", + "cfg_aliases", + "cubecl-common", + "cubecl-ir", + "derive-new", + "derive_more", + "dirs", + "enumset", + "foldhash 0.1.5", + "hashbrown 0.15.5", + "log", + "md5", + "serde", + "serde_json", + "spin 0.10.0", + "thiserror 2.0.18", + "toml 0.9.11+spec-1.1.0", + "tracing", + "variadics_please", + "wasm-bindgen-futures", + "web-time", +] + +[[package]] +name = "cubecl-std" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ff5741c98b7a7a5944b4afb0b67dd7f5e0be41ce7f303b587f8b0d6430b29b" +dependencies = [ + "cubecl-common", + "cubecl-core", + "cubecl-runtime", + "foldhash 0.1.5", + "half", + "num-traits", + "paste", + "serde", + "spin 0.10.0", + "variadics_please", +] + +[[package]] +name = "cubecl-wgpu" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29787364632fc7ec6a11cf3d95187f82f6fcce17d6bb4f0fb0dde580b837631d" +dependencies = [ + "async-channel", + "bytemuck", + "cfg-if", + "cfg_aliases", + "cubecl-common", + "cubecl-core", + "cubecl-ir", + "cubecl-runtime", + "derive-new", + "derive_more", + "half", + "hashbrown 0.15.5", + "log", + "sanitize-filename", + "tracing", + "wgpu", +] + +[[package]] +name = "cubecl-zspace" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0f819071413b19a00b7105497e0f6d2cf3e7e9d65cbb8d4ecf1ddb29c61dc2" + +[[package]] +name = "cubek" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb1cce47db02017925301bedec92ae84628493df3f9761ea7ac42a60c6146f8" +dependencies = [ + "cubecl", + "cubek-attention", + "cubek-convolution", + "cubek-matmul", + "cubek-quant", + "cubek-random", + "cubek-reduce", +] + +[[package]] +name = "cubek-attention" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7278bd122b2428af479f9af05285160613733c33c93b63ab3c6d25cd0460c18b" +dependencies = [ + "bytemuck", + "cubecl", + "cubecl-common", + "cubek-matmul", + "cubek-random", + "half", + "serde", +] + +[[package]] +name = "cubek-convolution" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18eb04bca4ae104d62a56def04b04f3d079c42fe49aac62202c96876f90fa28b" +dependencies = [ + "bytemuck", + "cubecl", + "cubecl-common", + "cubek-matmul", + "derive-new", + "enumset", + "half", + "serde", +] + +[[package]] +name = "cubek-matmul" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28f3b04b113760e97c65a8a4dca9afc220744031eeecd5ad6cd0e3be91ba3a9" +dependencies = [ + "bytemuck", + "cubecl", + "cubecl-common", + "half", + "serde", +] + +[[package]] +name = "cubek-quant" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ec3ae04af324df2d615c2b394e270d58d6f08cb833d67633e2ba794de75916" +dependencies = [ + "cubecl", + "cubecl-common", + "half", + "serde", +] + +[[package]] +name = "cubek-random" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65a34844d8b7f739185c1d24896137dcb73f458830444103b45f678585ad983e" +dependencies = [ + "cubecl", + "cubecl-common", + "half", + "num-traits", + "rand 0.9.2", + "serde", +] + +[[package]] +name = "cubek-reduce" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42397d9ed85bb3084dfb56ed26de75690b5b07caf42a32f4006b57eb23d5b6d6" +dependencies = [ + "cubecl", + "half", + "num-traits", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "cudarc" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aa12038120eb13347a6ae2ffab1d34efe78150125108627fd85044dd4d6ff1e" +dependencies = [ + "libloading", +] + +[[package]] +name = "cxx" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash 0.2.0", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +dependencies = [ + "cc", + "codespan-reporting 0.13.1", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.114", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +dependencies = [ + "clap", + "codespan-reporting 0.13.1", + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] @@ -509,8 +2346,28 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -524,7 +2381,34 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.114", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", ] [[package]] @@ -533,9 +2417,31 @@ version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.114", ] [[package]] @@ -554,43 +2460,17 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] -name = "deadpool" -version = "0.12.3" +name = "decorum" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" +checksum = "281759d3c8a14f5c3f0c49363be56810fcd7f910422f97f2db850c2920fde5cf" dependencies = [ - "deadpool-runtime", - "lazy_static", - "num_cpus", - "tokio", -] - -[[package]] -name = "deadpool-postgres" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d697d376cbfa018c23eb4caab1fd1883dd9c906a8c034e8d9a3cb06a7e0bef9" -dependencies = [ - "async-trait", - "deadpool", - "getrandom 0.2.17", - "tokio", - "tokio-postgres", - "tracing", -] - -[[package]] -name = "deadpool-runtime" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" -dependencies = [ - "tokio", + "num-traits", ] [[package]] @@ -599,20 +2479,41 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0df63c21a4383f94bd5388564829423f35c316aed85dc4f8427aded372c7c0d" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "deranged" -version = "0.5.8" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-new" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] @@ -623,30 +2524,83 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case 0.10.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.114", + "unicode-xid", +] + +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", + "block-buffer", + "crypto-common", "subtle", ] [[package]] -name = "digest" -version = "0.11.3" +name = "directories" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "block-buffer 0.12.0", - "const-oid", - "crypto-common 0.2.1", - "ctutils", + "dirs-sys", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", ] [[package]] @@ -657,7 +2611,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -681,6 +2635,22 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "dyn-stack" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c4713e43e2886ba72b8271aa66c93d722116acf7a75555cce11dcde84388fe8" +dependencies = [ + "bytemuck", + "dyn-stack-macros", +] + +[[package]] +name = "dyn-stack-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d926b4d407d372f141f93bb444696142c29d32962ccbd3531117cf3aa0bfa9" + [[package]] name = "either" version = "1.15.0" @@ -689,29 +2659,69 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "either_of" -version = "0.1.9" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5060e0a4cbf26a87550792688ade88e6b8aec9208613631a7a363bda7bc2d4cd" +checksum = "14f7f86eef3a7e4b9c2107583dbbbe3d9535c4b800796faf1774b82ba22033da" dependencies = [ "paste", "pin-project-lite", ] [[package]] -name = "email-encoding" -version = "0.4.1" +name = "embassy-futures" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6" +checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" + +[[package]] +name = "embassy-time" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8" dependencies = [ - "base64 0.22.1", - "memchr", + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-util", ] [[package]] -name = "email_address" -version = "0.2.9" +name = "embassy-time-driver" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" +dependencies = [ + "document-features", +] + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-async" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" +dependencies = [ + "embedded-hal 1.0.0", +] [[package]] name = "embedded-io" @@ -725,6 +2735,102 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "enumset" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" +dependencies = [ + "enumset_derive", + "serde", +] + +[[package]] +name = "enumset_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -738,7 +2844,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -778,16 +2884,99 @@ dependencies = [ ] [[package]] -name = "fallible-iterator" -version = "0.2.0" +name = "exr" +version = "1.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure 0.12.6", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] [[package]] name = "find-msvc-tools" @@ -807,6 +2996,55 @@ dependencies = [ "writeable 0.5.5", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "float4" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5939bac0ef2ad7c83a53e4fb889c1d81f007b07061d648cd271071984d86f257" + +[[package]] +name = "float8" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4203231de188ebbdfb85c11f3c20ca2b063945710de04e7b59268731e728b462" +dependencies = [ + "half", +] + +[[package]] +name = "float8" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719a903cc23e4a89e87962c2a80fdb45cdaad0983a89bd150bb57b4c8571a7d5" +dependencies = [ + "half", + "num-traits", + "rand 0.9.2", + "rand_distr", +] + [[package]] name = "fnv" version = "1.0.7" @@ -819,6 +3057,39 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -830,9 +3101,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -845,9 +3116,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -855,55 +3126,75 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", ] [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -913,9 +3204,129 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", + "pin-utils", "slab", ] +[[package]] +name = "gemm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa0673db364b12263d103b68337a68fbecc541d6f6b61ba72fe438654709eacb" +dependencies = [ + "dyn-stack", + "gemm-c32", + "gemm-c64", + "gemm-common", + "gemm-f16", + "gemm-f32", + "gemm-f64", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-c32" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086936dbdcb99e37aad81d320f98f670e53c1e55a98bee70573e83f95beb128c" +dependencies = [ + "dyn-stack", + "gemm-common", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-c64" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c8aeeeec425959bda4d9827664029ba1501a90a0d1e6228e48bef741db3a3f" +dependencies = [ + "dyn-stack", + "gemm-common", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-common" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88027625910cc9b1085aaaa1c4bc46bb3a36aad323452b33c25b5e4e7c8e2a3e" +dependencies = [ + "bytemuck", + "dyn-stack", + "half", + "libm", + "num-complex", + "num-traits", + "once_cell", + "paste", + "pulp", + "raw-cpuid", + "rayon", + "seq-macro", + "sysctl", +] + +[[package]] +name = "gemm-f16" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3df7a55202e6cd6739d82ae3399c8e0c7e1402859b30e4cb780e61525d9486e" +dependencies = [ + "dyn-stack", + "gemm-common", + "gemm-f32", + "half", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "rayon", + "seq-macro", +] + +[[package]] +name = "gemm-f32" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0b8c9da1fbec6e3e3ab2ce6bc259ef18eb5f6f0d3e4edf54b75f9fd41a81c" +dependencies = [ + "dyn-stack", + "gemm-common", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + +[[package]] +name = "gemm-f64" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056131e8f2a521bfab322f804ccd652520c79700d81209e9d9275bbdecaadc6a" +dependencies = [ + "dyn-stack", + "gemm-common", + "num-complex", + "num-traits", + "paste", + "raw-cpuid", + "seq-macro", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -935,7 +3346,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -948,46 +3359,122 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi 5.3.0", + "r-efi", "wasip2", "wasm-bindgen", ] [[package]] -name = "getrandom" -version = "0.4.2" +name = "gif" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" dependencies = [ - "cfg-if", + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "gix-features" +version = "0.45.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56aad357ae016449434705033df644ac6253dfcf1281aad3af3af9e907560d1" +dependencies = [ + "gix-trace", + "gix-utils", "libc", - "r-efi 6.0.0", - "rand_core 0.10.1", - "wasip2", - "wasip3", ] [[package]] -name = "gloo-net" -version = "0.5.0" +name = "gix-fs" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" +checksum = "785b9c499e46bc78d7b81c148c21b3fca18655379ee729a856ed19ce50d359ec" dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror 1.0.69", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", + "bstr", + "fastrand", + "gix-features", + "gix-path", + "gix-utils", + "thiserror 2.0.18", ] +[[package]] +name = "gix-path" +version = "0.10.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cb06c3e4f8eed6e24fd915fa93145e28a511f4ea0e768bae16673e05ed3f366" +dependencies = [ + "bstr", + "gix-trace", + "gix-validate", + "thiserror 2.0.18", +] + +[[package]] +name = "gix-tempfile" +version = "20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad89218e74850f42d364ed3877c7291f0474c8533502df91bb877ecc5cb0dd40" +dependencies = [ + "dashmap", + "gix-fs", + "libc", + "parking_lot", + "signal-hook 0.4.3", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e42a4c2583357721ba2d887916e78df504980f22f1182df06997ce197b89504" + +[[package]] +name = "gix-utils" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "befcdbdfb1238d2854591f760a48711bed85e72d80a10e8f2f93f656746ef7c5" +dependencies = [ + "fastrand", + "unicode-normalization", +] + +[[package]] +name = "gix-validate" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1e63a5b516e970a594f870ed4571a8fdcb8a344e7bd407a20db8bd61dbfde4" +dependencies = [ + "bstr", + "thiserror 2.0.18", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "gloo-net" version = "0.6.0" @@ -998,7 +3485,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils", - "http 1.4.0", + "http", "js-sys", "pin-project", "serde", @@ -1049,12 +3536,136 @@ dependencies = [ "web-sys", ] +[[package]] +name = "glow" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.10.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows 0.58.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags 2.10.0", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "guardian" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" +[[package]] +name = "gym-rs" +version = "0.3.1" +source = "git+https://github.com/MathisWellmann/gym-rs.git#5283afaa86a3a7c45c46c882cfad459f02539b62" +dependencies = [ + "derivative", + "derive-new", + "log", + "nalgebra", + "num-traits", + "ordered-float 5.1.0", + "rand 0.8.5", + "rand_pcg 0.3.1", + "sdl2", + "serde", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "bytemuck", + "cfg-if", + "crunchy", + "num-traits", + "rand 0.9.2", + "rand_distr", + "serde", + "zerocopy", +] + [[package]] name = "hash32" version = "0.2.1" @@ -1064,6 +3675,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1076,14 +3696,33 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "allocator-api2", + "equivalent", + "foldhash 0.1.5", + "serde", ] [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", + "serde", + "serde_core", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] [[package]] name = "heapless" @@ -1095,7 +3734,7 @@ dependencies = [ "hash32", "rustc_version", "serde", - "spin", + "spin 0.9.8", "stable_deref_trait", ] @@ -1112,23 +3751,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] -name = "hmac" -version = "0.13.0" +name = "hexf-parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" -dependencies = [ - "digest 0.11.3", -] +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] -name = "hostname" -version = "0.4.2" +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "cfg-if", - "libc", - "windows-link", + "digest", ] [[package]] @@ -1140,17 +3774,6 @@ dependencies = [ "utf8-width", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.4.0" @@ -1168,7 +3791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http", ] [[package]] @@ -1179,17 +3802,11 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http", "http-body", "pin-project-lite", ] -[[package]] -name = "http-range-header" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" - [[package]] name = "httparse" version = "1.10.1" @@ -1203,13 +3820,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "hybrid-array" -version = "0.4.11" +name = "humantime" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5" -dependencies = [ - "typenum", -] +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hydration_context" @@ -1227,22 +3841,42 @@ dependencies = [ [[package]] name = "hyper" -version = "1.9.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "http 1.4.0", + "h2", + "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 1.0.6", ] [[package]] @@ -1251,13 +3885,21 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ + "base64 0.22.1", "bytes", - "http 1.4.0", + "futures-channel", + "futures-util", + "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", + "socket2", "tokio", "tower-service", + "tracing", ] [[package]] @@ -1297,16 +3939,15 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", - "utf8_iter", - "yoke 0.8.2", + "yoke 0.8.1", "zerofrom", - "zerovec 0.11.6", + "zerovec 0.11.5", ] [[package]] @@ -1416,15 +4057,15 @@ checksum = "52b1a7fbdbf3958f1be8354cb59ac73f165b7b7082d447ff2090355c9a069120" [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", - "litemap 0.8.2", - "tinystr 0.8.3", - "writeable 0.6.3", - "zerovec 0.11.6", + "litemap 0.8.1", + "tinystr 0.8.2", + "writeable 0.6.2", + "zerovec 0.11.5", ] [[package]] @@ -1480,16 +4121,16 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "icu_collections 2.2.0", - "icu_normalizer_data 2.2.0", - "icu_properties 2.2.0", - "icu_provider 2.2.0", + "icu_collections 2.1.1", + "icu_normalizer_data 2.1.1", + "icu_properties 2.1.2", + "icu_provider 2.1.1", "smallvec", - "zerovec 0.11.6", + "zerovec 0.11.5", ] [[package]] @@ -1500,9 +4141,9 @@ checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_pattern" @@ -1554,16 +4195,16 @@ dependencies = [ [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "icu_collections 2.2.0", + "icu_collections 2.1.1", "icu_locale_core", - "icu_properties_data 2.2.0", - "icu_provider 2.2.0", - "zerotrie 0.2.4", - "zerovec 0.11.6", + "icu_properties_data 2.1.2", + "icu_provider 2.1.1", + "zerotrie 0.2.3", + "zerovec 0.11.5", ] [[package]] @@ -1574,9 +4215,9 @@ checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -1597,17 +4238,17 @@ dependencies = [ [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "writeable 0.6.3", - "yoke 0.8.2", + "writeable 0.6.2", + "yoke 0.8.1", "zerofrom", - "zerotrie 0.2.4", - "zerovec 0.11.6", + "zerotrie 0.2.3", + "zerovec 0.11.5", ] [[package]] @@ -1618,7 +4259,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -1642,12 +4283,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adcf7b613a268af025bc2a2532b4b9ee294e6051c5c0832d8bff20ac0232e68" -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1667,24 +4302,110 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "icu_normalizer 2.2.0", - "icu_properties 2.2.0", + "icu_normalizer 2.1.1", + "icu_properties 2.1.2", ] [[package]] -name = "indexmap" -version = "2.14.0" +name = "image" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core 0.5.1", + "zune-jpeg 0.5.12", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", + "hashbrown 0.16.1", +] + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instability" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +dependencies = [ + "darling 0.23.0", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "internal-iterator" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969ee3fc68ec2e88eb21434ce4d9b7e1600d1ce92ff974560a6c4a304f5124b9" + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] @@ -1693,6 +4414,57 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + +[[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.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -1704,18 +4476,56 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.18" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jiff" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89a5b5e10d5a9ad6e5d1f4bd58225f655d6fe9767575a5e8ac5a6fe64e04495" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff7a39c8862fc1369215ccf0a8f12dd4598c7f6484704359f0351bd617034dbf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] [[package]] name = "js-sys" -version = "0.3.98" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ - "cfg-if", - "futures-util", "once_cell", "wasm-bindgen", ] @@ -1732,19 +4542,21 @@ dependencies = [ ] [[package]] -name = "konst" -version = "0.2.20" +name = "khronos-egl" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ - "konst_macro_rules", + "libc", + "libloading", + "pkg-config", ] [[package]] -name = "konst_macro_rules" -version = "0.2.19" +name = "khronos_api" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "lazy_static" @@ -1753,10 +4565,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "lebe" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "leptos" @@ -1778,7 +4590,7 @@ dependencies = [ "or_poisoned", "paste", "reactive_graph", - "rustc-hash", + "rustc-hash 2.1.1", "send_wrapper", "serde", "serde_qs", @@ -1856,7 +4668,7 @@ dependencies = [ "quote", "rstml", "serde", - "syn 2.0.117", + "syn 2.0.114", "walkdir", ] @@ -1899,7 +4711,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.117", + "syn 2.0.114", "tinystr 0.7.6", "toml 0.8.23", ] @@ -1919,7 +4731,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "syn 2.0.117", + "syn 2.0.114", "tinystr 0.7.6", "toml 0.8.23", ] @@ -1934,7 +4746,7 @@ dependencies = [ "cfg-if", "convert_case 0.7.1", "html-escape", - "itertools", + "itertools 0.14.0", "leptos_hot_reload", "prettyplease", "proc-macro-error2", @@ -1942,7 +4754,7 @@ dependencies = [ "quote", "rstml", "server_fn_macro", - "syn 2.0.117", + "syn 2.0.114", "uuid", ] @@ -1962,42 +4774,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "leptos_router" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4168ead6a9715daba953aa842795cb2ad81b6e011a15745bd3d1baf86f76de95" -dependencies = [ - "any_spawner", - "either_of", - "futures", - "gloo-net 0.6.0", - "js-sys", - "leptos", - "leptos_router_macro", - "once_cell", - "or_poisoned", - "reactive_graph", - "send_wrapper", - "tachys", - "thiserror 2.0.18", - "url", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "leptos_router_macro" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31197af38d209ffc5d9f89715381c415a1570176f8d23455fbe00d148e79640" -dependencies = [ - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "leptos_server" version = "0.7.8" @@ -2019,35 +4795,51 @@ dependencies = [ ] [[package]] -name = "lettre" -version = "0.11.21" +name = "libc" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dabda5859ee7c06b995b9d1165aa52c39110e079ef609db97178d86aeb051fa7" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ - "async-trait", - "base64 0.22.1", - "email-encoding", - "email_address", - "fastrand", - "futures-io", - "futures-util", - "hostname", - "httpdate", - "idna", - "mime", - "nom", - "percent-encoding", - "quoted_printable", - "socket2", - "tokio", - "url", + "arbitrary", + "cc", ] [[package]] -name = "libc" -version = "0.2.186" +name = "libloading" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "liblzma" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73c36d08cad03a3fbe2c4e7bb3a9e84c57e4ee4135ed0b065cade3d98480c648" +dependencies = [ + "liblzma-sys", + "num_cpus", +] + +[[package]] +name = "liblzma-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2db66f3268487b5033077f266da6777d057949b8f93c8ad82e441df25e6186" +dependencies = [ + "cc", + "libc", + "pkg-config", +] [[package]] name = "libm" @@ -2057,11 +4849,24 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ + "bitflags 2.10.0", "libc", + "redox_syscall 0.7.0", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", ] [[package]] @@ -2070,6 +4875,27 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee" +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" version = "0.7.5" @@ -2078,9 +4904,9 @@ checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" @@ -2095,7 +4921,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ "scopeguard", - "serde", ] [[package]] @@ -2104,6 +4929,67 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "macerator" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b0b2dbe8b22f9e96ba12e29964889010117f92e6bd006010887320ae58e2f0" +dependencies = [ + "bytemuck", + "cfg_aliases", + "half", + "macerator-macros", + "moddef", + "num-traits", + "paste", + "rustc_version", +] + +[[package]] +name = "macerator-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd48b535b9b37a25a2589ab8d4f997886a2c68f59960ce06588525f38dd4944" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "manyhow" version = "0.11.4" @@ -2113,7 +4999,7 @@ dependencies = [ "manyhow-macros", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -2143,21 +5029,59 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] -name = "md-5" -version = "0.11.0" +name = "matrixmultiply" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "num_cpus", + "once_cell", + "rawpointer", + "thread-tree", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" dependencies = [ "cfg-if", - "digest 0.11.3", + "rayon", ] +[[package]] +name = "md5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" + [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memmap2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +dependencies = [ + "libc", + "stable_deref_trait", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "merge" version = "0.1.0" @@ -2180,6 +5104,21 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "metal" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" +dependencies = [ + "bitflags 2.10.0", + "block", + "core-graphics-types", + "foreign-types", + "log", + "objc", + "paste", +] + [[package]] name = "mime" version = "0.3.17" @@ -2187,42 +5126,197 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "mime_guess" -version = "2.0.5" +name = "minimal-lexical" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "minicov" -version = "0.3.8" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "cc", - "walkdir", + "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.2.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys", + "log", + "wasi", + "windows-sys 0.61.2", ] +[[package]] +name = "moddef" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0b3262dc837d2513fe2ef31ff8461352ef932dcca31ba0c0abe33547cf6b9b" + +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "naga" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916cbc7cb27db60be930a4e2da243cf4bc39569195f22fd8ee419cd31d5b662c" +dependencies = [ + "arrayvec 0.7.6", + "bit-set", + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "codespan-reporting 0.12.0", + "half", + "hashbrown 0.15.5", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "thiserror 2.0.18", + "unicode-ident", +] + +[[package]] +name = "nalgebra" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "ndarray" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "ndarray" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", + "rayon", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "next_tuple" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60993920e071b0c9b66f14e2b32740a4e27ffc82854dcd72035887f336a09a28" +[[package]] +name = "no-std-compat" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df270209a7f04d62459240d890ecb792714d5db12c92937823574a09930276b4" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nom" version = "8.0.0" @@ -2232,13 +5326,42 @@ dependencies = [ "memchr", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "ntapi" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", ] [[package]] @@ -2252,10 +5375,31 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.2.1" +name = "num-complex" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "bytemuck", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] [[package]] name = "num-integer" @@ -2266,6 +5410,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-rational" version = "0.4.2" @@ -2297,24 +5452,75 @@ dependencies = [ "libc", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "nvml-wrapper" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5c6c0ef9702176a570f06ad94f3198bc29c524c8b498f1b9346e1b1bdcbb3a" +dependencies = [ + "bitflags 2.10.0", + "libloading", + "nvml-wrapper-sys", + "static_assertions", + "thiserror 1.0.69", + "wrapcenum-derive", +] + +[[package]] +name = "nvml-wrapper-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd23dbe2eb8d8335d2bce0299e0a07d6a63c089243d626ca75b770a962ff49e6" +dependencies = [ + "libloading", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags", + "bitflags 2.10.0", ] [[package]] -name = "objc2-system-configuration" +name = "objc2-io-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" dependencies = [ + "libc", "objc2-core-foundation", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "oco_ref" version = "0.2.1" @@ -2326,10 +5532,22 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.21.4" +name = "octets" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +checksum = "3a74f2cda724d43a0a63140af89836d4e7db6138ef67c9f96d3a0f0150d05000" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -2337,12 +5555,44 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "or_poisoned" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c04f5d74368e4d0dfe06c45c8627c81bd7c317d52762d118fb9b3076f6420fd" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" +dependencies = [ + "num-traits", + "rand 0.8.5", + "serde", +] + [[package]] name = "parking" version = "2.2.1" @@ -2367,16 +5617,16 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] name = "password-hash" -version = "0.5.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" dependencies = [ "base64ct", "rand_core 0.6.4", @@ -2389,12 +5639,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2431,7 +5699,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -2441,53 +5709,132 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2 0.10.9", + "sha2", ] [[package]] -name = "phf" -version = "0.13.1" +name = "petgraph" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "phf_shared", - "serde", + "fixedbitset", + "indexmap", ] [[package]] -name = "phf_shared" -version = "0.13.1" +name = "pico-args" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" -dependencies = [ - "siphasher", -] +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.12" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.12" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "pin-project-lite" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" +dependencies = [ + "bitflags 2.10.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +dependencies = [ + "serde", +] + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] [[package]] name = "postcard" @@ -2502,42 +5849,13 @@ dependencies = [ "serde", ] -[[package]] -name = "postgres-protocol" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56201207dac53e2f38e848e31b4b91616a6bb6e0c7205b77718994a7f49e70fc" -dependencies = [ - "base64 0.22.1", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md-5", - "memchr", - "rand 0.10.1", - "sha2 0.11.0", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dc729a129e682e8d24170cd30ae1aa01b336b096cbb56df6d534ffec133d186" -dependencies = [ - "bytes", - "fallible-iterator", - "postgres-protocol", -] - [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ - "zerovec 0.11.6", + "zerovec 0.11.5", ] [[package]] @@ -2555,6 +5873,22 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "prettyplease" version = "0.2.37" @@ -2562,7 +5896,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn 2.0.114", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -2608,7 +5951,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -2639,11 +5982,30 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", "version_check", "yansi", ] +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn 2.0.114", +] + [[package]] name = "protocol" version = "0.1.0" @@ -2652,16 +6014,175 @@ dependencies = [ ] [[package]] -name = "qrcodegen" -version = "1.8.0" +name = "pulp" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142" +checksum = "2e205bb30d5b916c55e584c22201771bcf2bad9aabd5d4127f38387140c38632" +dependencies = [ + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "paste", + "pulp-wasm-simd-flag", + "raw-cpuid", + "reborrow", + "version_check", +] + +[[package]] +name = "pulp-wasm-simd-flag" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e24eee682d89fb193496edf918a7f407d30175b2e785fe057e4392dfd182e0" + +[[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "pyo3" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash 2.1.1", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -2685,15 +6206,9 @@ dependencies = [ "proc-macro-utils", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] -[[package]] -name = "quoted_printable" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478e0585659a122aa407eb7e3c0e1fa51b1d8a870038bd29f0cf4a8551eea972" - [[package]] name = "r-efi" version = "5.3.0" @@ -2701,41 +6216,69 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "r-efi" -version = "6.0.0" +name = "r2d2" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] [[package]] -name = "rand" -version = "0.8.6" +name = "r2d2_sqlite" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "63417e83dc891797eea3ad379f52a5986da4bca0d6ef28baf4d14034dd111b0c" dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", + "r2d2", + "rusqlite", + "uuid", ] [[package]] name = "rand" -version = "0.9.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg 0.2.1", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", + "serde", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", ] [[package]] -name = "rand" -version = "0.10.1" +name = "rand_chacha" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "chacha20", - "getrandom 0.4.2", - "rand_core 0.10.1", + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -2758,6 +6301,12 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + [[package]] name = "rand_core" version = "0.6.4" @@ -2765,6 +6314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.17", + "serde", ] [[package]] @@ -2777,10 +6327,169 @@ dependencies = [ ] [[package]] -name = "rand_core" -version = "0.10.1" +name = "rand_distr" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" +dependencies = [ + "num-traits", + "rand 0.9.2", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "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 = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + +[[package]] +name = "ratatui" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" +dependencies = [ + "bitflags 2.10.0", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "instability", + "itertools 0.13.0", + "lru", + "paste", + "strum 0.26.3", + "time", + "unicode-segmentation", + "unicode-truncate", + "unicode-width 0.2.0", +] + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec 0.7.6", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.14.0", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.2", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +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 = "reactive_graph" @@ -2795,7 +6504,7 @@ dependencies = [ "hydration_context", "or_poisoned", "pin-project-lite", - "rustc-hash", + "rustc-hash 2.1.1", "send_wrapper", "serde", "slotmap", @@ -2810,12 +6519,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aadc7c19e3a360bf19cd595d2dc8b58ce67b9240b95a103fbc1317a8ff194237" dependencies = [ "guardian", - "itertools", + "itertools 0.14.0", "or_poisoned", "paste", "reactive_graph", "reactive_stores_macro", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -2828,16 +6537,42 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] +[[package]] +name = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.10.0", +] + +[[package]] +name = "redox_syscall" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", ] [[package]] @@ -2874,34 +6609,157 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] -name = "relay-server" -version = "0.1.0" +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "renet" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "751424a2b0a8640bc41ca8b969250491b1a29e9b872dee4c7c56bcc56575b76e" dependencies = [ - "argon2", - "axum", - "axum-login", "bytes", - "deadpool-postgres", + "log", + "octets", + "renetcode", +] + +[[package]] +name = "renetcode" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed06c64c06cd7d80c61d6049c7dea9cc39bbf685b73ef9a6c5d01fada276094f" +dependencies = [ + "chacha20poly1305", + "log", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", "futures-util", - "lettre", - "postcard", - "protocol", - "rand 0.8.6", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", "serde", "serde_json", - "thiserror 1.0.69", - "time", + "serde_urlencoded", + "sync_wrapper", "tokio", - "tokio-postgres", + "tokio-rustls", + "tower", "tower-http", - "tower-sessions", - "tracing", - "tracing-subscriber", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 1.0.6", +] + +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ringbuffer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53" + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.114", + "unicode-ident", ] [[package]] @@ -2914,16 +6772,42 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.117", + "syn 2.0.114", "syn_derive", "thiserror 2.0.18", ] [[package]] -name = "rustc-hash" -version = "2.1.2" +name = "rusqlite" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags 2.10.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -2934,6 +6818,68 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2942,9 +6888,39 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.23" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "safetensors" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93279b86b3de76f820a8854dd06cbc33cfa57a417b19c47f6a25280112fb1df" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "safetensors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675656c1eabb620b921efea4f9199f97fc86e36dd6ffd1fbbe48d0f59a4987f5" +dependencies = [ + "hashbrown 0.16.1", + "serde", + "serde_json", +] [[package]] name = "same-file" @@ -2955,6 +6931,24 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc984f4f9ceb736a7bb755c3e3bd17dc56370af2600c9780dcc48c66453da34d" +dependencies = [ + "regex", +] + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" +dependencies = [ + "parking_lot", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2962,10 +6956,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "semver" -version = "1.0.28" +name = "scratch" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "sdl2" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380" +dependencies = [ + "bitflags 1.3.2", + "c_vec", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3" +dependencies = [ + "cfg-if", + "cmake", + "libc", + "version-compare", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "send_wrapper" @@ -2976,6 +7001,12 @@ dependencies = [ "futures-core", ] +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" + [[package]] name = "serde" version = "1.0.228" @@ -2986,6 +7017,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3003,7 +7044,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3041,6 +7082,16 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "serde_rusqlite" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8bd74f47e124e760475a7e863b5820dcef09cae50782d03d65961f5ca1e6d9" +dependencies = [ + "rusqlite", + "serde_core", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -3052,9 +7103,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -3094,8 +7145,8 @@ dependencies = [ "const_format", "dashmap", "futures", - "gloo-net 0.6.0", - "http 1.4.0", + "gloo-net", + "http", "js-sys", "once_cell", "pin-project-lite", @@ -3124,7 +7175,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", "xxhash-rust", ] @@ -3135,7 +7186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb8b274f568c94226a8045668554aace8142a59b8bca5414ac5a79627c825568" dependencies = [ "server_fn_macro", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3145,8 +7196,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", + "cpufeatures", + "digest", ] [[package]] @@ -3156,19 +7207,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "digest 0.11.3", + "cpufeatures", + "digest", ] [[package]] @@ -3186,6 +7226,37 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b57709da74f9ff9f4a27dce9526eec25ca8407c45a7887243b031a58935fb8e" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook 0.3.18", +] + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -3197,10 +7268,32 @@ dependencies = [ ] [[package]] -name = "siphasher" -version = "1.0.3" +name = "simba" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +checksum = "c99284beb21666094ba2b75bbceda012e610f5479dfcc2d6e2426f53197ffd95" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] [[package]] name = "slab" @@ -3217,6 +7310,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -3225,12 +7328,25 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "spiel_bot" +version = "0.1.0" +dependencies = [ + "anyhow", + "burn", + "criterion", + "rand 0.9.2", + "rand_distr", + "trictrac-bot", + "trictrac-store", ] [[package]] @@ -3242,35 +7358,101 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", + "portable-atomic", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "stable-vec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1dff32a2ce087283bec878419027cebd888760d8760b2941ad0843531dc9ec8" +dependencies = [ + "no-std-compat", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strength_reduce" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "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 0.26.4", +] + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + +[[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 2.0.114", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3290,9 +7472,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -3308,7 +7490,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3316,6 +7498,21 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] [[package]] name = "synstructure" @@ -3325,7 +7522,63 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", +] + +[[package]] +name = "sysctl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" +dependencies = [ + "bitflags 2.10.0", + "byteorder", + "enum-as-inner", + "libc", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "sysinfo" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", +] + +[[package]] +name = "sysinfo" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", +] + +[[package]] +name = "systemstat" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5021f5184d44b26fb184acd689671bbe1e4bbd24bbdaa6bc7ec383fad32d2033" +dependencies = [ + "bytesize", + "lazy_static", + "libc", + "nom 7.1.3", + "time", + "winapi", ] [[package]] @@ -3343,7 +7596,7 @@ dependencies = [ "futures", "html-escape", "indexmap", - "itertools", + "itertools 0.14.0", "js-sys", "linear-map", "next_tuple", @@ -3354,7 +7607,7 @@ dependencies = [ "paste", "reactive_graph", "reactive_stores", - "rustc-hash", + "rustc-hash 2.1.1", "send_wrapper", "slotmap", "throw_error", @@ -3362,6 +7615,79 @@ dependencies = [ "web-sys", ] +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tch" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e09b91610202dc4820c21eb474a42b386ef69f323b1c0902b5472ba7456ebb5" +dependencies = [ + "half", + "lazy_static", + "libc", + "ndarray 0.16.1", + "rand 0.8.5", + "safetensors 0.3.3", + "thiserror 1.0.69", + "torch-sys", + "zip 0.6.6", +] + +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix 1.1.3", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "text_placeholder" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5008f74a09742486ef0047596cf35df2b914e2a8dca5727fcb6ba6842a766b" +dependencies = [ + "hashbrown 0.13.2", + "serde", + "serde_json", +] + +[[package]] +name = "textdistance" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa672c55ab69f787dbc9126cc387dbe57fdd595f585e4524cf89018fa44ab819" + [[package]] name = "thiserror" version = "1.0.69" @@ -3388,7 +7714,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3399,7 +7725,16 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", +] + +[[package]] +name = "thread-tree" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbd370cb847953a25954d9f63e14824a36113f8c72eecf6eccef5dc4b45d630" +dependencies = [ + "crossbeam-channel", ] [[package]] @@ -3420,6 +7755,20 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "tiff" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg 0.4.21", +] + [[package]] name = "time" version = "0.3.47" @@ -3428,7 +7777,9 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde_core", "time-core", @@ -3451,6 +7802,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -3463,19 +7823,29 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", - "zerovec 0.11.6", + "zerovec 0.11.5", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", ] [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -3488,68 +7858,52 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.2" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", "mio", - "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "tracing", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] -name = "tokio-postgres" -version = "0.7.17" +name = "tokio-rustls" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd8df5ef180f6364759a6f00f7aadda4fbbac86cdee37480826a6ff9f3574ce" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures-channel", - "futures-util", - "log", - "parking_lot", - "percent-encoding", - "phf", - "pin-project-lite", - "postgres-protocol", - "postgres-types", - "rand 0.10.1", - "socket2", + "rustls", "tokio", - "tokio-util", - "whoami", ] [[package]] name = "tokio-tungstenite" -version = "0.29.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.29.0", + "tungstenite 0.28.0", ] [[package]] @@ -3574,20 +7928,22 @@ dependencies = [ "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ + "indexmap", "serde_core", - "serde_spanned 1.1.1", - "toml_datetime 1.1.1+spec-1.1.0", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "toml_writer", + "winnow", ] [[package]] @@ -3601,9 +7957,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] @@ -3619,16 +7975,28 @@ dependencies = [ "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow 0.7.15", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ - "winnow 1.0.2", + "winnow", ] [[package]] @@ -3637,6 +8005,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "torch-sys" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef40c585e342df95b66a1fa7c923188623999c2b657227befb481dfb03a6a42" +dependencies = [ + "anyhow", + "cc", + "libc", + "serde", + "serde_json", + "ureq", + "zip 0.6.6", +] + [[package]] name = "tower" version = "0.5.3" @@ -3653,43 +8042,20 @@ dependencies = [ "tracing", ] -[[package]] -name = "tower-cookies" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36" -dependencies = [ - "axum-core", - "cookie", - "futures-util", - "http 1.4.0", - "parking_lot", - "pin-project-lite", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-http" -version = "0.6.10" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags", + "bitflags 2.10.0", "bytes", - "futures-core", "futures-util", - "http 1.4.0", + "http", "http-body", - "http-body-util", - "http-range-header", - "httpdate", - "mime", - "mime_guess", - "percent-encoding", + "iri-string", "pin-project-lite", - "tokio", - "tokio-util", + "tower", "tower-layer", "tower-service", ] @@ -3707,54 +8073,82 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] -name = "tower-sessions" -version = "0.14.0" +name = "tracel-llvm" +version = "20.1.4-7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a05911f23e8fae446005fe9b7b97e66d95b6db589dc1c4d59f6a2d4d4927d3" +checksum = "982535db9eb1a30ac0f2c50239a0eec3e5cf50993a88e92b04747bd2f4d365b2" dependencies = [ - "async-trait", - "http 1.4.0", - "time", - "tokio", - "tower-cookies", - "tower-layer", - "tower-service", - "tower-sessions-core", - "tower-sessions-memory-store", - "tracing", + "tracel-mlir-rs", + "tracel-mlir-sys", ] [[package]] -name = "tower-sessions-core" -version = "0.14.0" +name = "tracel-llvm-bundler" +version = "20.1.4-7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cce604865576b7751b7a6bc3058f754569a60d689328bb74c52b1d87e355b" +checksum = "3c75b8e477cb8d49d907afab029ca74d48459f5b88c27bdb4c6cd6acb5e61977" dependencies = [ - "async-trait", - "axum-core", - "base64 0.22.1", - "futures", - "http 1.4.0", - "parking_lot", - "rand 0.8.6", + "anyhow", + "bytes", + "constcat", + "dirs", + "liblzma", + "regex", + "reqwest", "serde", "serde_json", - "thiserror 2.0.18", - "time", - "tokio", - "tracing", + "sha2", + "tar", + "walkdir", ] [[package]] -name = "tower-sessions-memory-store" -version = "0.14.0" +name = "tracel-mlir-rs" +version = "20.1.4-7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb05909f2e1420135a831dd5df9f5596d69196d0a64c3499ca474c4bd3d33242" +checksum = "77a478a35efd68d0ba73f747adfb7923b121c64e7f5be9cd8364ca1dcb772d5c" dependencies = [ - "async-trait", - "time", - "tokio", - "tower-sessions-core", + "tracel-mlir-rs-macros", + "tracel-mlir-sys", +] + +[[package]] +name = "tracel-mlir-rs-macros" +version = "20.1.4-7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a94f36868c3b10b1825945223d99d106c73f4d249f063caa4651deeb9379344" +dependencies = [ + "comrak", + "convert_case 0.8.0", + "proc-macro2", + "quote", + "regex", + "syn 2.0.114", + "tracel-llvm-bundler", + "tracel-tblgen-rs", + "unindent", +] + +[[package]] +name = "tracel-mlir-sys" +version = "20.1.4-7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02f26d31af0c225a6d2e3d65d012fd6de848c9fc776897b152ee83b7d1bd15c4" +dependencies = [ + "tracel-llvm-bundler", +] + +[[package]] +name = "tracel-tblgen-rs" +version = "20.1.4-7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d2581070380418ccc33b500f3739e4d4869421fdb477fcea51ff97c6253a52" +dependencies = [ + "bindgen", + "cc", + "paste", + "thiserror 2.0.18", + "tracel-llvm-bundler", ] [[package]] @@ -3769,6 +8163,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.18", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.31" @@ -3777,7 +8183,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] @@ -3803,9 +8209,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.23" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -3829,43 +8235,62 @@ dependencies = [ "strength_reduce", ] +[[package]] +name = "trictrac-bot" +version = "0.1.0" +dependencies = [ + "board-game", + "burn", + "burn-rl", + "confy", + "env_logger 0.10.2", + "internal-iterator", + "log", + "pretty_assertions", + "rand 0.9.2", + "serde", + "serde_json", + "trictrac-store", +] + +[[package]] +name = "trictrac-client_cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode 1.3.3", + "env_logger 0.11.8", + "itertools 0.13.0", + "log", + "pico-args", + "pretty_assertions", + "renet", + "spiel_bot", + "trictrac-bot", + "trictrac-store", +] + [[package]] name = "trictrac-store" version = "0.1.0" dependencies = [ "anyhow", "base64 0.21.7", + "cxx", + "cxx-build", "log", "merge", - "rand 0.9.4", + "pyo3", + "rand 0.9.2", "serde", "transpose", ] [[package]] -name = "trictrac-web" -version = "0.1.0" -dependencies = [ - "backbone-lib", - "futures", - "getrandom 0.3.4", - "gloo-net 0.5.0", - "gloo-storage", - "gloo-timers", - "js-sys", - "leptos", - "leptos_i18n", - "leptos_router", - "qrcodegen", - "rand 0.9.4", - "serde", - "serde_json", - "trictrac-store", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test", - "web-sys", -] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" @@ -3876,10 +8301,10 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.4.0", + "http", "httparse", "log", - "rand 0.8.6", + "rand 0.8.5", "sha1", "thiserror 1.0.69", "utf-8", @@ -3887,20 +8312,36 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.29.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http", "httparse", "log", - "rand 0.9.4", + "rand 0.9.2", "sha1", "thiserror 2.0.18", + "utf-8", ] +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash 2.1.1", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typed-builder" version = "0.20.1" @@ -3918,14 +8359,20 @@ checksum = "3c36781cc0e46a83726d9879608e4cf6c2505237e263a8eb8c24502989cfdb28" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] -name = "typenum" -version = "1.20.0" +name = "typed-path" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "3015e6ce46d5ad8751e4a772543a30c7511468070e98e64e20165f8f81155b64" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -3933,23 +8380,11 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" @@ -3961,16 +8396,33 @@ dependencies = [ ] [[package]] -name = "unicode-properties" -version = "0.1.4" +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] -name = "unicode-segmentation" -version = "1.13.2" +name = "unicode-truncate" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width 0.1.14", +] + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode-xid" @@ -3978,12 +8430,64 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + +[[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 = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "url", + "webpki-roots 0.26.11", +] + [[package]] name = "url" version = "2.5.8" @@ -3996,12 +8500,6 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf-8" version = "0.7.6" @@ -4027,13 +8525,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "uuid" -version = "1.23.1" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.3.4", "js-sys", + "rand 0.9.2", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", "wasm-bindgen", ] @@ -4043,12 +8559,41 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "variadics_please" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "walkdir" version = "2.5.0" @@ -4059,53 +8604,35 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasite" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fe902b4a6b8028a753d5424909b764ccf79b7a209eac9bf97e59cda9f71a42" -dependencies = [ - "wasi 0.14.7+wasi-0.2.4", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.121" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -4116,19 +8643,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.71" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ + "cfg-if", + "futures-util", "js-sys", + "once_cell", "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.121" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4136,87 +8667,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.121" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.121" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-bindgen-test" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af5ec93229ad9ccd0a545a516dec76dc276613f278f6a91aa6b463d5b33d42d0" -dependencies = [ - "async-trait", - "cast", - "js-sys", - "libm", - "minicov", - "nu-ansi-term", - "num-traits", - "oorandom", - "serde", - "serde_json", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", - "wasm-bindgen-test-shared", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c81b9fef827e575e0e54431736d1baa0d700315d8c62cfef1f61fa3aad0cbeb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "wasm-bindgen-test-shared" -version = "0.2.121" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4d8ae7ad5440360e9799dfd42857d126454a88441ddf72d288ef83fa47f527" - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -4230,23 +8700,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.98" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -4263,56 +8721,570 @@ dependencies = [ ] [[package]] -name = "whoami" -version = "2.1.2" +name = "webpki-roots" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ + "webpki-roots 1.0.6", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wgpu" +version = "26.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b6ff82bbf6e9206828e1a3178e851f8c20f1c9028e74dd3a8090741ccd5798" +dependencies = [ + "arrayvec 0.7.6", + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.15.5", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "26.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f62f1053bd28c2268f42916f31588f81f64796e2ff91b81293515017ca8bd9" +dependencies = [ + "arrayvec 0.7.6", + "bit-set", + "bit-vec", + "bitflags 2.10.0", + "cfg_aliases", + "document-features", + "hashbrown 0.15.5", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18ae5fbde6a4cbebae38358aa73fcd6e0f15c6144b67ef5dc91ded0db125dbdf" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7670e390f416006f746b4600fdd9136455e3627f5bd763abf9a65daa216dd2d" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "720a5cb9d12b3d337c15ff0e24d3e97ed11490ff3f7506e7f3d98c68fa5d6f14" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "26.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d0e67224cc7305b3b4eb2cc57ca4c4c3afc665c1d1bee162ea806e19c47bdd" +dependencies = [ + "android_system_properties", + "arrayvec 0.7.6", + "ash", + "bit-set", + "bitflags 2.10.0", + "block", + "bytemuck", + "cfg-if", + "cfg_aliases", + "core-graphics-types", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.15.5", + "js-sys", + "khronos-egl", "libc", - "libredox", - "objc2-system-configuration", - "wasite", + "libloading", + "log", + "metal", + "naga", + "ndk-sys", + "objc", + "ordered-float 4.6.0", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "smallvec", + "thiserror 2.0.18", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "windows 0.58.0", + "windows-core 0.58.0", +] + +[[package]] +name = "wgpu-types" +version = "26.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca7a8d8af57c18f57d393601a1fb159ace8b2328f1b6b5f80893f7d672c9ae2" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "js-sys", + "log", + "thiserror 2.0.18", "web-sys", ] +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] -name = "winnow" -version = "0.7.15" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "memchr", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] -name = "winnow" -version = "1.0.2" +name = "windows-targets" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -4322,93 +9294,17 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] [[package]] -name = "wit-bindgen" -version = "0.57.1" +name = "wrapcenum-derive" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", + "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", + "syn 2.0.114", ] [[package]] @@ -4428,9 +9324,25 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.3", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xxhash-rust" @@ -4438,6 +9350,12 @@ version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yansi" version = "1.0.1" @@ -4458,12 +9376,12 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ "stable_deref_trait", - "yoke-derive 0.8.2", + "yoke-derive 0.8.1", "zerofrom", ] @@ -4475,63 +9393,69 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", - "synstructure", + "syn 2.0.114", + "synstructure 0.13.2", ] [[package]] name = "yoke-derive" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", - "synstructure", + "syn 2.0.114", + "synstructure 0.13.2", ] [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", - "synstructure", + "syn 2.0.114", + "synstructure 0.13.2", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.1.3" @@ -4545,12 +9469,12 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", - "yoke 0.8.2", + "yoke 0.8.1", "zerofrom", ] @@ -4567,13 +9491,13 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ - "yoke 0.8.2", + "yoke 0.8.1", "zerofrom", - "zerovec-derive 0.11.3", + "zerovec-derive 0.11.2", ] [[package]] @@ -4584,22 +9508,122 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", ] [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.114", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zip" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc12baa6db2b15a140161ce53d72209dacea594230798c24774139b54ecaa980" +dependencies = [ + "crc32fast", + "indexmap", + "memchr", + "typed-path", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "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 = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core 0.4.12", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe" +dependencies = [ + "zune-core 0.5.1", +] diff --git a/Cargo.toml b/Cargo.toml index e78d862..72e3f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,4 @@ [workspace] resolver = "2" -members = [ - "store", - "clients/backbone-lib", - "clients/web", - "server/protocol", - "server/relay-server", -] - -default-members = [ - "store", - "clients/backbone-lib", - "server/protocol", - "server/relay-server", -] - -# For the server we will need opt-level='3' -[profile.release] -opt-level = 'z' # Minimum space -lto = "fat" # Aggressive Link Time Optimization -codegen-units = 1 +members = ["client_cli", "bot", "store", "spiel_bot", "client_web"] diff --git a/README.md b/README.md index ca4c0de..e74fb69 100644 --- a/README.md +++ b/README.md @@ -2,133 +2,40 @@ This is a game of [Trictrac](https://en.wikipedia.org/wiki/Trictrac) rust implementation. -The project is still on its early stages. +The project is on its early stages. +Rules (without "schools") are implemented, as well as a rudimentary terminal interface which allow you to play against a bot which plays randomly. + +Training of AI bots is the work in progress. ## Usage -Install [devenv](https://devenv.sh/getting-started/), start a devenv shell `devenv shell`, and run the following commands. - -```bash -# Run the relay server -just build-relay -just run-relay # listens on :8080 - -# Run the game (separate terminal) -just dev -``` - -Open two browser windows at `http://127.0.0.1:9091`. In one, create a room; in the other, join with the same room name. - -Playing with the cli against the 'random' bot: `cargo run --bin=client_cli -- --bot random` +`cargo run --bin=client_cli -- --bot random` ## Roadmap - [x] rules - [x] command line interface - [x] basic bot (random play) -- [ ] web client (in progress) -- [ ] network game (in progress) - [ ] AI bot +- [ ] network game +- [ ] web client ## Code structure - game rules and game state are implemented in the _store/_ folder. -- the command-line application is implemented in _clients/cli/_; it allows you to play against a bot, or to have two bots play against each other +- the command-line application is implemented in _client_cli/_; it allows you to play against a bot, or to have two bots play against each other - the bots algorithms and the training of their models are implemented in the _bot/_ and _spiel_bot_ folders. ### _store_ package The game state is defined by the `GameState` struct in _store/src/game.rs_. The `to_string_id()` method allows this state to be encoded compactly in a string (without the played moves history). For a more readable textual representation, the `fmt::Display` trait is implemented. -### _clients/cli_ package +### _client_cli_ package -`clients/cli/src/game_runner.rs` contains the logic to make two bots play against each other. +`client_cli/src/game_runner.rs` contains the logic to make two bots play against each other. ### _bot_ package - `bot/src/strategy/default.rs` contains the code for a basic bot strategy: it determines the list of valid moves (using the `get_possible_moves_sequences` method of `store::MoveRules`) and simply executes the first move in the list. - `bot/src/strategy/dqnburn.rs` is another bot strategy that uses a reinforcement learning trained model with the DQN algorithm via the burn library (). - `bot/scripts/trains.sh` allows you to train agents using different algorithms (DQN, PPO, SAC). - -### multiplayer game - -Packages "clients/backbone-lib", "clients/web/game", "server/protocol", "server/relay-server" are a Leptos-optimized adaptation of the macroquad-based [Carbonfreezer/multiplayer](https://github.com/Carbonfreezer/multiplayer) project. It is a multiplayer game system in Rust targeting browser-based board games compiled as WASM. The original project used Macroquad with a polling-based transport layer; this version replaces that with an async session API built for [Leptos](https://leptos.dev/). - -The system consists of: - -- A **relay server** (Axum/Tokio) that routes messages between players and manages rooms, without knowing anything about game rules. -- A **backbone library** that handles WebSocket connection, handshake, and message routing, exposing an async API to the game frontend. -- Game-specific **backend logic** implementing the `BackEndArchitecture` trait, which runs only on the hosting client. -- A **Leptos frontend** that connects to a session and reacts to state updates. - -There is no dedicated game server. One of the players acts as the host: their browser runs the game backend locally. The relay server only forwards messages — it never touches game state. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Host Client │ -│ ┌─────────────┐ ┌──────────────────┐ ┌────────────┐ │ -│ │ Leptos UI │◄──►│ GameSession │◄──►│ Backend │ │ -│ └─────────────┘ └────────┬─────────┘ └────────────┘ │ -└───────────────────────────── │ ────────────────────────────┘ - │ WebSocket - ┌──────▼──────┐ - │ Relay Server│ - └──────┬──────┘ - │ WebSocket -┌───────────────────────────────│────────────────────────────┐ -│ ┌─────────────┐ ┌─────────▼────────┐ │ -│ │ Leptos UI │◄──►│ GameSession │ (no backend) │ -│ └─────────────┘ └──────────────────┘ │ -│ Remote Client │ -└─────────────────────────────────────────────────────────────┘ -``` - -#### Data flow - -- **Actions** (e.g. "place stone at B3") flow from the UI to the host backend via `GameSession::send_action()`. -- **State updates** flow back as `ViewStateUpdate::Full` (full snapshot, on join or reset) or `ViewStateUpdate::Incremental` (delta, for animations). -- **Timers** are managed by the host's background task (wall-clock, no polling required from the game). - -#### backbone-lib session API - -The key design choice: `backbone-lib` owns a background async task per session. The Leptos app never drives a loop — it just awaits on events. - -#### Workspace - -**server/protocol** - -Shared message-type constants and the `JoinRequest` struct used during the WebSocket handshake. - -**server/relay-server** - -Listens on port 8080. Loads `GameConfig.json` on startup to know which games exist and their player limits: - -```json -[{ "name": "trictrac", "max_players": 10 }] -``` - -Games can be added at runtime via the `/reload` endpoint. `/enlist` lists active rooms. A watchdog cleans up inactive rooms every 20 minutes. - -For production, put it behind a reverse proxy with SSL (the browser requires `wss://` on HTTPS pages). Example Caddy config: - -``` -your-domain.com { - handle_path /api/* { - reverse_proxy localhost:8080 - } - file_server -} -``` - -**clients/backbone-lib** - -Modules: - -| Module | Purpose | -| ---------- | ---------------------------------------------------------------------------------------------------------- | -| `session` | `GameSession`, `connect()`, `SessionEvent`, `RoomConfig` | -| `host` | Background async task for the hosting client (drives `BackEndArchitecture`, manages timers) | -| `client` | Background async task for non-hosting clients | -| `protocol` | Wire encoding/decoding helpers (postcard + message-type bytes) | -| `platform` | `spawn_task` / `sleep_ms` abstractions (WASM: `spawn_local` + gloo-timers; native: thread + thread::sleep) | -| `traits` | `BackEndArchitecture`, `BackendCommand`, `ViewStateUpdate`, `SerializationCap` | diff --git a/bot/Cargo.toml b/bot/Cargo.toml index de957df..d24adcc 100644 --- a/bot/Cargo.toml +++ b/bot/Cargo.toml @@ -13,7 +13,7 @@ path = "src/burnrl/main.rs" pretty_assertions = "1.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -trictrac-store = { path = "../store" } +trictrac-store = { path = "../store", features = ["python"] } rand = "0.9" env_logger = "0.10" burn = { version = "0.20", features = ["ndarray", "autodiff"] } diff --git a/bot/python/test.py b/bot/python/test.py new file mode 100644 index 0000000..8c4f16b --- /dev/null +++ b/bot/python/test.py @@ -0,0 +1,5 @@ +import trictrac_store + +game = trictrac_store.TricTrac() +print(game.current_player_idx()) +print(game.get_legal_actions(game.current_player_idx())) diff --git a/clients/cli/Cargo.toml b/client_cli/Cargo.toml similarity index 67% rename from clients/cli/Cargo.toml rename to client_cli/Cargo.toml index 0149b1b..d85dd8b 100644 --- a/clients/cli/Cargo.toml +++ b/client_cli/Cargo.toml @@ -13,9 +13,9 @@ bincode = "1.3.3" pico-args = "0.5.0" pretty_assertions = "1.4.0" renet = "0.0.13" -trictrac-store = { path = "../../store" } -trictrac-bot = { path = "../../bot" } -spiel_bot = { path = "../../spiel_bot" } +trictrac-store = { path = "../store", features = ["python"] } +trictrac-bot = { path = "../bot" } +spiel_bot = { path = "../spiel_bot" } itertools = "0.13.0" env_logger = "0.11.6" log = "0.4.20" diff --git a/clients/cli/src/app.rs b/client_cli/src/app.rs similarity index 100% rename from clients/cli/src/app.rs rename to client_cli/src/app.rs diff --git a/clients/cli/src/game_runner.rs b/client_cli/src/game_runner.rs similarity index 100% rename from clients/cli/src/game_runner.rs rename to client_cli/src/game_runner.rs diff --git a/clients/cli/src/main.rs b/client_cli/src/main.rs similarity index 100% rename from clients/cli/src/main.rs rename to client_cli/src/main.rs diff --git a/client_web/Cargo.toml b/client_web/Cargo.toml new file mode 100644 index 0000000..3e648ea --- /dev/null +++ b/client_web/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "client_web" +version = "0.1.0" +edition = "2021" + +[package.metadata.leptos-i18n] +default = "en" +locales = ["en", "fr"] + +[dependencies] +leptos_i18n = { version = "0.5", features = ["csr", "interpolate_display"] } +trictrac-store = { path = "../store" } +backbone-lib = { path = "../../forks/multiplayer/backbone-lib" } +leptos = { version = "0.7", features = ["csr"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1" +futures = "0.3" +rand = "0.9" +gloo-storage = "0.3" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4" +# getrandom 0.3 requires an explicit WASM backend; "wasm_js" uses window.crypto.getRandomValues. +# Must be a direct dependency (not just transitive) for the feature to take effect. +getrandom = { version = "0.3", features = ["wasm_js"] } diff --git a/client_web/Trunk.toml b/client_web/Trunk.toml new file mode 100644 index 0000000..57a2aaa --- /dev/null +++ b/client_web/Trunk.toml @@ -0,0 +1,2 @@ +[serve] +port = 9092 diff --git a/client_web/assets/style.css b/client_web/assets/style.css new file mode 100644 index 0000000..61d8cec --- /dev/null +++ b/client_web/assets/style.css @@ -0,0 +1,418 @@ +/* ── Reset & base ──────────────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +body { + font-family: sans-serif; + background: #c8b084; + display: flex; + justify-content: center; + padding: 1.5rem; + min-height: 100vh; +} + +/* ── Login / Connecting screens ────────────────────────────────────── */ +.login-container { + display: flex; + flex-direction: column; + gap: 0.75rem; + max-width: 320px; + margin-top: 4rem; +} + +.login-container h1 { font-size: 2rem; text-align: center; margin-bottom: 0.5rem; } + +input[type="text"] { + padding: 0.5rem 0.75rem; + font-size: 1rem; + border: 1px solid #aaa; + border-radius: 4px; +} + +.error-msg { color: #c00; font-size: 0.9rem; } + +.connecting { font-size: 1.2rem; margin-top: 4rem; text-align: center; } + +/* ── Buttons ────────────────────────────────────────────────────────── */ +.btn { + padding: 0.5rem 1.25rem; + font-size: 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + transition: opacity 0.15s; +} +.btn:disabled { opacity: 0.4; cursor: default; } +.btn-primary { background: #3a6b3a; color: #fff; } +.btn-secondary { background: #5a4a2a; color: #fff; } +.btn-bot { background: #2a5a7a; color: #fff; } +.btn:not(:disabled):hover { opacity: 0.85; } + +/* ── Game container ─────────────────────────────────────────────────── */ +.game-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + width: 100%; +} + +/* ── Language switcher ──────────────────────────────────────────────── */ +.lang-switcher { + display: flex; + gap: 0.25rem; +} + +.lang-switcher button { + font-size: 0.75rem; + padding: 0.15rem 0.4rem; + border: 1px solid rgba(0,0,0,0.3); + border-radius: 3px; + background: transparent; + cursor: pointer; + color: inherit; + opacity: 0.6; +} + +.lang-switcher button.lang-active { + opacity: 1; + font-weight: bold; + background: rgba(0,0,0,0.12); +} + +.login-container .lang-switcher { + justify-content: flex-end; + margin-bottom: 1rem; +} + +/* ── Top bar ─────────────────────────────────────────────────────────── */ +.top-bar { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} + +.quit-link { + font-size: 0.85rem; + color: #5a4a2a; + text-decoration: underline; + cursor: pointer; +} + +/* ── Player score panel ─────────────────────────────────────────────── */ +.player-score-panel { + background: #f5edd8; + border-radius: 6px; + padding: 0.5rem 1rem; + font-size: 0.9rem; + box-shadow: 0 1px 4px rgba(0,0,0,0.2); + width: 100%; +} + +.player-score-header { + margin-bottom: 0.3rem; +} + +.player-name { + font-weight: bold; + font-size: 1rem; +} + +.score-bars { + display: flex; + flex-direction: column; + gap: 4px; +} + +.score-bar-row { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.score-bar-label { + font-size: 0.8rem; + color: #555; + width: 3.5rem; + text-align: right; + flex-shrink: 0; +} + +.score-bar { + width: 140px; + height: 10px; + background: rgba(0,0,0,0.12); + border-radius: 5px; + overflow: hidden; + flex-shrink: 0; +} + +.score-bar-fill { + height: 100%; + border-radius: 5px; + transition: width 0.3s; +} + +.score-bar-points { background: #4a7a3a; } +.score-bar-holes { background: #7a4a2a; } + +.score-bar-value { + font-size: 0.8rem; + color: #444; + min-width: 2.5rem; +} + +.bredouille-badge { + font-size: 0.7rem; + font-weight: bold; + color: #fff; + background: #c07800; + border-radius: 3px; + padding: 0.05em 0.35em; + cursor: default; +} + +.player-jans { + margin-top: 0.35rem; + border-top: 1px solid rgba(0,0,0,0.1); + padding-top: 0.25rem; +} + +/* ── Board + side panel ─────────────────────────────────────────────── */ +.board-and-panel { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 1rem; +} + +.side-panel { + display: flex; + flex-direction: column; + gap: 0.75rem; + min-width: 160px; + padding-top: 0.25rem; +} + +.action-buttons { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +/* ── Status bar ─────────────────────────────────────────────────────── */ +.status-bar { + display: flex; + gap: 1rem; + align-items: center; + font-size: 1.05rem; + font-weight: 500; +} + +/* ── Dice bar ───────────────────────────────────────────────────────── */ +.dice-bar { + display: flex; + align-items: center; + gap: 0.75rem; +} + +/* ── Die face (SVG) ─────────────────────────────────────────────────── */ +.die-face rect { + fill: #fffff0; + stroke: #2a1a00; + stroke-width: 2; +} +.die-face circle { + fill: #1a0a00; +} +.die-face.die-used rect { + fill: #d8d4c8; + stroke: #8a7a60; +} +.die-face.die-used circle { + fill: #8a7a60; +} + +/* ── Jan panel ──────────────────────────────────────────────────────── */ +.jan-panel { + display: flex; + flex-direction: column; + gap: 2px; + background: #f5edd8; + border-radius: 6px; + padding: 0.4rem 1rem; + font-size: 0.9rem; + box-shadow: 0 1px 4px rgba(0,0,0,0.15); + min-width: 260px; +} + +.jan-row { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 2px 4px; + border-radius: 3px; +} +.jan-expandable { cursor: pointer; } +.jan-expandable:hover { background: rgba(0,0,0,0.06); } + +.jan-positive { color: #1a5c1a; } +.jan-negative { color: #8b1a1a; } + +.jan-label { flex: 1; } +.jan-tag { + font-size: 0.75rem; + padding: 0.1em 0.4em; + border-radius: 3px; + background: rgba(0,0,0,0.08); + color: #555; + white-space: nowrap; +} +.jan-pts { font-weight: bold; text-align: right; min-width: 3rem; } + +.jan-moves { padding: 1px 4px 4px 1rem; display: flex; flex-direction: column; gap: 2px; } +.jan-moves.hidden { display: none; } +.jan-move-line { font-family: monospace; font-size: 0.8rem; color: #444; } + +/* ── Game-over overlay ──────────────────────────────────────────────── */ +.game-over-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 100; +} + +.game-over-box { + background: #f5edd8; + border-radius: 8px; + padding: 2rem 2.5rem; + text-align: center; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); + display: flex; + flex-direction: column; + gap: 1.25rem; + min-width: 260px; +} + +.game-over-box h2 { + font-size: 1.75rem; +} + +.game-over-winner { + font-size: 1.25rem; + font-weight: bold; + color: #3a6b3a; +} + +.game-over-actions { + display: flex; + gap: 0.75rem; + justify-content: center; +} + +/* ── Board ──────────────────────────────────────────────────────────── */ +.board { + background: #2e6b2e; + border: 4px solid #1a3d1a; + border-radius: 8px; + padding: 4px; + display: flex; + flex-direction: column; + gap: 4px; + user-select: none; + box-shadow: 0 4px 12px rgba(0,0,0,0.4); + position: relative; +} + +.board-row { + display: flex; + gap: 4px; +} + +.board-quarter { + display: flex; + gap: 2px; +} + +.board-bar { + width: 20px; + background: #1a3d1a; + border-radius: 3px; +} + +.board-center-bar { + height: 12px; + background: #1a3d1a; + border-radius: 3px; +} + +/* ── Fields ─────────────────────────────────────────────────────────── */ +.field { + width: 60px; + height: 180px; + background: #d4a843; + border-radius: 4px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + padding: 4px 2px; + position: relative; + transition: background 0.1s; +} + +/* Alternating field colours */ +.board-quarter .field:nth-child(odd) { background: #c49030; } +.board-quarter .field:nth-child(even) { background: #d4a843; } + +.top-row .field { justify-content: flex-start; } + +.field.clickable { cursor: pointer; } +.field.clickable:hover { background: #e8c060 !important; } +.field.selected { background: #88bb44 !important; outline: 2px solid #446622; } +.field.dest { background: #aad060 !important; } + +.field-num { + font-size: 0.65rem; + color: rgba(0,0,0,0.45); + position: absolute; + bottom: 2px; +} + +.top-row .field-num { bottom: auto; top: 2px; } + +/* ── Checkers ───────────────────────────────────────────────────────── */ +.checker-stack { + display: flex; + flex-direction: column; + align-items: center; +} + +.checker { + width: 40px; + height: 40px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + font-weight: bold; + border: 2px solid rgba(0,0,0,0.3); + box-shadow: inset 0 2px 4px rgba(255,255,255,0.3), 0 1px 3px rgba(0,0,0,0.3); + flex-shrink: 0; +} + +.checker + .checker { margin-top: 2px; } + +.checker.white { + background: radial-gradient(circle at 35% 35%, #ffffff, #cccccc); + color: #333; +} + +.checker.black { + background: radial-gradient(circle at 35% 35%, #555555, #111111); + color: #eee; +} diff --git a/clients/web/index.html b/client_web/index.html similarity index 81% rename from clients/web/index.html rename to client_web/index.html index 7399dbc..b661d76 100644 --- a/clients/web/index.html +++ b/client_web/index.html @@ -6,7 +6,6 @@ Trictrac - diff --git a/client_web/locales/en.json b/client_web/locales/en.json new file mode 100644 index 0000000..799dbbb --- /dev/null +++ b/client_web/locales/en.json @@ -0,0 +1,42 @@ +{ + "room_name_placeholder": "Room name", + "create_room": "Create Room", + "join_room": "Join Room", + "connecting": "Connecting…", + "game_over": "Game over", + "waiting_for_opponent": "Waiting for opponent…", + "your_turn_roll": "Your turn — roll the dice", + "hold_or_go": "Hold or Go?", + "select_move": "Select move {{ n }} of 2", + "your_turn": "Your turn", + "opponent_turn": "Opponent's turn", + "room_label": "Room: {{ id }}", + "quit": "Quit", + "roll_dice": "Roll dice", + "go": "Go", + "empty_move": "Empty move", + "you_suffix": " (you)", + "points_label": "Points", + "holes_label": "Holes", + "bredouille_title": "Can bredouille", + "jan_double": "double", + "jan_simple": "simple", + "jan_filled_quarter": "Quarter filled", + "jan_true_hit_small": "True hit (small jan)", + "jan_true_hit_big": "True hit (big jan)", + "jan_true_hit_corner": "True hit (opp. corner)", + "jan_first_exit": "First to exit", + "jan_six_tables": "Six tables", + "jan_two_tables": "Two tables", + "jan_mezeas": "Mezeas", + "jan_false_hit_small": "False hit (small jan)", + "jan_false_hit_big": "False hit (big jan)", + "jan_contre_two": "Contre two tables", + "jan_contre_mezeas": "Contre mezeas", + "jan_helpless_man": "Helpless man", + "play_vs_bot": "Play vs Bot", + "vs_bot_label": "vs Bot", + "you_win": "You win!", + "opp_wins": "{{ name }} wins!", + "play_again": "Play again" +} diff --git a/client_web/locales/fr.json b/client_web/locales/fr.json new file mode 100644 index 0000000..df6a2b5 --- /dev/null +++ b/client_web/locales/fr.json @@ -0,0 +1,42 @@ +{ + "room_name_placeholder": "Nom de la salle", + "create_room": "Créer une salle", + "join_room": "Rejoindre", + "connecting": "Connexion en cours…", + "game_over": "Partie terminée", + "waiting_for_opponent": "En attente de l'adversaire…", + "your_turn_roll": "À votre tour — lancez les dés", + "hold_or_go": "Tenir ou s'en aller ?", + "select_move": "Sélectionner le coup {{ n }} sur 2", + "your_turn": "Votre tour", + "opponent_turn": "Tour de l'adversaire", + "room_label": "Salle : {{ id }}", + "quit": "Quitter", + "roll_dice": "Lancer les dés", + "go": "S'en aller", + "empty_move": "Mouvement impossible", + "you_suffix": " (vous)", + "points_label": "Points", + "holes_label": "Trous", + "bredouille_title": "Peut faire bredouille", + "jan_double": "double", + "jan_simple": "simple", + "jan_filled_quarter": "Remplissage", + "jan_true_hit_small": "Battage à vrai (petit jan)", + "jan_true_hit_big": "Battage à vrai (grand jan)", + "jan_true_hit_corner": "Battage coin adverse", + "jan_first_exit": "Premier sorti", + "jan_six_tables": "Jan de six tables", + "jan_two_tables": "Jan de deux tables", + "jan_mezeas": "Jan de mézéas", + "jan_false_hit_small": "Battage à faux (petit jan)", + "jan_false_hit_big": "Battage à faux (grand jan)", + "jan_contre_two": "Contre jan de deux tables", + "jan_contre_mezeas": "Contre jan de mezeas", + "jan_helpless_man": "Dame impuissante", + "play_vs_bot": "Jouer contre le bot", + "vs_bot_label": "contre le bot", + "you_win": "Vous avez gagné !", + "opp_wins": "{{ name }} gagne !", + "play_again": "Rejouer" +} diff --git a/client_web/src/app.rs b/client_web/src/app.rs new file mode 100644 index 0000000..ae4c22f --- /dev/null +++ b/client_web/src/app.rs @@ -0,0 +1,331 @@ +use futures::channel::mpsc; +use futures::{FutureExt, StreamExt}; +use gloo_storage::{LocalStorage, Storage}; +use leptos::prelude::*; +use leptos::task::spawn_local; +use serde::{Deserialize, Serialize}; + +use backbone_lib::session::{ConnectError, GameSession, RoomConfig, RoomRole, SessionEvent}; +use backbone_lib::traits::{BackEndArchitecture, BackendCommand, ViewStateUpdate}; + +use crate::components::{ConnectingScreen, GameScreen, LoginScreen}; +use crate::i18n::I18nContextProvider; +use crate::trictrac::backend::TrictracBackend; +use crate::trictrac::bot_local::bot_decide; +use crate::trictrac::types::{GameDelta, PlayerAction, ViewState}; + +const RELAY_URL: &str = "ws://127.0.0.1:8080/ws"; +const GAME_ID: &str = "trictrac"; +const STORAGE_KEY: &str = "trictrac_session"; + +/// The state the UI needs to render the game screen. +#[derive(Clone, PartialEq)] +pub struct GameUiState { + pub view_state: ViewState, + /// 0 = host, 1 = guest + pub player_id: u16, + pub room_id: String, + pub is_bot_game: bool, +} + +/// Which screen is currently shown. +#[derive(Clone, PartialEq)] +pub enum Screen { + Login { error: Option }, + Connecting, + Playing(GameUiState), +} + +/// Commands sent from UI event handlers into the network task. +pub enum NetCommand { + CreateRoom { + room: String, + }, + JoinRoom { + room: String, + }, + Reconnect { + relay_url: String, + game_id: String, + room_id: String, + token: u64, + host_state: Option>, + }, + PlayVsBot, + Action(PlayerAction), + Disconnect, +} + +/// Stored in localStorage to reconnect after a page refresh. +#[derive(Serialize, Deserialize)] +struct StoredSession { + relay_url: String, + game_id: String, + room_id: String, + token: u64, + #[serde(default)] + is_host: bool, + #[serde(default)] + view_state: Option, +} + +fn save_session(session: &StoredSession) { + LocalStorage::set(STORAGE_KEY, session).ok(); +} + +fn load_session() -> Option { + LocalStorage::get::(STORAGE_KEY).ok() +} + +fn clear_session() { + LocalStorage::delete(STORAGE_KEY); +} + +#[component] +pub fn App() -> impl IntoView { + let stored = load_session(); + let initial_screen = if stored.is_some() { + Screen::Connecting + } else { + Screen::Login { error: None } + }; + let screen = RwSignal::new(initial_screen); + + let (cmd_tx, mut cmd_rx) = mpsc::unbounded::(); + provide_context(cmd_tx.clone()); + + if let Some(s) = stored { + let host_state = s + .view_state + .as_ref() + .and_then(|vs| serde_json::to_vec(vs).ok()); + cmd_tx + .unbounded_send(NetCommand::Reconnect { + relay_url: s.relay_url, + game_id: s.game_id, + room_id: s.room_id, + token: s.token, + host_state, + }) + .ok(); + } + + spawn_local(async move { + loop { + // Wait for a connect/reconnect command (or PlayVsBot). + // None means "play vs bot"; Some((config, is_reconnect)) means "connect to relay". + let remote_config: Option<(RoomConfig, bool)> = loop { + match cmd_rx.next().await { + Some(NetCommand::PlayVsBot) => break None, + Some(NetCommand::CreateRoom { room }) => { + break Some(( + RoomConfig { + relay_url: RELAY_URL.to_string(), + game_id: GAME_ID.to_string(), + room_id: room, + rule_variation: 0, + role: RoomRole::Create, + reconnect_token: None, + host_state: None, + }, + false, + )); + } + Some(NetCommand::JoinRoom { room }) => { + break Some(( + RoomConfig { + relay_url: RELAY_URL.to_string(), + game_id: GAME_ID.to_string(), + room_id: room, + rule_variation: 0, + role: RoomRole::Join, + reconnect_token: None, + host_state: None, + }, + false, + )); + } + Some(NetCommand::Reconnect { + relay_url, + game_id, + room_id, + token, + host_state, + }) => { + break Some(( + RoomConfig { + relay_url, + game_id, + room_id, + rule_variation: 0, + role: RoomRole::Join, + reconnect_token: Some(token), + host_state, + }, + true, + )); + } + _ => {} // Ignore game commands while disconnected. + } + }; + + if remote_config.is_none() { + loop { + let restart = run_local_bot_game(screen, &mut cmd_rx).await; + if !restart { break; } + } + screen.set(Screen::Login { error: None }); + continue; + } + let (config, is_reconnect) = remote_config.unwrap(); + + screen.set(Screen::Connecting); + + let room_id_for_storage = config.room_id.clone(); + let mut session: GameSession = + match GameSession::connect::(config).await { + Ok(s) => s, + Err(ConnectError::WebSocket(e) | ConnectError::Handshake(e)) => { + if is_reconnect { + clear_session(); + } + screen.set(Screen::Login { error: Some(e) }); + continue; + } + }; + + if !session.is_host { + save_session(&StoredSession { + relay_url: RELAY_URL.to_string(), + game_id: GAME_ID.to_string(), + room_id: room_id_for_storage.clone(), + token: session.reconnect_token, + is_host: false, + view_state: None, + }); + } + + let is_host = session.is_host; + let player_id = session.player_id; + let reconnect_token = session.reconnect_token; + let mut vs = ViewState::default_with_names("Host", "Guest"); + + loop { + futures::select! { + cmd = cmd_rx.next().fuse() => match cmd { + Some(NetCommand::Action(action)) => { + session.send_action(action); + } + _ => { + clear_session(); + session.disconnect(); + screen.set(Screen::Login { error: None }); + break; + } + }, + event = session.next_event().fuse() => match event { + Some(SessionEvent::Update(u)) => { + match u { + ViewStateUpdate::Full(state) => vs = state, + ViewStateUpdate::Incremental(delta) => vs.apply_delta(&delta), + } + if is_host { + save_session(&StoredSession { + relay_url: RELAY_URL.to_string(), + game_id: GAME_ID.to_string(), + room_id: room_id_for_storage.clone(), + token: reconnect_token, + is_host: true, + view_state: Some(vs.clone()), + }); + } + screen.set(Screen::Playing(GameUiState { + view_state: vs.clone(), + player_id, + room_id: room_id_for_storage.clone(), + is_bot_game: false, + })); + } + Some(SessionEvent::Disconnected(reason)) => { + screen.set(Screen::Login { error: reason }); + break; + } + None => { + screen.set(Screen::Login { error: None }); + break; + } + } + } + } + } + }); + + view! { + + {move || match screen.get() { + Screen::Login { error } => view! { }.into_any(), + Screen::Connecting => view! { }.into_any(), + Screen::Playing(state) => view! { }.into_any(), + }} + + } +} + +/// Runs one local bot game. Returns `true` if the player wants to play again. +async fn run_local_bot_game( + screen: RwSignal, + cmd_rx: &mut futures::channel::mpsc::UnboundedReceiver, +) -> bool { + let mut backend = TrictracBackend::new(0); + backend.player_arrival(0); + backend.player_arrival(1); + + let mut vs = ViewState::default_with_names("You", "Bot"); + drain_and_update(&mut backend, &mut vs, screen); + + loop { + match cmd_rx.next().await { + Some(NetCommand::Action(action)) => { + backend.inform_rpc(0, action); + } + Some(NetCommand::PlayVsBot) => return true, + _ => return false, + } + + drain_and_update(&mut backend, &mut vs, screen); + + loop { + match bot_decide(backend.get_game()) { + None => break, + Some(action) => { + backend.inform_rpc(1, action); + drain_and_update(&mut backend, &mut vs, screen); + } + } + } + } +} + +fn drain_and_update( + backend: &mut TrictracBackend, + vs: &mut ViewState, + screen: RwSignal, +) { + for cmd in backend.drain_commands() { + match cmd { + BackendCommand::ResetViewState => { + *vs = backend.get_view_state().clone(); + } + BackendCommand::Delta(delta) => { + vs.apply_delta(&delta); + } + _ => {} + } + } + screen.set(Screen::Playing(GameUiState { + view_state: vs.clone(), + player_id: 0, + room_id: String::new(), + is_bot_game: true, + })); +} diff --git a/client_web/src/components/board.rs b/client_web/src/components/board.rs new file mode 100644 index 0000000..a60b99e --- /dev/null +++ b/client_web/src/components/board.rs @@ -0,0 +1,371 @@ +use leptos::prelude::*; +use trictrac_store::CheckerMove; + +use crate::trictrac::types::{SerTurnStage, ViewState}; + +/// Field numbers in visual display order (left-to-right for each quarter), white's perspective. +const TOP_LEFT_W: [u8; 6] = [13, 14, 15, 16, 17, 18]; +const TOP_RIGHT_W: [u8; 6] = [19, 20, 21, 22, 23, 24]; +const BOT_LEFT_W: [u8; 6] = [12, 11, 10, 9, 8, 7]; +const BOT_RIGHT_W: [u8; 6] = [6, 5, 4, 3, 2, 1]; + +/// 180° rotation of white's layout: black's pieces (field 24) appear at the bottom. +const TOP_LEFT_B: [u8; 6] = [1, 2, 3, 4, 5, 6]; +const TOP_RIGHT_B: [u8; 6] = [7, 8, 9, 10, 11, 12]; +const BOT_LEFT_B: [u8; 6] = [24, 23, 22, 21, 20, 19]; +const BOT_RIGHT_B: [u8; 6] = [18, 17, 16, 15, 14, 13]; + +/// Returns the displayed board value for `field_num` after applying `staged_moves`. +/// Field numbers are always in white's coordinate system (1–24). +fn displayed_value( + base_board: [i8; 24], + staged_moves: &[(u8, u8)], + is_white: bool, + field_num: u8, +) -> i8 { + let mut val = base_board[(field_num - 1) as usize]; + let delta: i8 = if is_white { 1 } else { -1 }; + for &(from, to) in staged_moves { + if from == field_num { + val -= delta; + } + if to == field_num { + val += delta; + } + } + val +} + +/// Fields whose checkers may be selected as the next origin given already-staged moves. +fn valid_origins_for(seqs: &[(CheckerMove, CheckerMove)], staged: &[(u8, u8)]) -> Vec { + let mut v: Vec = match staged.len() { + 0 => seqs.iter() + .map(|(m1, _)| m1.get_from() as u8) + .filter(|&f| f != 0) + .collect(), + 1 => { + let (f0, t0) = staged[0]; + seqs.iter() + .filter(|(m1, _)| m1.get_from() as u8 == f0 && m1.get_to() as u8 == t0) + .map(|(_, m2)| m2.get_from() as u8) + .filter(|&f| f != 0) + .collect() + } + _ => vec![], + }; + v.sort_unstable(); + v.dedup(); + v +} + +/// Pixel center of a board field in the SVG overlay coordinate space. +/// Geometry is derived from CSS: field 60px wide, 180px tall, board padding 4px, +/// board-row gap 4px, board-bar 20px, board-center-bar 12px. +fn field_center(f: usize, is_white: bool) -> Option<(f32, f32)> { + if f == 0 || f > 24 { + return None; + } + let (qi, right, top): (usize, bool, bool) = if is_white { + match f { + 13..=18 => (f - 13, false, true), + 19..=24 => (f - 19, true, true), + 7..=12 => (12 - f, false, false), + 1..=6 => (6 - f, true, false), + _ => return None, + } + } else { + match f { + 1..=6 => (f - 1, false, true), + 7..=12 => (f - 7, true, true), + 19..=24 => (24 - f, false, false), + 13..=18 => (18 - f, true, false), + _ => return None, + } + }; + // Left-quarter field i center x: 4 + i*62 + 30 = 34 + 62i + // Right-quarter field i center x: 4 + 370 + 4 + 20 + 4 + i*62 + 30 = 432 + 62i + let x = if right { 432.0 + qi as f32 * 62.0 } else { 34.0 + qi as f32 * 62.0 }; + // Top row center y: 4 + 90 = 94; bot row: 4 + 180 + 4 + 12 + 4 + 90 = 294 + let y = if top { 94.0 } else { 294.0 }; + Some((x, y)) +} + +/// SVG `` element drawing one arrow (shadow + gold) from `fp` to `tp`. +fn arrow_svg(fp: (f32, f32), tp: (f32, f32)) -> AnyView { + let (x1, y1) = fp; + let (x2, y2) = tp; + let dx = x2 - x1; + let dy = y2 - y1; + let len = (dx * dx + dy * dy).sqrt(); + if len < 10.0 { + return view! { }.into_any(); + } + let nx = dx / len; + let ny = dy / len; + let px = -ny; + let py = nx; + + // Shrink line ends so arrows don't overlap the checker stack + let lx1 = x1 + nx * 20.0; + let ly1 = y1 + ny * 20.0; + let lx2 = x2 - nx * 15.0; + let ly2 = y2 - ny * 15.0; + + // Arrowhead triangle at (x2, y2) + let ah = 15.0_f32; + let aw = 7.0_f32; + let bx = x2 - nx * ah; + let bary = y2 - ny * ah; + let pts = format!( + "{:.1},{:.1} {:.1},{:.1} {:.1},{:.1}", + x2, y2, + bx + px * aw, bary + py * aw, + bx - px * aw, bary - py * aw, + ); + let shadow_pts = format!( + "{:.1},{:.1} {:.1},{:.1} {:.1},{:.1}", + x2, y2, + bx + px * (aw + 1.5), bary + py * (aw + 1.5), + bx - px * (aw + 1.5), bary - py * (aw + 1.5), + ); + + view! { + + // Drop-shadow for readability on coloured fields + + + // Gold arrow + + + + } + .into_any() +} + +/// Valid destinations for a selected origin given already-staged moves. +/// May include 0 (exit); callers handle that case. +fn valid_dests_for(seqs: &[(CheckerMove, CheckerMove)], staged: &[(u8, u8)], origin: u8) -> Vec { + let mut v: Vec = match staged.len() { + 0 => seqs.iter() + .filter(|(m1, _)| m1.get_from() as u8 == origin) + .map(|(m1, _)| m1.get_to() as u8) + .collect(), + 1 => { + let (f0, t0) = staged[0]; + seqs.iter() + .filter(|(m1, m2)| { + m1.get_from() as u8 == f0 + && m1.get_to() as u8 == t0 + && m2.get_from() as u8 == origin + }) + .map(|(_, m2)| m2.get_to() as u8) + .collect() + } + _ => vec![], + }; + v.sort_unstable(); + v.dedup(); + v +} + +#[component] +pub fn Board( + view_state: ViewState, + player_id: u16, + /// Pending origin selection (first click of a move pair). + selected_origin: RwSignal>, + /// Moves staged so far this turn (max 2). Each entry is (from, to), 0 = empty move. + staged_moves: RwSignal>, + /// All valid two-move sequences for this turn (empty when not in move stage). + valid_sequences: Vec<(CheckerMove, CheckerMove)>, +) -> impl IntoView { + let board = view_state.board; + let is_move_stage = view_state.active_mp_player == Some(player_id) + && matches!( + view_state.turn_stage, + SerTurnStage::Move | SerTurnStage::HoldOrGoChoice + ); + let is_white = player_id == 0; + let hovered_moves = use_context::>>(); + + // `valid_sequences` is cloned per field (the Vec is small; Send-safe unlike Rc). + let fields_from = |nums: &[u8], is_top_row: bool| -> Vec { + nums.iter() + .map(|&field_num| { + // Each reactive closure gets its own owned clone — Vec<(CheckerMove,CheckerMove)> + // is Send, which Leptos requires for reactive attribute functions. + let seqs_c = valid_sequences.clone(); + let seqs_k = valid_sequences.clone(); + view! { +
0 } else { val < 0 }; + let can_stage = is_move_stage && staged.len() < 2; + let sel = selected_origin.get(); + + let mut cls = "field".to_string(); + + if seqs_c.is_empty() { + // No restriction (dice not rolled or not move stage) + if can_stage && (sel.is_some() || is_mine) { + cls.push_str(" clickable"); + } + if sel == Some(field_num) { cls.push_str(" selected"); } + if can_stage && sel.is_some() && sel != Some(field_num) { + cls.push_str(" dest"); + } + } else if can_stage { + if let Some(origin) = sel { + if origin == field_num { + cls.push_str(" selected clickable"); + } else { + let dests = valid_dests_for(&seqs_c, &staged, origin); + // Only highlight non-exit destinations (field 0 = exit has no tile) + if dests.iter().any(|&d| d == field_num && d != 0) { + cls.push_str(" clickable dest"); + } + } + } else { + let origins = valid_origins_for(&seqs_c, &staged); + if origins.iter().any(|&o| o == field_num) { + cls.push_str(" clickable"); + } + } + } + + cls + } + on:click=move |_| { + if !is_move_stage { return; } + let staged = staged_moves.get_untracked(); + if staged.len() >= 2 { return; } + + match selected_origin.get_untracked() { + Some(origin) if origin == field_num => { + selected_origin.set(None); + } + Some(origin) => { + let valid = if seqs_k.is_empty() { + true + } else { + valid_dests_for(&seqs_k, &staged, origin) + .iter() + .any(|&d| d == field_num) + }; + if valid { + staged_moves.update(|v| v.push((origin, field_num))); + selected_origin.set(None); + } + } + None => { + if seqs_k.is_empty() { + let val = displayed_value(board, &staged, is_white, field_num); + if is_white && val > 0 || !is_white && val < 0 { + selected_origin.set(Some(field_num)); + } + } else { + let origins = valid_origins_for(&seqs_k, &staged); + if origins.iter().any(|&o| o == field_num) { + let dests = valid_dests_for(&seqs_k, &staged, field_num); + if !dests.is_empty() && dests.iter().all(|&d| d == 0) { + // All destinations are exits: auto-stage + staged_moves.update(|v| v.push((field_num, 0))); + } else { + selected_origin.set(Some(field_num)); + } + } + } + } + } + } + > + {field_num} + {move || { + let moves = staged_moves.get(); + let val = displayed_value(board, &moves, is_white, field_num); + let count = val.unsigned_abs(); + (count > 0).then(|| { + let color = if val > 0 { "white" } else { "black" }; + let display_n = (count as usize).min(4); + // outermost index: last for top rows, first for bottom rows. + let outer_idx = if is_top_row { display_n - 1 } else { 0 }; + let chips: Vec = (0..display_n).map(|i| { + let label = if i == outer_idx && count >= 5 { + count.to_string() + } else { + String::new() + }; + view! { +
{label}
+ }.into_any() + }).collect(); + view! {
{chips}
} + }) + }} +
+ } + .into_any() + }) + .collect() + }; + + let (tl, tr, bl, br) = if is_white { + (&TOP_LEFT_W, &TOP_RIGHT_W, &BOT_LEFT_W, &BOT_RIGHT_W) + } else { + (&TOP_LEFT_B, &TOP_RIGHT_B, &BOT_LEFT_B, &BOT_RIGHT_B) + }; + + view! { +
+
+
{fields_from(tl, true)}
+
+
{fields_from(tr, true)}
+
+
+
+
{fields_from(bl, false)}
+
+
{fields_from(br, false)}
+
+ // SVG overlay: arrows for hovered jan moves + + {move || { + let Some(hm) = hovered_moves else { return vec![]; }; + let pairs = hm.get(); + if pairs.is_empty() { return vec![]; } + // Collect unique individual (from, to) moves; skip empty/exit. + let mut moves: Vec<(usize, usize)> = pairs.iter() + .flat_map(|(m1, m2)| [ + (m1.get_from(), m1.get_to()), + (m2.get_from(), m2.get_to()), + ]) + .filter(|&(f, t)| f != 0 && t != 0) + .collect(); + moves.sort_unstable(); + moves.dedup(); + moves.into_iter() + .filter_map(|(from, to)| { + let p1 = field_center(from, is_white)?; + let p2 = field_center(to, is_white)?; + Some(arrow_svg(p1, p2)) + }) + .collect() + }} + +
+ } +} diff --git a/clients/web/src/game/components/connecting_screen.rs b/client_web/src/components/connecting_screen.rs similarity index 100% rename from clients/web/src/game/components/connecting_screen.rs rename to client_web/src/components/connecting_screen.rs diff --git a/clients/web/src/game/components/die.rs b/client_web/src/components/die.rs similarity index 59% rename from clients/web/src/game/components/die.rs rename to client_web/src/components/die.rs index 7576280..1f83ea9 100644 --- a/clients/web/src/game/components/die.rs +++ b/client_web/src/components/die.rs @@ -16,30 +16,9 @@ fn dot_positions(value: u8) -> &'static [(&'static str, &'static str)] { /// A single die face rendered as SVG. /// `value` 1–6 shows dots; 0 shows an empty face (not-yet-rolled). /// `used` dims the die. -/// `is_double` applies a golden glow (both dice same value). #[component] -pub fn Die( - value: u8, - used: bool, - #[prop(default = false)] is_double: bool, -) -> AnyView { - let mut cls = if used { - "die-face die-used".to_string() - } else { - "die-face".to_string() - }; - if is_double && !used { - cls.push_str(" die-double"); - } - if value == 0 { - return view! { - - - {"?"} - - }.into_any(); - } +pub fn Die(value: u8, used: bool) -> impl IntoView { + let cls = if used { "die-face die-used" } else { "die-face" }; let dots: Vec = dot_positions(value) .iter() .map(|&(cx, cy)| view! { }.into_any()) @@ -49,5 +28,5 @@ pub fn Die( {dots} - }.into_any() + } } diff --git a/client_web/src/components/game_screen.rs b/client_web/src/components/game_screen.rs new file mode 100644 index 0000000..8a43399 --- /dev/null +++ b/client_web/src/components/game_screen.rs @@ -0,0 +1,309 @@ +use futures::channel::mpsc::UnboundedSender; +use leptos::prelude::*; +use trictrac_store::{Board as StoreBoard, CheckerMove, Color, Dice as StoreDice, MoveRules}; + +use crate::app::{GameUiState, NetCommand}; +use crate::i18n::*; +use crate::trictrac::types::{JanEntry, PlayerAction, SerStage, SerTurnStage}; + +use super::board::Board; +use super::die::Die; +use super::score_panel::PlayerScorePanel; + +#[allow(dead_code)] +/// Returns (d0_used, d1_used) by matching each staged move's distance to a die. +fn matched_dice_used(staged: &[(u8, u8)], dice: (u8, u8)) -> (bool, bool) { + let mut d0 = false; + let mut d1 = false; + for &(from, to) in staged { + let dist = if from < to { + to.saturating_sub(from) + } else { + from.saturating_sub(to) + }; + if !d0 && dist == dice.0 { + d0 = true; + } else if !d1 && dist == dice.1 { + d1 = true; + } else if !d0 { + d0 = true; + } else { + d1 = true; + } + } + (d0, d1) +} + +/// Split `dice_jans` into (viewer_jans, opponent_jans). +fn split_jans(dice_jans: &[JanEntry], viewer_is_active: bool) -> (Vec, Vec) { + let mut mine = Vec::new(); + let mut theirs = Vec::new(); + for e in dice_jans { + if viewer_is_active { + if e.total >= 0 { + mine.push(e.clone()); + } else { + theirs.push(JanEntry { + total: -e.total, + points_per: -e.points_per, + ..e.clone() + }); + } + } else if e.total >= 0 { + theirs.push(e.clone()); + } else { + mine.push(JanEntry { + total: -e.total, + points_per: -e.points_per, + ..e.clone() + }); + } + } + (mine, theirs) +} + +#[component] +pub fn GameScreen(state: GameUiState) -> impl IntoView { + let i18n = use_i18n(); + + let vs = state.view_state.clone(); + let player_id = state.player_id; + let is_my_turn = vs.active_mp_player == Some(player_id); + let is_move_stage = is_my_turn + && matches!( + vs.turn_stage, + SerTurnStage::Move | SerTurnStage::HoldOrGoChoice + ); + + // ── Hovered jan moves (shown as arrows on the board) ────────────────────── + let hovered_jan_moves: RwSignal> = RwSignal::new(vec![]); + provide_context(hovered_jan_moves); + + // ── Staged move state ────────────────────────────────────────────────────── + let selected_origin: RwSignal> = RwSignal::new(None); + let staged_moves: RwSignal> = RwSignal::new(Vec::new()); + + let cmd_tx = use_context::>() + .expect("UnboundedSender not found in context"); + let cmd_tx_effect = cmd_tx.clone(); + Effect::new(move |_| { + let moves = staged_moves.get(); + if moves.len() == 2 { + let to_cm = |&(from, to): &(u8, u8)| { + CheckerMove::new(from as usize, to as usize).unwrap_or_default() + }; + cmd_tx_effect + .unbounded_send(NetCommand::Action(PlayerAction::Move( + to_cm(&moves[0]), + to_cm(&moves[1]), + ))) + .ok(); + staged_moves.set(vec![]); + selected_origin.set(None); + } + }); + + let dice = vs.dice; + let show_dice = dice != (0, 0); + + // ── Button senders ───────────────────────────────────────────────────────── + let cmd_tx_roll = cmd_tx.clone(); + let cmd_tx_go = cmd_tx.clone(); + let cmd_tx_quit = cmd_tx.clone(); + let cmd_tx_end_quit = cmd_tx.clone(); + let cmd_tx_end_replay = cmd_tx.clone(); + let show_roll = is_my_turn && vs.turn_stage == SerTurnStage::RollDice; + let show_hold_go = is_my_turn && vs.turn_stage == SerTurnStage::HoldOrGoChoice; + + // ── Valid move sequences for this turn ───────────────────────────────────── + // Computed once per ViewState snapshot; used by Board (highlighting) and the + // empty-move button (visibility). + let valid_sequences: Vec<(CheckerMove, CheckerMove)> = if is_move_stage && dice != (0, 0) { + let mut store_board = StoreBoard::new(); + store_board.set_positions(&Color::White, vs.board); + let store_dice = StoreDice { values: dice }; + let color = if player_id == 0 { Color::White } else { Color::Black }; + let rules = MoveRules::new(&color, &store_board, store_dice); + let raw = rules.get_possible_moves_sequences(true, vec![]); + if player_id == 0 { + raw + } else { + raw.into_iter().map(|(m1, m2)| (m1.mirror(), m2.mirror())).collect() + } + } else { + vec![] + }; + // Clone for the empty-move button reactive closure (Board consumes the original). + let valid_seqs_empty = valid_sequences.clone(); + + // ── Jan split: viewer_jans / opponent_jans ───────────────────────────────── + let (my_jans, opp_jans) = split_jans(&vs.dice_jans, is_my_turn && !show_roll); + + // ── Scores ───────────────────────────────────────────────────────────────── + let my_score = vs.scores[player_id as usize].clone(); + let opp_score = vs.scores[1 - player_id as usize].clone(); + + // ── Capture for closures ─────────────────────────────────────────────────── + let stage = vs.stage.clone(); + let turn_stage = vs.turn_stage.clone(); + let room_id = state.room_id.clone(); + let is_bot_game = state.is_bot_game; + + // ── Game-over info ───────────────────────────────────────────────────────── + let stage_is_ended = stage == SerStage::Ended; + let winner_is_me = my_score.holes >= 12; + let opp_name_end = opp_score.name.clone(); + + view! { +
+ // ── Top bar ────────────────────────────────────────────────────── +
+ {move || if is_bot_game { + t_string!(i18n, vs_bot_label).to_owned() + } else { + t_string!(i18n, room_label, id = room_id.as_str()) + }} +
+ + +
+ {t!(i18n, quit)} +
+ + // ── Opponent score (above board) ───────────────────────────────── + + + // ── Board + side panel ─────────────────────────────────────────── +
+ + + // ── Side panel ─────────────────────────────────────────────── +
+ // Status message +
+ {move || { + let n = staged_moves.get().len(); + if is_move_stage { + t_string!(i18n, select_move, n = n + 1) + } else { + String::from(match (&stage, is_my_turn, &turn_stage) { + (SerStage::Ended, _, _) => t_string!(i18n, game_over), + (SerStage::PreGame, _, _) => t_string!(i18n, waiting_for_opponent), + (SerStage::InGame, true, SerTurnStage::RollDice) => t_string!(i18n, your_turn_roll), + (SerStage::InGame, true, SerTurnStage::HoldOrGoChoice) => t_string!(i18n, hold_or_go), + (SerStage::InGame, true, _) => t_string!(i18n, your_turn), + (SerStage::InGame, false, _) => t_string!(i18n, opponent_turn), + }) + } + }} +
+ + // Dice (always shown when rolled, used state depends on whose turn) + {show_dice.then(|| view! { +
+ {move || { + let (d0, d1) = if is_move_stage { + matched_dice_used(&staged_moves.get(), dice) + } else { + (true, true) + }; + view! { + + + } + }} +
+ })} + + // Action buttons +
+ {show_roll.then(|| view! { + + })} + {show_hold_go.then(|| view! { + + })} + {move || { + // Show the empty-move button only when (0,0) is a valid + // first or second move given what has already been staged. + let staged = staged_moves.get(); + let show = is_move_stage && staged.len() < 2 && ( + valid_seqs_empty.is_empty() || match staged.len() { + 0 => valid_seqs_empty.iter().any(|(m1, _)| m1.get_from() == 0), + 1 => { + let (f0, t0) = staged[0]; + valid_seqs_empty.iter() + .filter(|(m1, _)| { + m1.get_from() as u8 == f0 + && m1.get_to() as u8 == t0 + }) + .any(|(_, m2)| m2.get_from() == 0) + } + _ => false, + } + ); + show.then(|| view! { + + }) + }} +
+
+
+ + // ── Player score (below board) ──────────────────────────────────── + + + // ── Game-over overlay ───────────────────────────────────────────── + {stage_is_ended.then(|| { + let winner_text = if winner_is_me { + t_string!(i18n, you_win).to_owned() + } else { + t_string!(i18n, opp_wins, name = opp_name_end.as_str()) + }; + view! { +
+
+

{t!(i18n, game_over)}

+

{winner_text}

+
+ + {is_bot_game.then(|| view! { + + })} +
+
+
+ } + })} +
+ } +} diff --git a/client_web/src/components/login_screen.rs b/client_web/src/components/login_screen.rs new file mode 100644 index 0000000..91e6d1b --- /dev/null +++ b/client_web/src/components/login_screen.rs @@ -0,0 +1,77 @@ +use futures::channel::mpsc::UnboundedSender; +use leptos::prelude::*; + +use crate::app::NetCommand; +use crate::i18n::*; + +#[component] +pub fn LoginScreen(error: Option) -> impl IntoView { + let i18n = use_i18n(); + let (room_name, set_room_name) = signal(String::new()); + + let cmd_tx = use_context::>() + .expect("UnboundedSender not found in context"); + + let cmd_tx_create = cmd_tx.clone(); + let cmd_tx_join = cmd_tx.clone(); + let cmd_tx_bot = cmd_tx; + + view! { + + } +} diff --git a/clients/web/src/game/components/mod.rs b/client_web/src/components/mod.rs similarity index 74% rename from clients/web/src/game/components/mod.rs rename to client_web/src/components/mod.rs index 4c48cbd..cd5fc33 100644 --- a/clients/web/src/game/components/mod.rs +++ b/client_web/src/components/mod.rs @@ -2,8 +2,9 @@ mod board; mod connecting_screen; mod die; mod game_screen; +mod login_screen; mod score_panel; -mod scoring; pub use connecting_screen::ConnectingScreen; pub use game_screen::GameScreen; +pub use login_screen::LoginScreen; diff --git a/client_web/src/components/score_panel.rs b/client_web/src/components/score_panel.rs new file mode 100644 index 0000000..9045008 --- /dev/null +++ b/client_web/src/components/score_panel.rs @@ -0,0 +1,156 @@ +use leptos::prelude::*; +use trictrac_store::{CheckerMove, Jan}; + +use crate::i18n::*; +use crate::trictrac::types::{JanEntry, PlayerScore}; + +fn jan_label(jan: &Jan) -> String { + let i18n = use_i18n(); + match jan { + Jan::FilledQuarter => t_string!(i18n, jan_filled_quarter).to_owned(), + Jan::TrueHitSmallJan => t_string!(i18n, jan_true_hit_small).to_owned(), + Jan::TrueHitBigJan => t_string!(i18n, jan_true_hit_big).to_owned(), + Jan::TrueHitOpponentCorner => t_string!(i18n, jan_true_hit_corner).to_owned(), + Jan::FirstPlayerToExit => t_string!(i18n, jan_first_exit).to_owned(), + Jan::SixTables => t_string!(i18n, jan_six_tables).to_owned(), + Jan::TwoTables => t_string!(i18n, jan_two_tables).to_owned(), + Jan::Mezeas => t_string!(i18n, jan_mezeas).to_owned(), + Jan::FalseHitSmallJan => t_string!(i18n, jan_false_hit_small).to_owned(), + Jan::FalseHitBigJan => t_string!(i18n, jan_false_hit_big).to_owned(), + Jan::ContreTwoTables => t_string!(i18n, jan_contre_two).to_owned(), + Jan::ContreMezeas => t_string!(i18n, jan_contre_mezeas).to_owned(), + Jan::HelplessMan => t_string!(i18n, jan_helpless_man).to_owned(), + } +} + +fn format_move_pair(m1: CheckerMove, m2: CheckerMove) -> String { + let fmt = |m: CheckerMove| -> String { + let (f, t) = (m.get_from(), m.get_to()); + if f == 0 && t == 0 { + "—".to_string() + } else if t == 0 { + format!("{f}↑") + } else { + format!("{f}→{t}") + } + }; + format!("{} & {}", fmt(m1), fmt(m2)) +} + +fn jan_row(idx: usize, entry: JanEntry, expanded: RwSignal>) -> impl IntoView { + let i18n = use_i18n(); + let row_class = if entry.total >= 0 { + "jan-row jan-expandable jan-positive" + } else { + "jan-row jan-expandable jan-negative" + }; + let label = jan_label(&entry.jan); + let double_tag = if entry.is_double { + t_string!(i18n, jan_double).to_owned() + } else { + t_string!(i18n, jan_simple).to_owned() + }; + let ways_tag = format!("×{}", entry.ways); + let pts_str = if entry.total >= 0 { + format!("+{}", entry.total) + } else { + format!("{}", entry.total) + }; + + let moves = entry.moves.clone(); + let moves_hover = entry.moves.clone(); + // RwSignal is Copy so it can be captured by both closures independently. + let hovered = use_context::>>(); + + view! { +
+
+ {label} + {double_tag} + {ways_tag} + {pts_str} +
+ { + let move_lines: Vec<_> = moves.iter() + .map(|&(m1, m2)| { + let text = format_move_pair(m1, m2); + view! {
{text}
} + }) + .collect(); + view! { +
+ {move_lines} +
+ } + } +
+ } +} + +#[component] +pub fn PlayerScorePanel(score: PlayerScore, jans: Vec, is_you: bool) -> impl IntoView { + let i18n = use_i18n(); + + let points_pct = format!("{}%", (score.points as u32 * 100 / 12).min(100)); + let holes_pct = format!("{}%", (score.holes as u32 * 100 / 12).min(100)); + let points_val = format!("{}/12", score.points); + let holes_val = format!("{}/12", score.holes); + let can_bredouille = score.can_bredouille; + + let expanded: RwSignal> = RwSignal::new(None); + let jan_rows: Vec<_> = jans + .into_iter() + .enumerate() + .map(|(i, entry)| jan_row(i, entry, expanded)) + .collect(); + + view! { +
+
+ + {score.name} + {is_you.then(|| t!(i18n, you_suffix))} + +
+
+
+ {t!(i18n, points_label)} +
+
+
+ {points_val} + {can_bredouille.then(|| view! { + "B" + })} +
+
+ {t!(i18n, holes_label)} +
+
+
+ {holes_val} +
+
+ {(!jan_rows.is_empty()).then(|| view! { +
{jan_rows}
+ })} +
+ } +} diff --git a/client_web/src/main.rs b/client_web/src/main.rs new file mode 100644 index 0000000..209ae60 --- /dev/null +++ b/client_web/src/main.rs @@ -0,0 +1,12 @@ +leptos_i18n::load_locales!(); + +mod app; +mod components; +mod trictrac; + +use app::App; +use leptos::prelude::*; + +fn main() { + mount_to_body(|| view! { }) +} diff --git a/clients/web/src/game/trictrac/backend.rs b/client_web/src/trictrac/backend.rs similarity index 50% rename from clients/web/src/game/trictrac/backend.rs rename to client_web/src/trictrac/backend.rs index 53c14cc..e96f080 100644 --- a/clients/web/src/game/trictrac/backend.rs +++ b/client_web/src/trictrac/backend.rs @@ -1,7 +1,7 @@ use backbone_lib::traits::{BackEndArchitecture, BackendCommand}; -use trictrac_store::{Color, Dice, DiceRoller, GameEvent, GameState, Player, Stage, TurnStage}; +use trictrac_store::{DiceRoller, GameEvent, GameState, TurnStage}; -use super::types::{GameDelta, PlayerAction, PreGameRollState, SerStage, SerTurnStage, ViewState}; +use crate::trictrac::types::{GameDelta, PlayerAction, ViewState}; // Store PlayerId (u64) values used for the two players. const HOST_PLAYER_ID: u64 = 1; @@ -14,28 +14,11 @@ pub struct TrictracBackend { view_state: ViewState, /// Arrival flags: have host (index 0) and guest (index 1) joined? arrived: [bool; 2], - /// Die rolled by each player during the ceremony ([host, guest]). - pre_game_dice: [Option; 2], - /// Number of tied rounds so far. - tie_count: u8, - /// True while the first-player ceremony is running. - ceremony_started: bool, } impl TrictracBackend { fn sync_view_state(&mut self) { - let mut vs = ViewState::from_game_state(&self.game, HOST_PLAYER_ID, GUEST_PLAYER_ID); - if self.ceremony_started { - vs.stage = SerStage::PreGameRoll; - vs.pre_game_roll = Some(PreGameRollState { - host_die: self.pre_game_dice[0], - guest_die: self.pre_game_dice[1], - tie_count: self.tie_count, - }); - // Both players roll independently; no single "active" player. - vs.active_mp_player = None; - } - self.view_state = vs; + self.view_state = ViewState::from_game_state(&self.game, HOST_PLAYER_ID, GUEST_PLAYER_ID); } fn broadcast_state(&mut self) { @@ -46,49 +29,6 @@ impl TrictracBackend { self.commands.push(BackendCommand::Delta(delta)); } - /// Process one ceremony die-roll for `mp_player` (0 = host, 1 = guest). - fn handle_pre_game_roll(&mut self, mp_player: u16) { - let idx = mp_player as usize; - // Ignore if this player already rolled. - if self.pre_game_dice[idx].is_some() { - return; - } - let single = self.dice_roller.roll().values.0; - self.pre_game_dice[idx] = Some(single); - - if let [Some(h), Some(g)] = self.pre_game_dice { - // Both have rolled — broadcast both dice before resolving. - self.broadcast_state(); - if h == g { - // Tie: reset for another round. - self.tie_count += 1; - self.pre_game_dice = [None; 2]; - self.broadcast_state(); - } else { - // Highest die goes first. - let goes_first = if h > g { - HOST_PLAYER_ID - } else { - GUEST_PLAYER_ID - }; - self.ceremony_started = false; - let _ = self.game.consume(&GameEvent::BeginGame { goes_first }); - // Use pre-game dice roll for the first move - let _ = self.game.consume(&GameEvent::Roll { - player_id: goes_first, - }); - let _ = self.game.consume(&GameEvent::RollResult { - player_id: goes_first, - dice: Dice { values: (g, h) }, - }); - self.broadcast_state(); - } - } else { - // Only one die rolled so far — broadcast the partial result. - self.broadcast_state(); - } - } - /// Roll dice using the store's DiceRoller and fire Roll + RollResult events. fn do_roll(&mut self) { let dice = self.dice_roller.roll(); @@ -130,72 +70,13 @@ impl TrictracBackend { pub fn get_game(&self) -> &GameState { &self.game } - - /// Build a backend pre-loaded with the given `ViewState` snapshot so a bot - /// game can resume from an arbitrary position (debug feature). - pub fn from_view_state(vs: ViewState, player_name: &str) -> Self { - let mut game = GameState::new(false); - - game.board.set_positions(&Color::White, vs.board); - - game.stage = match vs.stage { - SerStage::InGame => Stage::InGame, - SerStage::Ended => Stage::Ended, - _ => Stage::InGame, - }; - - game.turn_stage = match vs.turn_stage { - SerTurnStage::RollDice => TurnStage::RollDice, - SerTurnStage::RollWaiting => TurnStage::RollWaiting, - SerTurnStage::MarkPoints => TurnStage::MarkPoints, - SerTurnStage::HoldOrGoChoice => TurnStage::HoldOrGoChoice, - SerTurnStage::Move => TurnStage::Move, - SerTurnStage::MarkAdvPoints => TurnStage::MarkAdvPoints, - }; - - game.dice = Dice { values: vs.dice }; - - game.active_player_id = match vs.active_mp_player { - Some(0) => HOST_PLAYER_ID, - Some(1) => GUEST_PLAYER_ID, - _ => HOST_PLAYER_ID, - }; - - let build_player = |score: &crate::game::trictrac::types::PlayerScore, - color: Color| - -> Player { - let mut p = Player::new(score.name.clone(), color); - p.points = score.points; - p.holes = score.holes; - p.can_bredouille = score.can_bredouille; - p - }; - - game.players.insert(HOST_PLAYER_ID, build_player(&vs.scores[0], Color::White)); - game.players.insert(GUEST_PLAYER_ID, build_player(&vs.scores[1], Color::Black)); - - let mut view_state = ViewState::from_game_state(&game, HOST_PLAYER_ID, GUEST_PLAYER_ID); - view_state.scores[0].name = player_name.to_string(); - view_state.scores[1].name = "Bot".to_string(); - - TrictracBackend { - game, - dice_roller: DiceRoller::default(), - commands: Vec::new(), - view_state, - arrived: [true, true], - pre_game_dice: [None; 2], - tie_count: 0, - ceremony_started: false, - } - } } impl BackEndArchitecture for TrictracBackend { fn new(_rule_variation: u16) -> Self { let mut game = GameState::new(false); - game.init_player("Blancs"); - game.init_player("Noirs"); + game.init_player("Host"); + game.init_player("Guest"); let view_state = ViewState::from_game_state(&game, HOST_PLAYER_ID, GUEST_PLAYER_ID); @@ -205,9 +86,6 @@ impl BackEndArchitecture for TrictracBackend commands: Vec::new(), view_state, arrived: [false; 2], - pre_game_dice: [None; 2], - tie_count: 0, - ceremony_started: false, } } @@ -232,15 +110,11 @@ impl BackEndArchitecture for TrictracBackend timer_id: mp_player, }); - // Start the ceremony once both players have arrived. - if self.arrived[0] - && self.arrived[1] - && self.game.stage == trictrac_store::Stage::PreGame - && !self.ceremony_started - { - self.ceremony_started = true; - self.pre_game_dice = [None; 2]; - self.tie_count = 0; + // Start the game once both players have arrived. + if self.arrived[0] && self.arrived[1] && self.game.stage == trictrac_store::Stage::PreGame { + let _ = self.game.consume(&GameEvent::BeginGame { + goes_first: HOST_PLAYER_ID, + }); self.sync_view_state(); self.commands.push(BackendCommand::ResetViewState); } else { @@ -261,24 +135,6 @@ impl BackEndArchitecture for TrictracBackend } fn inform_rpc(&mut self, mp_player: u16, action: PlayerAction) { - // SetName is always accepted regardless of game stage or whose turn it is. - if let PlayerAction::SetName(name) = action { - let store_id = if mp_player == 0 { HOST_PLAYER_ID } else { GUEST_PLAYER_ID }; - if let Some(p) = self.game.players.get_mut(&store_id) { - p.name = name; - } - self.broadcast_state(); - return; - } - - // During the first-player ceremony only PreGameRoll actions are accepted. - if self.ceremony_started { - if matches!(action, PlayerAction::PreGameRoll) { - self.handle_pre_game_roll(mp_player); - } - return; - } - if self.game.stage == trictrac_store::Stage::Ended { return; } @@ -330,8 +186,6 @@ impl BackEndArchitecture for TrictracBackend self.drive_automatic_stages(); } } - PlayerAction::PreGameRoll => {} // ignored outside ceremony - PlayerAction::SetName(_) => {} // handled at the top of inform_rpc } self.broadcast_state(); @@ -359,7 +213,6 @@ impl BackEndArchitecture for TrictracBackend #[cfg(test)] mod tests { use super::*; - use super::{SerStage, SerTurnStage}; use backbone_lib::traits::BackEndArchitecture; fn make_backend() -> TrictracBackend { @@ -378,37 +231,15 @@ mod tests { .collect() } - /// Drive the ceremony to completion (both players roll until one wins). - fn complete_ceremony(b: &mut TrictracBackend) { - loop { - if b.get_view_state().stage != SerStage::PreGameRoll { - break; - } - let pgr = b.get_view_state().pre_game_roll.clone().unwrap_or_default(); - let host_needs = pgr.host_die.is_none(); - let guest_needs = pgr.guest_die.is_none(); - if !host_needs && !guest_needs { - break; // both rolled but stage not yet resolved — shouldn't happen - } - if host_needs { - b.inform_rpc(0, PlayerAction::PreGameRoll); - } - if guest_needs { - b.inform_rpc(1, PlayerAction::PreGameRoll); - } - b.drain_commands(); - } - } - #[test] - fn both_players_arrive_starts_ceremony() { + fn both_players_arrive_starts_game() { let mut b = make_backend(); b.player_arrival(0); // host b.drain_commands(); b.player_arrival(1); // guest let cmds = b.drain_commands(); - // ResetViewState should have been issued to start the ceremony. + // ResetViewState should have been issued after BeginGame. let has_reset = cmds .iter() .any(|c| matches!(c, BackendCommand::ResetViewState)); @@ -417,44 +248,11 @@ mod tests { "expected ResetViewState after both players arrive" ); - // Stage should now be PreGameRoll, not InGame. - assert_eq!(b.get_view_state().stage, SerStage::PreGameRoll); - } - - #[test] - fn ceremony_resolves_to_in_game() { - let mut b = make_backend(); - b.player_arrival(0); - b.player_arrival(1); - b.drain_commands(); - - complete_ceremony(&mut b); - + // Game should now be InGame. + use crate::trictrac::types::SerStage; assert_eq!(b.get_view_state().stage, SerStage::InGame); } - #[test] - fn ceremony_any_order_allowed() { - let mut b = make_backend(); - b.player_arrival(0); - b.player_arrival(1); - b.drain_commands(); - - // Guest may roll before host. - b.inform_rpc(1, PlayerAction::PreGameRoll); - let states = drain_deltas(&mut b); - assert!( - !states.is_empty(), - "guest PreGameRoll should broadcast a state" - ); - let pgr = states.last().unwrap().pre_game_roll.as_ref().unwrap(); - assert!( - pgr.guest_die.is_some(), - "guest die should be set after guest rolls" - ); - assert!(pgr.host_die.is_none(), "host die should still be blank"); - } - #[test] fn unknown_player_kicked() { let mut b = make_backend(); @@ -472,18 +270,12 @@ mod tests { b.player_arrival(1); b.drain_commands(); - // Complete ceremony before rolling. - complete_ceremony(&mut b); - - // Roll for whoever won the ceremony (either player could go first). - let first_player = b - .get_view_state() - .active_mp_player - .expect("someone should be active"); - b.inform_rpc(first_player, PlayerAction::Roll); + // Host rolls (player_id 0, whose store id == HOST_PLAYER_ID == active after BeginGame). + b.inform_rpc(0, PlayerAction::Roll); let states = drain_deltas(&mut b); assert!(!states.is_empty(), "expected a state broadcast after roll"); + use crate::trictrac::types::SerTurnStage; let last = states.last().unwrap(); assert!( matches!( @@ -504,14 +296,14 @@ mod tests { b.player_arrival(0); b.player_arrival(1); b.drain_commands(); - complete_ceremony(&mut b); - // Identify who goes first and have the OTHER player try to roll. - let active = b.get_view_state().active_mp_player; - let wrong_player = if active == Some(0) { 1u16 } else { 0u16 }; - b.inform_rpc(wrong_player, PlayerAction::Roll); + // Guest tries to roll when it's the host's turn. + b.inform_rpc(1, PlayerAction::Roll); let cmds = b.drain_commands(); - assert!(cmds.is_empty(), "wrong player roll should be ignored"); + assert!( + cmds.is_empty(), + "guest roll should be ignored when it's host's turn" + ); } #[test] @@ -538,20 +330,3 @@ mod tests { .any(|c| matches!(c, BackendCommand::TerminateRoom))); } } - -// ── Public API: WASM delegates to `inner`, other targets are no-ops ─────────── - -#[cfg(target_arch = "wasm32")] -mod inner { - use web_sys::console; - - pub fn console_log(message: String) { - console::log_1(&message.into()); - } -} - -#[cfg(target_arch = "wasm32")] -pub use inner::console_log; - -#[cfg(not(target_arch = "wasm32"))] -pub fn console_log(message: String) {} diff --git a/client_web/src/trictrac/bot_local.rs b/client_web/src/trictrac/bot_local.rs new file mode 100644 index 0000000..8941a09 --- /dev/null +++ b/client_web/src/trictrac/bot_local.rs @@ -0,0 +1,33 @@ +use rand::prelude::IndexedRandom; +use trictrac_store::{CheckerMove, Color, GameState, MoveRules, Stage, TurnStage}; + +use crate::trictrac::types::PlayerAction; + +const GUEST_PLAYER_ID: u64 = 2; + +/// Returns the next action for the bot (mp_player 1 / guest), or None if it is not the bot's turn. +pub fn bot_decide(game: &GameState) -> Option { + if game.stage == Stage::Ended { + return None; + } + if game.active_player_id != GUEST_PLAYER_ID { + return None; + } + match game.turn_stage { + TurnStage::RollDice => Some(PlayerAction::Roll), + TurnStage::HoldOrGoChoice => Some(PlayerAction::Go), + TurnStage::Move => { + let rules = MoveRules::new(&Color::Black, &game.board, game.dice); + let sequences = rules.get_possible_moves_sequences(true, vec![]); + let mut rng = rand::rng(); + let (m1, m2) = sequences + .choose(&mut rng) + .cloned() + .unwrap_or((CheckerMove::default(), CheckerMove::default())); + // MoveRules with Color::Black mirrors the board internally, so + // returned move coordinates are in mirrored (White) space — mirror back. + Some(PlayerAction::Move(m1.mirror(), m2.mirror())) + } + _ => None, + } +} diff --git a/clients/web/src/game/trictrac/mod.rs b/client_web/src/trictrac/mod.rs similarity index 100% rename from clients/web/src/game/trictrac/mod.rs rename to client_web/src/trictrac/mod.rs diff --git a/clients/web/src/game/trictrac/types.rs b/client_web/src/trictrac/types.rs similarity index 66% rename from clients/web/src/game/trictrac/types.rs rename to client_web/src/trictrac/types.rs index 45ac48c..02e2675 100644 --- a/clients/web/src/game/trictrac/types.rs +++ b/client_web/src/trictrac/types.rs @@ -14,10 +14,6 @@ pub enum PlayerAction { Go, /// Acknowledge point marking (hold / advance points). Mark, - /// Roll a single die during the pre-game ceremony to decide who goes first. - PreGameRoll, - /// Declare the player's display name; sent once immediately after connecting. - SetName(String), } // ── Incremental state update broadcast to all clients ──────────────────────── @@ -31,18 +27,6 @@ pub struct GameDelta { // ── Full game snapshot ──────────────────────────────────────────────────────── -/// State of the pre-game ceremony where each player rolls one die to decide -/// who goes first. Present only when `stage == SerStage::PreGameRoll`. -#[derive(Clone, Default, PartialEq, Serialize, Deserialize)] -pub struct PreGameRollState { - /// Die value (1–6) rolled by the host; `None` = not yet rolled this round. - pub host_die: Option, - /// Die value (1–6) rolled by the guest; `None` = not yet rolled this round. - pub guest_die: Option, - /// Number of tied rounds so far (0 on the first round). - pub tie_count: u8, -} - #[derive(Clone, PartialEq, Serialize, Deserialize)] pub struct ViewState { /// Board positions: index i = field i+1. Positive = white, negative = black. @@ -57,11 +41,6 @@ pub struct ViewState { pub dice: (u8, u8), /// Jans (scoring events) triggered by the last dice roll. pub dice_jans: Vec, - /// Last two checker moves played; default when no move has occurred yet. - pub dice_moves: (CheckerMove, CheckerMove), - /// Present while the pre-game ceremony is in progress. - #[serde(default)] - pub pre_game_roll: Option, } /// One scoring event from a dice roll. @@ -89,23 +68,11 @@ impl ViewState { turn_stage: SerTurnStage::RollDice, active_mp_player: None, scores: [ - PlayerScore { - name: host_name.to_string(), - points: 0, - holes: 0, - can_bredouille: false, - }, - PlayerScore { - name: guest_name.to_string(), - points: 0, - holes: 0, - can_bredouille: false, - }, + PlayerScore { name: host_name.to_string(), points: 0, holes: 0, can_bredouille: false }, + PlayerScore { name: guest_name.to_string(), points: 0, holes: 0, can_bredouille: false }, ], dice: (0, 0), dice_jans: Vec::new(), - dice_moves: (CheckerMove::default(), CheckerMove::default()), - pre_game_roll: None, } } @@ -116,21 +83,25 @@ impl ViewState { /// Convert a store `GameState` to a `ViewState`. /// `host_store_id` and `guest_store_id` are the trictrac `PlayerId`s assigned /// to the host (mp player 0) and guest (mp player 1) respectively. - pub fn from_game_state(gs: &GameState, host_store_id: u64, guest_store_id: u64) -> Self { + pub fn from_game_state( + gs: &GameState, + host_store_id: u64, + guest_store_id: u64, + ) -> Self { let board_vec = gs.board.to_vec(); let board: [i8; 24] = board_vec.try_into().expect("board is always 24 fields"); let stage = match gs.stage { Stage::PreGame => SerStage::PreGame, - Stage::InGame => SerStage::InGame, - Stage::Ended => SerStage::Ended, + Stage::InGame => SerStage::InGame, + Stage::Ended => SerStage::Ended, }; let turn_stage = match gs.turn_stage { - TurnStage::RollDice => SerTurnStage::RollDice, - TurnStage::RollWaiting => SerTurnStage::RollWaiting, - TurnStage::MarkPoints => SerTurnStage::MarkPoints, + TurnStage::RollDice => SerTurnStage::RollDice, + TurnStage::RollWaiting => SerTurnStage::RollWaiting, + TurnStage::MarkPoints => SerTurnStage::MarkPoints, TurnStage::HoldOrGoChoice => SerTurnStage::HoldOrGoChoice, - TurnStage::Move => SerTurnStage::Move, + TurnStage::Move => SerTurnStage::Move, TurnStage::MarkAdvPoints => SerTurnStage::MarkAdvPoints, }; @@ -151,12 +122,7 @@ impl ViewState { holes: p.holes, can_bredouille: p.can_bredouille, }) - .unwrap_or_else(|| PlayerScore { - name: String::new(), - points: 0, - holes: 0, - can_bredouille: false, - }) + .unwrap_or_else(|| PlayerScore { name: String::new(), points: 0, holes: 0, can_bredouille: false }) }; // is_double for scoring: dice show the same value (both dice identical). @@ -165,16 +131,13 @@ impl ViewState { // Build JanEntry list from the PossibleJans map. let empty_move = CheckerMove::new(0, 0).unwrap_or_default(); - let mut dice_jans: Vec = gs - .dice_jans + let mut dice_jans: Vec = gs.dice_jans .iter() .map(|(jan, moves)| { // HelplessMan: is_double = true only when *both* dice are unplayable // (the moves list contains a single (empty, empty) sentinel). let is_double = if *jan == Jan::HelplessMan { - moves - .first() - .map(|&(m1, m2)| m1 == empty_move && m2 == empty_move) + moves.first().map(|&(m1, m2)| m1 == empty_move && m2 == empty_move) .unwrap_or(false) } else { dice_are_double @@ -203,29 +166,10 @@ impl ViewState { scores: [score_for(host_store_id), score_for(guest_store_id)], dice: (gs.dice.values.0, gs.dice.values.1), dice_jans, - dice_moves: gs.dice_moves, - pre_game_roll: None, } } } -// ── Scored event (notification) ────────────────────────────────────────── - -/// Points scored in a single scoring event, used for the notification panel. -#[derive(Clone, PartialEq)] -pub struct ScoredEvent { - /// Raw points earned (sum of jan values; before hole wrapping). - pub points_earned: u8, - /// Number of holes gained (0 = no hole). - pub holes_gained: u8, - /// Total holes after this event. - pub holes_total: u8, - /// Was bredouille active when the hole was made (doubles hole count)? - pub bredouille: bool, - /// Contributing jans from this player's perspective (totals always positive). - pub jans: Vec, -} - // ── Score snapshot ──────────────────────────────────────────────────────────── #[derive(Clone, PartialEq, Serialize, Deserialize)] @@ -241,8 +185,6 @@ pub struct PlayerScore { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum SerStage { PreGame, - /// Both players have arrived; ceremony in progress to decide who goes first. - PreGameRoll, InGame, Ended, } diff --git a/clients/backbone-lib/Cargo.toml b/clients/backbone-lib/Cargo.toml deleted file mode 100644 index 1e57d93..0000000 --- a/clients/backbone-lib/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "backbone-lib" -version = "0.1.0" -edition = "2024" - -[dependencies] -serde = { version = "1.0", features = ["derive"] } -postcard = { version = "1.1", features = ["use-std"] } -bytes = "1.11" -ewebsock = "0.8" -protocol = { path = "../../server/protocol" } -futures = "0.3" -web-time = "1.1" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen-futures = "0.4" -gloo-timers = { version = "0.3", features = ["futures"] } diff --git a/clients/backbone-lib/src/client.rs b/clients/backbone-lib/src/client.rs deleted file mode 100644 index 65c7fdb..0000000 --- a/clients/backbone-lib/src/client.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Background task for the client (non-host) side of a session. - -use ewebsock::{WsEvent, WsMessage, WsReceiver, WsSender}; -use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; - -use crate::platform::sleep_ms; -use crate::protocol::{parse_client_update, send_disconnect, send_rpc}; -use crate::session::{BackendMsg, SessionEvent}; -use crate::traits::SerializationCap; - -pub(crate) async fn client_loop( - mut ws_sender: WsSender, - ws_receiver: WsReceiver, - mut action_rx: UnboundedReceiver>, - event_tx: UnboundedSender>, -) where - A: SerializationCap, - D: SerializationCap, - VS: SerializationCap, -{ - loop { - // 1. Drain outbound actions. - loop { - match action_rx.try_next() { - Ok(Some(BackendMsg::Action(action))) => { - send_rpc(&mut ws_sender, &action); - } - Ok(Some(BackendMsg::Disconnect)) => { - send_disconnect(&mut ws_sender, false); - event_tx - .unbounded_send(SessionEvent::Disconnected(None)) - .ok(); - return; - } - Ok(None) => { - send_disconnect(&mut ws_sender, false); - return; - } - Err(_) => break, - } - } - - // 2. Drain inbound state updates. - loop { - match ws_receiver.try_recv() { - Some(WsEvent::Message(WsMessage::Binary(data))) => { - match parse_client_update::(data) { - Ok(updates) => { - for u in updates { - event_tx - .unbounded_send(SessionEvent::Update(u)) - .ok(); - } - } - Err(e) => { - event_tx - .unbounded_send(SessionEvent::Disconnected(Some(e))) - .ok(); - return; - } - } - } - Some(WsEvent::Closed) => { - event_tx - .unbounded_send(SessionEvent::Disconnected(Some( - "Connection closed".to_string(), - ))) - .ok(); - return; - } - Some(WsEvent::Error(e)) => { - event_tx - .unbounded_send(SessionEvent::Disconnected(Some(e))) - .ok(); - return; - } - Some(_) => continue, - None => break, - } - } - - sleep_ms(2).await; - } -} diff --git a/clients/backbone-lib/src/host.rs b/clients/backbone-lib/src/host.rs deleted file mode 100644 index c78e228..0000000 --- a/clients/backbone-lib/src/host.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! Background task for the host (game server) side of a session. - -use std::collections::HashSet; - -use ewebsock::{WsEvent, WsMessage, WsReceiver, WsSender}; -use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; -use web_time::{Duration, Instant}; - -use crate::platform::sleep_ms; -use crate::protocol::{ - ToServerCommand, parse_server_command, send_delta, send_disconnect, send_full_state, - send_kick, send_reset, -}; -use crate::session::{BackendMsg, SessionEvent}; -use crate::traits::{BackEndArchitecture, BackendCommand, SerializationCap, ViewStateUpdate}; - -struct Timer { - id: u16, - fire_at: Instant, -} - -pub(crate) async fn host_loop( - mut ws_sender: WsSender, - ws_receiver: WsReceiver, - mut action_rx: UnboundedReceiver>, - event_tx: UnboundedSender>, - rule_variation: u16, - host_state: Option>, -) where - A: SerializationCap, - D: SerializationCap + Clone, - VS: SerializationCap + Clone, - Backend: BackEndArchitecture, -{ - let mut backend = host_state - .as_deref() - .and_then(|b| Backend::from_bytes(rule_variation, b)) - .unwrap_or_else(|| Backend::new(rule_variation)); - backend.player_arrival(0); - - // Push initial state to UI immediately. - let initial = backend.get_view_state().clone(); - event_tx - .unbounded_send(SessionEvent::Update(ViewStateUpdate::Full(initial))) - .ok(); - - let mut timers: Vec = Vec::new(); - let mut cancelled_timers: HashSet = HashSet::new(); - let mut remote_player_count: u16 = 0; - - loop { - let mut client_joined = false; - - // 1. Drain local actions / detect session drop or disconnect request. - loop { - match action_rx.try_next() { - Ok(Some(BackendMsg::Action(action))) => { - backend.inform_rpc(0, action); - } - Ok(Some(BackendMsg::Disconnect)) => { - send_disconnect(&mut ws_sender, true); - event_tx - .unbounded_send(SessionEvent::Disconnected(None)) - .ok(); - return; - } - Ok(None) => { - // All senders dropped — session was dropped without calling disconnect(). - send_disconnect(&mut ws_sender, true); - return; - } - Err(_) => break, // Channel empty; nothing pending. - } - } - - // 2. Drain WebSocket events from the relay. - loop { - match ws_receiver.try_recv() { - Some(WsEvent::Message(WsMessage::Binary(data))) => { - match parse_server_command::(data) { - ToServerCommand::ClientJoin(id) => { - backend.player_arrival(id); - remote_player_count += 1; - client_joined = true; - } - ToServerCommand::ClientLeft(id) => { - backend.player_departure(id); - remote_player_count = remote_player_count.saturating_sub(1); - } - ToServerCommand::Rpc(id, payload) => { - backend.inform_rpc(id, payload); - } - ToServerCommand::Error(e) => { - event_tx - .unbounded_send(SessionEvent::Disconnected(Some(e))) - .ok(); - return; - } - } - } - Some(WsEvent::Closed) => { - event_tx - .unbounded_send(SessionEvent::Disconnected(Some( - "Connection closed".to_string(), - ))) - .ok(); - return; - } - Some(WsEvent::Error(e)) => { - event_tx - .unbounded_send(SessionEvent::Disconnected(Some(e))) - .ok(); - return; - } - Some(_) => continue, // Ignore Opened / text messages. - None => break, // No more events this iteration. - } - } - - // 3. Fire elapsed timers. - let now = Instant::now(); - let mut fired = Vec::new(); - timers.retain(|t| { - if t.fire_at <= now { - fired.push(t.id); - false - } else { - true - } - }); - for id in fired { - if !cancelled_timers.remove(&id) { - backend.timer_triggered(id); - } - } - - // 4. Drain and process backend commands. - let commands = backend.drain_commands(); - - if commands.is_empty() && !client_joined { - sleep_ms(2).await; - continue; - } - - let mut delta_batch: Vec = Vec::new(); - let mut reset = false; - - for cmd in commands { - match cmd { - BackendCommand::TerminateRoom => { - send_disconnect(&mut ws_sender, true); - event_tx - .unbounded_send(SessionEvent::Disconnected(None)) - .ok(); - return; - } - BackendCommand::SetTimer { timer_id, duration } => { - // Cancel any existing timer with the same id, then re-arm. - timers.retain(|t| t.id != timer_id); - cancelled_timers.remove(&timer_id); - timers.push(Timer { - id: timer_id, - fire_at: Instant::now() + Duration::from_secs_f32(duration), - }); - } - BackendCommand::CancelTimer { timer_id } => { - cancelled_timers.insert(timer_id); - } - BackendCommand::KickPlayer { player } => { - if remote_player_count > 0 { - send_kick(&mut ws_sender, player); - } - } - BackendCommand::ResetViewState => { - reset = true; - } - BackendCommand::Delta(d) => { - delta_batch.push(d); - } - } - } - - if reset { - // Reset supersedes all pending deltas: send fresh full state. - let state = backend.get_view_state().clone(); - if remote_player_count > 0 { - send_reset(&mut ws_sender, &state); - } - event_tx - .unbounded_send(SessionEvent::Update(ViewStateUpdate::Full(state))) - .ok(); - } else { - // Broadcast deltas, then notify local UI. - if remote_player_count > 0 && !delta_batch.is_empty() { - send_delta(&mut ws_sender, &delta_batch); - } - for d in delta_batch { - event_tx - .unbounded_send(SessionEvent::Update(ViewStateUpdate::Incremental(d))) - .ok(); - } - } - - // Send full state to clients that joined this iteration. - if client_joined { - send_full_state(&mut ws_sender, backend.get_view_state()); - } - - sleep_ms(2).await; - } -} diff --git a/clients/backbone-lib/src/lib.rs b/clients/backbone-lib/src/lib.rs deleted file mode 100644 index d67a96c..0000000 --- a/clients/backbone-lib/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod session; -pub mod traits; - -mod client; -mod host; -mod platform; -mod protocol; - -pub use session::{ConnectError, GameSession, RoomConfig, RoomRole, SessionEvent}; -pub use traits::{BackEndArchitecture, BackendCommand, SerializationCap, ViewStateUpdate}; diff --git a/clients/backbone-lib/src/platform.rs b/clients/backbone-lib/src/platform.rs deleted file mode 100644 index 92f2414..0000000 --- a/clients/backbone-lib/src/platform.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::future::Future; - -/// Spawns a background task. -/// - WASM: uses `wasm_bindgen_futures::spawn_local` (no Send required) -/// - Native: spawns an OS thread running `futures::executor::block_on` -#[cfg(target_arch = "wasm32")] -pub fn spawn_task(fut: F) -where - F: Future + 'static, -{ - wasm_bindgen_futures::spawn_local(fut); -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn spawn_task(fut: F) -where - F: Future + Send + 'static, -{ - std::thread::spawn(move || { - futures::executor::block_on(fut); - }); -} - -/// Yields for approximately `ms` milliseconds. -/// - WASM: non-blocking yield via browser timer -/// - Native: blocks the current thread (safe on a dedicated background thread) -#[cfg(target_arch = "wasm32")] -pub async fn sleep_ms(ms: u32) { - gloo_timers::future::TimeoutFuture::new(ms).await; -} - -#[cfg(not(target_arch = "wasm32"))] -pub async fn sleep_ms(ms: u32) { - std::thread::sleep(std::time::Duration::from_millis(ms as u64)); -} - -/// Platform-agnostic bound for types that can be moved into a background task. -/// - WASM: only requires `'static` (single-threaded, no Send needed) -/// - Native: requires `Send + 'static` -#[cfg(target_arch = "wasm32")] -pub trait TaskBound: 'static {} -#[cfg(target_arch = "wasm32")] -impl TaskBound for T {} - -#[cfg(not(target_arch = "wasm32"))] -pub trait TaskBound: Send + 'static {} -#[cfg(not(target_arch = "wasm32"))] -impl TaskBound for T {} diff --git a/clients/backbone-lib/src/protocol.rs b/clients/backbone-lib/src/protocol.rs deleted file mode 100644 index 65f972a..0000000 --- a/clients/backbone-lib/src/protocol.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! Wire protocol encoding/decoding helpers. -//! -//! Translates between raw WebSocket binary frames and typed Rust values using -//! postcard serialization and the message-type constants from the `protocol` crate. - -use crate::traits::{SerializationCap, ViewStateUpdate}; -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use ewebsock::{WsMessage, WsSender}; -use postcard::{from_bytes, take_from_bytes, to_stdvec}; -use protocol::{ - CLIENT_DISCONNECTS, CLIENT_DISCONNECTS_SELF, CLIENT_GETS_KICKED, CLIENT_ID_SIZE, DELTA_UPDATE, - FULL_UPDATE, HAND_SHAKE_RESPONSE, JoinRequest, NEW_CLIENT, RESET, SERVER_DISCONNECTS, - SERVER_ERROR, SERVER_RPC, -}; - -// --------------------------------------------------------------------------- -// Inbound command types (relay → host) -// --------------------------------------------------------------------------- - -pub enum ToServerCommand { - ClientJoin(u16), - ClientLeft(u16), - Rpc(u16, A), - Error(String), -} - -// --------------------------------------------------------------------------- -// Send helpers -// --------------------------------------------------------------------------- - -fn send_binary(sender: &mut WsSender, data: &[u8]) { - sender.send(WsMessage::Binary(data.to_vec())); -} - -pub fn send_join_request(sender: &mut WsSender, req: &JoinRequest) -> Result<(), String> { - let bytes = to_stdvec(req).map_err(|e| e.to_string())?; - send_binary(sender, &bytes); - Ok(()) -} - -pub fn send_rpc(sender: &mut WsSender, action: &A) { - let raw = to_stdvec(action).expect("Failed to serialize RPC"); - let mut buf = BytesMut::with_capacity(1 + raw.len()); - buf.put_u8(SERVER_RPC); - buf.put_slice(&raw); - send_binary(sender, &buf); -} - -pub fn send_delta(sender: &mut WsSender, deltas: &[D]) { - let serialized: Vec = deltas - .iter() - .flat_map(|d| to_stdvec(d).expect("Failed to serialize delta")) - .collect(); - let mut buf = BytesMut::with_capacity(1 + serialized.len()); - buf.put_u8(DELTA_UPDATE); - buf.put_slice(&serialized); - send_binary(sender, &buf); -} - -pub fn send_full_state(sender: &mut WsSender, state: &VS) { - let serialized = to_stdvec(state).expect("Failed to serialize full state"); - let mut buf = BytesMut::with_capacity(1 + serialized.len()); - buf.put_u8(FULL_UPDATE); - buf.put_slice(&serialized); - send_binary(sender, &buf); -} - -pub fn send_reset(sender: &mut WsSender, state: &VS) { - let serialized = to_stdvec(state).expect("Failed to serialize reset state"); - let mut buf = BytesMut::with_capacity(1 + serialized.len()); - buf.put_u8(RESET); - buf.put_slice(&serialized); - send_binary(sender, &buf); -} - -pub fn send_kick(sender: &mut WsSender, player_id: u16) { - let mut buf = BytesMut::with_capacity(1 + CLIENT_ID_SIZE); - buf.put_u8(CLIENT_GETS_KICKED); - buf.put_u16(player_id); - send_binary(sender, &buf); -} - -pub fn send_disconnect(sender: &mut WsSender, as_host: bool) { - let msg = if as_host { - SERVER_DISCONNECTS - } else { - CLIENT_DISCONNECTS_SELF - }; - send_binary(sender, &[msg]); -} - -// --------------------------------------------------------------------------- -// Receive / parse helpers -// --------------------------------------------------------------------------- - -/// Parses the relay's handshake response. -/// -/// Returns `(player_id, rule_variation, reconnect_token)`. -pub fn parse_handshake_response(data: Vec) -> Result<(u16, u16, u64), String> { - let mut bytes = Bytes::from(data); - let msg = bytes.get_u8(); - match msg { - SERVER_ERROR => Err(String::from_utf8_lossy(&bytes).to_string()), - HAND_SHAKE_RESPONSE => { - let player_id = bytes.get_u16(); - let rule_variation = bytes.get_u16(); - let token = bytes.get_u64(); - Ok((player_id, rule_variation, token)) - } - other => Err(format!("Unexpected handshake message id: {other}")), - } -} - -pub fn parse_server_command(data: Vec) -> ToServerCommand { - let mut bytes = Bytes::from(data); - let msg = bytes.get_u8(); - match msg { - SERVER_ERROR => ToServerCommand::Error(String::from_utf8_lossy(&bytes).to_string()), - NEW_CLIENT => ToServerCommand::ClientJoin(bytes.get_u16()), - CLIENT_DISCONNECTS => ToServerCommand::ClientLeft(bytes.get_u16()), - SERVER_RPC => { - let client_id = bytes.get_u16(); - let payload: A = - from_bytes(bytes.chunk()).expect("Failed to deserialize server RPC payload"); - ToServerCommand::Rpc(client_id, payload) - } - other => ToServerCommand::Error(format!("Unknown server message id: {other}")), - } -} - -pub fn parse_client_update( - data: Vec, -) -> Result>, String> -where - VS: SerializationCap, - D: SerializationCap, -{ - let mut bytes = Bytes::from(data); - let msg = bytes.get_u8(); - match msg { - SERVER_ERROR => Err(String::from_utf8_lossy(&bytes).to_string()), - DELTA_UPDATE => { - let mut result = Vec::new(); - let mut remaining: &[u8] = &bytes; - while !remaining.is_empty() { - let (delta, rest): (D, &[u8]) = - take_from_bytes(remaining).map_err(|e| e.to_string())?; - remaining = rest; - result.push(ViewStateUpdate::Incremental(delta)); - } - Ok(result) - } - FULL_UPDATE | RESET => { - let state: VS = from_bytes(&bytes).map_err(|e| e.to_string())?; - Ok(vec![ViewStateUpdate::Full(state)]) - } - other => Err(format!("Unknown client message id: {other}")), - } -} diff --git a/clients/backbone-lib/src/session.rs b/clients/backbone-lib/src/session.rs deleted file mode 100644 index 24314f7..0000000 --- a/clients/backbone-lib/src/session.rs +++ /dev/null @@ -1,266 +0,0 @@ -//! The public-facing session API. -//! -//! # Usage -//! -//! ```ignore -//! // Connect (async, returns after handshake completes) -//! let mut session: GameSession = -//! GameSession::connect::(RoomConfig { -//! relay_url: "ws://localhost:8080/ws".to_string(), -//! game_id: "my-game".to_string(), -//! room_id: "room-42".to_string(), -//! rule_variation: 0, -//! role: RoomRole::Create, -//! reconnect_token: None, -//! }) -//! .await?; -//! -//! // In a loop (e.g. Dioxus coroutine with futures::select!): -//! loop { -//! futures::select! { -//! cmd = ui_rx.next().fuse() => session.send_action(cmd), -//! event = session.next_event().fuse() => match event { -//! Some(SessionEvent::Update(u)) => view_state.apply(u), -//! Some(SessionEvent::Disconnected(reason)) | None => break, -//! } -//! } -//! } -//! ``` - -use ewebsock::{WsEvent, WsMessage}; -use futures::StreamExt; -use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use protocol::JoinRequest; - -use crate::client::client_loop; -use crate::host::host_loop; -use crate::platform::{TaskBound, sleep_ms, spawn_task}; -use crate::protocol::{parse_handshake_response, send_join_request}; -use crate::traits::{BackEndArchitecture, SerializationCap, ViewStateUpdate}; - -// --------------------------------------------------------------------------- -// Public configuration types -// --------------------------------------------------------------------------- - -/// Whether to create a new room (host) or join an existing one (client). -pub enum RoomRole { - Create, - Join, -} - -/// Configuration required to connect to a game session. -pub struct RoomConfig { - /// WebSocket URL of the relay server (e.g. `"ws://localhost:8080/ws"`). - pub relay_url: String, - /// Game identifier registered on the relay (e.g. `"tic-tac-toe"`). - pub game_id: String, - /// Room identifier shared between host and clients. - pub room_id: String, - /// Game mode/variant. Only used when `role` is `Create`. - pub rule_variation: u16, - pub role: RoomRole, - /// If `Some`, attempt to reconnect to an existing session instead of creating/joining fresh. - /// The value is the token returned by a previous successful handshake. - pub reconnect_token: Option, - /// Serialized backend state for host reconnect. - /// - /// Produced by the app layer (e.g. `serde_json::to_vec(&view_state)`) and stored in - /// localStorage. Passed to [`BackEndArchitecture::from_bytes`] when the host - /// reconnects so the game can resume from the last known state. - /// Ignored for non-host reconnects and normal connections. - pub host_state: Option>, -} - -/// Error returned by [`GameSession::connect`]. -#[derive(Debug)] -pub enum ConnectError { - WebSocket(String), - Handshake(String), -} - -impl std::fmt::Display for ConnectError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ConnectError::WebSocket(e) => write!(f, "WebSocket error: {e}"), - ConnectError::Handshake(e) => write!(f, "Handshake error: {e}"), - } - } -} - -// --------------------------------------------------------------------------- -// Internal message type (UI → background task) -// --------------------------------------------------------------------------- - -pub(crate) enum BackendMsg { - Action(A), - Disconnect, -} - -// --------------------------------------------------------------------------- -// Session event (background task → UI) -// --------------------------------------------------------------------------- - -/// Events emitted by the session to the UI. -pub enum SessionEvent { - /// A state update arrived from the host backend. - Update(ViewStateUpdate), - /// The session ended. `None` = clean disconnect, `Some(reason)` = error. - Disconnected(Option), -} - -// --------------------------------------------------------------------------- -// GameSession -// --------------------------------------------------------------------------- - -/// A connected game session. -/// -/// Created by [`GameSession::connect`]. Holds channels to the background task -/// that owns the WebSocket connection and (on host) the game backend. -pub struct GameSession { - /// The player ID assigned by the relay server. Always `0` for the host. - pub player_id: u16, - /// The game mode/variant selected by the host. - pub rule_variation: u16, - /// `true` if this client is hosting the game (runs the backend). - pub is_host: bool, - /// Token to persist in localStorage for reconnect on page refresh. - /// Only meaningful for non-host players (player_id > 0). - pub reconnect_token: u64, - action_tx: UnboundedSender>, - event_rx: UnboundedReceiver>, -} - -impl GameSession -where - A: SerializationCap + TaskBound, - D: SerializationCap + Clone + TaskBound, - VS: SerializationCap + Clone + TaskBound, -{ - /// Connects to the relay server and performs the handshake. - /// - /// Returns after the relay confirms the player ID and rule variation. - /// Spawns a background task that drives the WebSocket connection for the - /// lifetime of the session. - /// - /// # Errors - /// Returns `Err` if the WebSocket cannot be opened or the handshake fails. - pub async fn connect(config: RoomConfig) -> Result - where - Backend: BackEndArchitecture + TaskBound, - { - let create_room = matches!(config.role, RoomRole::Create); - - // 1. Open WebSocket. - let (mut ws_sender, ws_receiver) = - ewebsock::connect(&config.relay_url, ewebsock::Options::default()) - .map_err(|e| ConnectError::WebSocket(e.to_string()))?; - - // 2. Wait for the Opened event (WASM WebSocket is async). - loop { - match ws_receiver.try_recv() { - Some(WsEvent::Opened) => break, - Some(WsEvent::Error(e)) => return Err(ConnectError::WebSocket(e)), - Some(WsEvent::Closed) => { - return Err(ConnectError::WebSocket("Connection closed".to_string())); - } - Some(_) => continue, - None => sleep_ms(1).await, - } - } - - // 3. Send the join request. - let req = JoinRequest { - game_id: config.game_id, - room_id: config.room_id, - rule_variation: config.rule_variation, - create_room, - reconnect_token: config.reconnect_token, - }; - send_join_request(&mut ws_sender, &req).map_err(ConnectError::Handshake)?; - - // 4. Wait for the handshake response. - let (player_id, rule_variation, reconnect_token) = loop { - match ws_receiver.try_recv() { - Some(WsEvent::Message(WsMessage::Binary(data))) => { - break parse_handshake_response(data).map_err(ConnectError::Handshake)?; - } - Some(WsEvent::Error(e)) => return Err(ConnectError::Handshake(e)), - Some(WsEvent::Closed) => { - // The relay may have sent a binary error frame just before - // closing. ewebsock can deliver Closed before that frame, - // so drain one more message to catch it. - if let Some(WsEvent::Message(WsMessage::Binary(data))) = - ws_receiver.try_recv() - { - break parse_handshake_response(data) - .map_err(ConnectError::Handshake)?; - } - return Err(ConnectError::Handshake( - "Connection closed during handshake".to_string(), - )); - } - Some(_) => continue, - None => sleep_ms(1).await, - } - }; - - // The relay assigns player_id == 0 exclusively to the host. - let is_host = player_id == 0; - - // 5. Set up channels between the UI and the background task. - let (action_tx, action_rx) = mpsc::unbounded::>(); - let (event_tx, event_rx) = mpsc::unbounded::>(); - - // 6. Spawn the background event loop. - if is_host { - spawn_task(host_loop::( - ws_sender, - ws_receiver, - action_rx, - event_tx, - rule_variation, - config.host_state, - )); - } else { - spawn_task(client_loop::( - ws_sender, - ws_receiver, - action_rx, - event_tx, - )); - } - - Ok(GameSession { - player_id, - rule_variation, - is_host, - reconnect_token, - action_tx, - event_rx, - }) - } - - /// Sends a game action to the backend (fire-and-forget). - pub fn send_action(&self, action: A) { - self.action_tx - .unbounded_send(BackendMsg::Action(action)) - .ok(); - } - - /// Awaits the next session event. - /// - /// Returns `None` if the background task has exited (i.e. the session is - /// over). Normal termination arrives as `Some(SessionEvent::Disconnected(_))` - /// before the channel closes. - pub async fn next_event(&mut self) -> Option> { - self.event_rx.next().await - } - - /// Signals the background task to send a graceful disconnect message and - /// shut down. Consumes the session. - pub fn disconnect(self) { - self.action_tx - .unbounded_send(BackendMsg::Disconnect) - .ok(); - } -} diff --git a/clients/backbone-lib/src/traits.rs b/clients/backbone-lib/src/traits.rs deleted file mode 100644 index 1ec50f7..0000000 --- a/clients/backbone-lib/src/traits.rs +++ /dev/null @@ -1,97 +0,0 @@ -use serde::Serialize; -use serde::de::DeserializeOwned; - -/// Marker trait for types that can be serialized with postcard. -pub trait SerializationCap: Serialize + DeserializeOwned {} -impl SerializationCap for T where T: Serialize + DeserializeOwned {} - -/// State updates delivered to the frontend for rendering. -/// -/// - [`Full`](Self::Full): Immediately set all visual state, no animation. -/// - [`Incremental`](Self::Incremental): Apply with animation/transition. -pub enum ViewStateUpdate { - /// Complete game state snapshot. Received on join or after a reset. - Full(ViewState), - /// Incremental state change for animated transitions. - Incremental(DeltaInformation), -} - -/// Commands emitted by the game backend to control the session. -pub enum BackendCommand -where - DeltaInformation: SerializationCap, -{ - /// Incremental state change to be broadcast to all frontends. - Delta(DeltaInformation), - - /// Signals a complete reset: discard queued deltas, broadcast fresh full state. - ResetViewState, - - /// Forcibly removes a player from the session. - KickPlayer { player: u16 }, - - /// Schedules a callback after `duration` seconds. Overwrites any existing - /// timer with the same `timer_id`. - SetTimer { timer_id: u16, duration: f32 }, - - /// Cancels a previously scheduled timer. No-op if already fired or not set. - CancelTimer { timer_id: u16 }, - - /// Shuts down the entire room and disconnects all players. - TerminateRoom, -} - -/// The contract for game-specific server logic. -/// -/// Implement this on the host side. The session calls these methods in response -/// to network events and drives `drain_commands` to collect outbound messages. -/// -/// # Type Parameters -/// * `ServerRpcPayload` — Actions sent by players (e.g. `PlacePiece { x, y }`) -/// * `DeltaInformation` — Incremental state changes for animations -/// * `ViewState` — Complete game snapshot for syncing new clients -pub trait BackEndArchitecture -where - ServerRpcPayload: SerializationCap, - DeltaInformation: SerializationCap, - ViewState: SerializationCap + Clone, -{ - /// Creates a new game instance. `rule_variation` selects the game mode. - fn new(rule_variation: u16) -> Self; - - /// Attempt to restore a previously running game from serialized bytes. - /// - /// Called when the host reconnects after a page refresh. The bytes are the - /// game-specific snapshot produced by the app layer (via `serde_json` or - /// similar) and stored in localStorage. - /// - /// Return `None` if restoration is not supported or the bytes are invalid — - /// the caller falls back to `new(rule_variation)`. - fn from_bytes(_rule_variation: u16, _bytes: &[u8]) -> Option - where - Self: Sized, - { - None - } - - /// Called when a player connects. Player will receive a full state snapshot - /// automatically after this returns. - fn player_arrival(&mut self, player: u16); - - /// Called when a player disconnects. - fn player_departure(&mut self, player: u16); - - /// Called when a player sends a game action. - fn inform_rpc(&mut self, player: u16, payload: ServerRpcPayload); - - /// Called when a previously scheduled timer fires. - fn timer_triggered(&mut self, timer_id: u16); - - /// Returns the complete current game state. - fn get_view_state(&self) -> &ViewState; - - /// Collects and clears all pending commands since the last drain. - /// - /// Implement with `std::mem::take(&mut self.command_list)`. - fn drain_commands(&mut self) -> Vec>; -} diff --git a/clients/web/Cargo.toml b/clients/web/Cargo.toml deleted file mode 100644 index f184a28..0000000 --- a/clients/web/Cargo.toml +++ /dev/null @@ -1,48 +0,0 @@ -[package] -name = "trictrac-web" -version = "0.1.0" -edition = "2021" - -[package.metadata.leptos-i18n] -default = "en" -locales = ["en", "fr"] - -[dependencies] -leptos_i18n = { version = "0.5", features = ["csr", "interpolate_display"] } -leptos_router = { version = "0.7" } -trictrac-store = { path = "../../store" } -backbone-lib = { path = "../backbone-lib" } -leptos = { version = "0.7", features = ["csr"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1" -futures = "0.3" -rand = "0.9" -gloo-storage = "0.3" -qrcodegen = "1.8" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasm-bindgen = "0.2" -wasm-bindgen-futures = "0.4" -gloo-net = { version = "0.5", features = ["http"] } -gloo-timers = { version = "0.3", features = ["futures"] } -getrandom = { version = "0.3", features = ["wasm_js"] } -js-sys = "0.3" -web-sys = { version = "0.3", features = [ - "RequestCredentials", - "AudioContext", - "AudioParam", - "AudioNode", - "AudioDestinationNode", - "AudioScheduledSourceNode", - "GainNode", - "OscillatorNode", - "OscillatorType", - "BaseAudioContext", - "HtmlAudioElement", - "Clipboard", - "Navigator", - "Location", -] } - -[dev-dependencies] -wasm-bindgen-test = "0.3" diff --git a/clients/web/Trunk.toml b/clients/web/Trunk.toml deleted file mode 100644 index bae5297..0000000 --- a/clients/web/Trunk.toml +++ /dev/null @@ -1,2 +0,0 @@ -[serve] -port = 9091 diff --git a/clients/web/assets/diceroll.mp3 b/clients/web/assets/diceroll.mp3 deleted file mode 100644 index b16adff..0000000 Binary files a/clients/web/assets/diceroll.mp3 and /dev/null differ diff --git a/clients/web/assets/style.css b/clients/web/assets/style.css deleted file mode 100644 index 428d693..0000000 --- a/clients/web/assets/style.css +++ /dev/null @@ -1,2078 +0,0 @@ -/* ── Google Fonts ───────────────────────────────────────────────── */ -@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Jost:wght@300;400;500&display=swap'); - -/* ── Design tokens ──────────────────────────────────────────────────── */ -:root { - --board-felt: #1d3d28; - --board-rail: #2a1508; - --field-ivory: #f0e6c8; - --field-burgundy: #7a1e2a; - --field-blue: #e5eadc; - --field-blue-light: #1a4f72; - --field-brown: #f2dfa0; - --field-brown-light: #6a2810; - --field-corner: #b8900a; - --checker-white: #f5edd8; - --checker-black: #1a0f06; - --checker-ring: #c8a448; - --ui-parchment: #f2e8d0; - --ui-parchment-dark: #e4d8b8; - --ui-ink: #2a1a08; - --ui-gold: #c8a448; - --ui-gold-dark: #8a6a28; - --ui-green-accent: #3a6b2a; - --ui-red-accent: #7a1e2a; - --font-display: 'Cormorant Garamond', Georgia, serif; - --font-ui: 'Jost', system-ui, sans-serif; -} - - -/* ── Reset & base ──────────────────────────────────────────────────── */ -*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } - -body { - font-family: var(--font-ui); - background: #8a7050; - background-image: - radial-gradient(ellipse at 20% 10%, rgba(80,48,16,0.35) 0%, transparent 60%), - radial-gradient(ellipse at 80% 90%, rgba(40,24,8,0.3) 0%, transparent 55%), - repeating-linear-gradient( - 45deg, transparent, transparent 3px, - rgba(0,0,0,0.03) 3px, rgba(0,0,0,0.03) 4px - ); - display: flex; - flex-direction: column; - min-height: 100vh; -} - -.hidden { display: none !important; } - -/* -- svg icons -- */ -.icon { - width: 1.2em; - height: 1.2em; - color: var(--ui-parchment); - vertical-align: -0.25em; - margin-right: 0.7em; -} - -/* ── Site navigation ─────────────────────────────────────────────── */ -.site-nav { - background: var(--board-rail); - border-bottom: 2px solid var(--ui-gold-dark); - padding: 0 1.5rem; - height: 52px; - display: flex; - align-items: center; - gap: 1.5rem; - position: sticky; - top: 0; - z-index: 50; - box-shadow: 0 2px 8px rgba(0,0,0,0.4); - flex-shrink: 0; -} - -.site-nav-brand { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 600; - color: var(--ui-gold); - text-decoration: none; - letter-spacing: 0.1em; -} -.site-nav-brand:hover { color: #e0b840; } - -.site-nav-spacer { flex: 1; } - -.site-nav a { - font-family: var(--font-ui); - font-size: 0.9rem; - color: var(--ui-parchment); - text-decoration: none; - opacity: 0.8; - transition: opacity 0.15s, color 0.15s; -} -.site-nav a:hover { opacity: 1; } - -.site-nav-btn { - padding: 0.3rem 0.9rem; - font-family: var(--font-ui); - font-size: 0.85rem; - font-weight: 500; - letter-spacing: 0.03em; - border: 1px solid rgba(200,164,72,0.4); - border-radius: 4px; - background: transparent; - color: var(--ui-parchment); - cursor: pointer; - transition: background 0.15s, border-color 0.15s; -} -.site-nav-btn:hover { - background: rgba(200,164,72,0.12); - border-color: var(--ui-gold); -} - -/* ── Portal main content area ────────────────────────────────────── */ -.portal-main { - flex: 1; - max-width: 900px; - width: 100%; - margin: 2rem auto; - padding: 0 1.5rem; -} - -.portal-card { - background: var(--ui-parchment); - border-radius: 8px; - box-shadow: - 0 20px 60px rgba(0,0,0,0.55), - 0 0 3px 3px rgba(42,21,8,0.9) - ; - /* box-shadow: 0 4px 16px rgba(0,0,0,0.18); */ - /* border: 1px solid rgba(200,164,72,0.3); */ - /* border-top: 3px solid var(--ui-gold-dark); */ - padding: 1.75rem 2rem; - margin-bottom: 1.5rem; -} - -.portal-card h1 { - font-family: var(--font-display); - font-size: 2rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.04em; - margin-bottom: 0.25rem; -} -.portal-card h2 { - font-family: var(--font-display); - font-size: 1.35rem; - font-weight: 600; - color: var(--ui-ink); - margin-bottom: 0.75rem; -} - -.portal-meta { - font-size: 0.85rem; - color: #665544; - margin-bottom: 1.5rem; - font-family: var(--font-ui); -} - -/* ── Stats grid ──────────────────────────────────────────────────── */ -.stats-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1rem; - margin-bottom: 1.5rem; -} -.stat-box { - background: var(--ui-parchment); - border: 1px solid rgba(200,164,72,0.28); - border-radius: 6px; - padding: 1rem; - text-align: center; - box-shadow: 0 1px 4px rgba(0,0,0,0.1); -} -.stat-box .value { - font-family: var(--font-display); - font-size: 2.2rem; - font-weight: 600; - color: var(--ui-gold-dark); -} -.stat-box .label { - font-size: 0.78rem; - color: #665544; - margin-top: 0.2rem; - letter-spacing: 0.04em; - text-transform: uppercase; -} - -/* ── Tables ──────────────────────────────────────────────────────── */ -table { - width: 100%; - border-collapse: collapse; - font-size: 0.9rem; - font-family: var(--font-ui); -} -th { - text-align: left; - padding: 0.5rem 0.75rem; - border-bottom: 2px solid rgba(200,164,72,0.4); - color: #665544; - font-weight: 500; - font-size: 0.8rem; - letter-spacing: 0.05em; - text-transform: uppercase; -} -td { - padding: 0.5rem 0.75rem; - border-bottom: 1px solid rgba(200,164,72,0.12); - color: var(--ui-ink); -} -tr:last-child td { border-bottom: none; } -tr:hover td { background: rgba(200,164,72,0.05); } - -a { color: var(--ui-gold-dark); text-decoration: none; } -a:hover { text-decoration: underline; } - -/* ── Outcome classes ─────────────────────────────────────────────── */ -.outcome-win { color: var(--ui-green-accent); font-weight: 600; } -.outcome-loss { color: var(--ui-red-accent); font-weight: 600; } -.outcome-draw { color: #c07020; font-weight: 600; } - -/* ── Portal tabs ─────────────────────────────────────────────────── */ -.portal-tabs { - display: flex; - gap: 0; - margin-bottom: 1.5rem; - border-bottom: 1px solid rgba(200,164,72,0.3); -} -.portal-tab-btn { - padding: 0.55rem 1.5rem; - font-family: var(--font-ui); - font-size: 0.9rem; - background: transparent; - border: none; - cursor: pointer; - color: #665544; - border-bottom: 2px solid transparent; - margin-bottom: -1px; - transition: color 0.15s, border-color 0.15s; -} -.portal-tab-btn.active { - color: var(--ui-ink); - border-bottom-color: var(--ui-gold-dark); - font-weight: 500; -} - -/* ── Portal form ─────────────────────────────────────────────────── */ -.portal-label { - display: block; - font-size: 0.82rem; - color: #665544; - margin-bottom: 0.3rem; - letter-spacing: 0.03em; -} -.portal-input { - width: 100%; - padding: 0.55rem 0.85rem; - font-size: 0.95rem; - font-family: var(--font-ui); - border: 1px solid rgba(138,106,40,0.35); - border-radius: 5px; - background: rgba(255,252,240,0.8); - color: var(--ui-ink); - outline: none; - margin-bottom: 1rem; - transition: border-color 0.15s, box-shadow 0.15s; -} -.portal-input:focus { - border-color: var(--ui-gold); - box-shadow: 0 0 0 3px rgba(200,164,72,0.18); -} -.portal-submit-btn { - padding: 0.6rem 2rem; - font-family: var(--font-ui); - font-size: 0.9rem; - font-weight: 500; - background: linear-gradient(160deg, #4a7a38 0%, #2e5222 100%); - color: #e8f0e0; - border: none; - border-radius: 5px; - cursor: pointer; - box-shadow: 0 2px 5px rgba(0,0,0,0.25); - transition: opacity 0.15s; -} -.portal-submit-btn:disabled { opacity: 0.45; cursor: not-allowed; } -.portal-submit-btn:not(:disabled):hover { opacity: 0.9; } - -.portal-page-btn { - padding: 0.35rem 0.9rem; - font-family: var(--font-ui); - font-size: 0.85rem; - background: var(--board-rail); - color: var(--ui-parchment); - border: none; - border-radius: 4px; - cursor: pointer; - opacity: 0.85; - transition: opacity 0.15s; -} -.portal-page-btn:hover { opacity: 1; } - -.portal-loading { color: #665544; font-style: italic; padding: 1rem 0; } -.portal-empty { color: #aa9070; font-style: italic; padding: 1rem 0; } -.portal-error { color: var(--ui-red-accent); font-size: 0.875rem; margin-top: 0.5rem; } -.portal-success { color: var(--ui-green-accent); font-size: 0.875rem; margin-top: 0.5rem; } - -.portal-link { - color: var(--ui-gold); - text-decoration: none; - font-size: 0.875rem; -} -.portal-link:hover { text-decoration: underline; } - -.portal-verification-banner { - background: rgba(200,164,72,0.08); - border: 1px solid rgba(200,164,72,0.35); - border-radius: 6px; - padding: 1.25rem; - text-align: center; -} -.portal-verification-banner p { - margin-bottom: 0.75rem; - font-size: 0.9rem; -} - -/* ── Share URL row (lobby waiting card + game top bar) ──────────── */ -.share-url-row { - display: flex; - align-items: center; - gap: 0.5rem; - background: rgba(0,0,0,0.18); - border: 1px solid rgba(200,164,72,0.25); - border-radius: 5px; - padding: 0.4rem 0.6rem; -} -.share-url-text { - flex: 1; - font-family: var(--font-ui); - font-size: 0.72rem; - color: rgba(242,232,208,0.75); - word-break: break-all; - user-select: all; -} -.share-copy-btn { - flex-shrink: 0; - font-family: var(--font-ui); - font-size: 0.72rem; - padding: 0.2rem 0.6rem; - border: 1px solid rgba(200,164,72,0.4); - border-radius: 3px; - background: rgba(200,164,72,0.1); - color: var(--ui-parchment); - cursor: pointer; - transition: background 0.15s; - white-space: nowrap; -} -.share-copy-btn:hover { background: rgba(200,164,72,0.22); } - -/* ── QR code container ───────────────────────────────────────────── */ -.qr-container { - width: 160px; - height: 160px; - margin: 0 auto; - border-radius: 4px; - overflow: hidden; -} -.qr-container svg { width: 100%; height: 100%; display: block; } - -/* ── Share popover (in-game top bar) ─────────────────────────────── */ -.share-popover { - width: 100%; - background: rgba(0,0,0,0.3); - border: 1px solid rgba(200,164,72,0.2); - border-radius: 6px; - padding: 0.75rem 1rem; - display: flex; - flex-direction: column; - align-items: center; - gap: 0.4rem; - margin-bottom: 0.5rem; -} -.share-popover .qr-container { width: 120px; height: 120px; } -.share-popover-label { - font-size: 0.75rem; - color: rgba(242,232,208,0.6); - text-align: center; - margin: 0; -} - -/* ── Game overlay (full-screen, covers portal during play) ───────── */ -.game-overlay { - position: fixed; - inset: 0; - background: #8a7050; - background-image: - radial-gradient(ellipse at 20% 10%, rgba(80,48,16,0.35) 0%, transparent 60%), - radial-gradient(ellipse at 80% 90%, rgba(40,24,8,0.3) 0%, transparent 55%), - repeating-linear-gradient( - 45deg, transparent, transparent 3px, - rgba(0,0,0,0.03) 3px, rgba(0,0,0,0.03) 4px - ); - z-index: 200; - display: flex; - justify-content: center; - align-items: flex-start; - padding: 1.5rem; - overflow-y: auto; -} - -/* ── Login card (§11) ───────────────────────────────────────────────── */ -.login-card { - background: var(--ui-parchment); - border-radius: 8px; - box-shadow: - 0 20px 60px rgba(0,0,0,0.55), - 0 0 3px 3px rgba(42,21,8,0.9) - ; - /* box-shadow: - 0 20px 60px rgba(0,0,0,0.55), - 0 0 0 1px rgba(200,164,72,0.35), - 0 0 0 5px rgba(42,21,8,0.9), - 0 0 0 6px rgba(200,164,72,0.2); - */ - /* border-top: 3px solid var(--ui-gold-dark); */ - width: 340px; - margin-top: 5vh; - overflow: hidden; -} - -/* Decorative header — row of triangular flèches like the actual board */ -.login-card-header { - height: 52px; - background: var(--board-felt); - position: relative; - overflow: hidden; -} - -.login-board-stripe { - position: absolute; - inset: 0; - background: - repeating-linear-gradient( - 90deg, - var(--field-burgundy) 0, var(--field-burgundy) 50%, - var(--field-ivory) 50%, var(--field-ivory) 100% - ); - background-size: 34px 100%; - clip-path: polygon( - 0% 0%, 2.94% 100%, 5.88% 0%, 8.82% 100%, 11.76% 0%, - 14.7% 100%, 17.65% 0%, 20.59% 100%, 23.53% 0%, - 26.47% 100%, 29.41% 0%, 32.35% 100%, 35.29% 0%, - 38.24% 100%, 41.18% 0%, 44.12% 100%, 47.06% 0%, - 50% 100%, 52.94% 0%, 55.88% 100%, 58.82% 0%, - 61.76% 100%, 64.71% 0%, 67.65% 100%, 70.59% 0%, - 73.53% 100%, 76.47% 0%, 79.41% 100%, 82.35% 0%, - 85.29% 100%, 88.24% 0%, 91.18% 100%, 94.12% 0%, - 97.06% 100%, 100% 0% - ); - opacity: 0.9; -} - -.login-card-body { - display: flex; - flex-direction: column; - align-items: center; - gap: 0; - padding: 1.5rem 2rem 2rem; -} - -.login-lang-switcher { - align-self: flex-end; - margin-bottom: 0.75rem; -} - -/* Override lang-switcher colours for the parchment card */ -.login-card .lang-switcher button { - color: var(--ui-ink); - border-color: rgba(42,21,8,0.2); - opacity: 0.5; -} -.login-card .lang-switcher button.lang-active { - opacity: 1; - background: rgba(42,21,8,0.08); - border-color: rgba(42,21,8,0.35); -} - -.login-title { - font-family: var(--font-display); - font-size: 3.25rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.12em; - text-align: center; - line-height: 1; - margin-bottom: 0.3rem; -} - -.login-subtitle { - font-family: var(--font-display); - font-size: 0.85rem; - color: rgba(42,26,8,0.55); - text-align: center; - letter-spacing: 0.06em; - font-style: italic; - margin-bottom: 1.25rem; -} -.login-subtitle sup { - font-size: 0.65em; - vertical-align: super; -} - -.login-ornament { - color: var(--ui-gold); - font-size: 1rem; - opacity: 0.7; - margin-bottom: 1.25rem; - letter-spacing: 0.3em; - text-align: center; -} - -.error-msg { - color: #c03030; - font-size: 0.85rem; - text-align: center; - margin-bottom: 0.5rem; -} - -.login-input { - width: 100%; - padding: 0.55rem 0.85rem; - font-size: 0.95rem; - font-family: var(--font-ui); - border: 1px solid rgba(138,106,40,0.4); - border-radius: 5px; - background: rgba(255,252,240,0.8); - color: var(--ui-ink); - outline: none; - transition: border-color 0.15s, box-shadow 0.15s; - margin-bottom: 1rem; -} -.login-input:focus { - border-color: var(--ui-gold); - box-shadow: 0 0 0 3px rgba(200,164,72,0.2); -} - -.login-actions { - display: flex; - flex-direction: column; - gap: 0.55rem; - width: 100%; -} - -/* Login buttons styled as embossed wooden tiles */ -.login-btn { - width: 100%; - padding: 0.65rem 1rem; - font-family: var(--font-ui); - font-size: 0.9rem; - font-weight: 500; - letter-spacing: 0.04em; - border: none; - border-radius: 5px; - cursor: pointer; - transition: opacity 0.15s, transform 0.1s, box-shadow 0.15s; - position: relative; -} -.login-btn:disabled { opacity: 0.35; cursor: default; } -.login-btn:not(:disabled):hover { opacity: 0.92; transform: translateY(-1px); } -.login-btn:not(:disabled):active { transform: translateY(0); } - -.login-btn-primary { - background: linear-gradient(160deg, #4a7a38 0%, #2e5222 100%); - color: #e8f0e0; - box-shadow: 0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.12); -} -.login-btn-secondary { - background: linear-gradient(160deg, #3a2010 0%, #241408 100%); - color: #e4d4b4; - box-shadow: 0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.08); -} -.login-btn-bot { - background: linear-gradient(160deg, #2a4a6a 0%, #183050 100%); - color: #d0e0f0; - box-shadow: 0 2px 6px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.08); -} - -/* ── Connecting screen ──────────────────────────────────────────────── */ -.connecting { - font-family: var(--font-display); - font-size: 1.4rem; - font-style: italic; - margin-top: 4rem; - text-align: center; - color: var(--ui-parchment); - text-shadow: 0 1px 4px rgba(0,0,0,0.4); -} - -/* ── Game-action buttons ─────────────────────────────────────────────── */ -.btn { - padding: 0.5rem 1.25rem; - font-size: 0.95rem; - font-family: var(--font-ui); - font-weight: 500; - letter-spacing: 0.03em; - border: none; - border-radius: 4px; - cursor: pointer; - transition: opacity 0.15s, box-shadow 0.15s; -} -.btn:disabled { opacity: 0.4; cursor: default; } -.btn-primary { background: var(--ui-green-accent); color: #fff; } -.btn-secondary { background: var(--board-rail); color: #e8d8b8; } -.btn-bot { background: #2a5a7a; color: #fff; } -.btn:not(:disabled):hover { - opacity: 0.9; - box-shadow: 0 2px 6px rgba(0,0,0,0.25); -} - -/* ── Game container ─────────────────────────────────────────────────── */ -.game-container { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.6rem; -} - -/* ── Language switcher (in-game) ────────────────────────────────────── */ -.lang-switcher { display: flex; gap: 0.25rem; } - -.lang-switcher button { - font-size: 0.7rem; - font-family: var(--font-ui); - letter-spacing: 0.05em; - padding: 0.15rem 0.4rem; - border: 1px solid rgba(200,164,72,0.3); - border-radius: 3px; - background: transparent; - cursor: pointer; - color: var(--ui-parchment); - opacity: 0.55; -} -.lang-switcher button.lang-active { - opacity: 1; - font-weight: 500; - background: rgba(200,164,72,0.15); - border-color: rgba(200,164,72,0.6); -} - -/* ── Top bar ─────────────────────────────────────────────────────────── */ -.top-bar { - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - color: var(--ui-parchment); - font-size: 0.85rem; - opacity: 0.8; -} - -.quit-link { - font-size: 0.8rem; - color: var(--ui-parchment); - text-decoration: underline; - text-underline-offset: 2px; - cursor: pointer; - opacity: 0.7; -} -.quit-link:hover { opacity: 1; } - -.playing-as { - font-size: 0.75rem; - color: rgba(242,232,208,0.7); - font-family: var(--font-ui); -} -.playing-as strong { color: rgba(242,232,208,0.9); } - -/* ── Game status bar (§10b) — above board ───────────────────────────── */ -.game-status { - font-family: var(--font-display); - font-size: 1.2rem; - font-style: italic; - color: var(--ui-parchment); - text-align: center; - letter-spacing: 0.04em; - padding: 0.2rem 1rem 0; - width: 100%; - text-shadow: 0 1px 4px rgba(0,0,0,0.4); -} - -/* ── Contextual sub-prompt (§8a) ────────────────────────────────────── */ -.game-sub-prompt { - font-family: var(--font-ui); - font-size: 0.72rem; - color: rgba(240,228,192,0.5); - text-align: center; - letter-spacing: 0.04em; - padding: 0.15rem 1rem 0; - width: 100%; -} - -/* ── Player score panel ─────────────────────────────────────────────── */ -.player-score-panel { - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.45rem 1.25rem; - font-size: 0.88rem; - box-shadow: 0 2px 6px rgba(0,0,0,0.25); - width: 100%; - border-top: 2px solid var(--ui-gold-dark); - display: flex; - align-items: center; - gap: 1.5rem; -} - -.player-score-header { - flex-shrink: 0; - min-width: 90px; -} - -.player-name { - font-family: var(--font-display); - font-weight: 600; - font-size: 1.05rem; - color: var(--ui-ink); - letter-spacing: 0.02em; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - min-width: 0; -} - -.score-bars { display: flex; flex-direction: row; gap: 1.5rem; flex: 1; align-items: center; } - -.score-bar-row { - display: flex; - align-items: center; - gap: 0.5rem; - flex: 1; -} - -.score-bar-label { - font-size: 0.75rem; - color: #665544; - width: 3rem; - text-align: right; - flex-shrink: 0; -} - -/* ── Points bar ─────────────────────────────────────────────────────── */ -.score-bar { - flex: 1; - max-width: 220px; - height: 8px; - background: rgba(0,0,0,0.1); - border-radius: 4px; - overflow: hidden; - flex-shrink: 0; -} - -.score-bar-fill { - height: 100%; - border-radius: 4px; - transition: width 0.35s ease-out; -} - -.score-bar-points { background: linear-gradient(90deg, var(--ui-green-accent), #5a9b3a); } - -.score-bar-value { - font-size: 0.75rem; - color: #665544; - min-width: 2.5rem; - font-variant-numeric: tabular-nums; -} - -/* ── Hole peg tracker (§7a) ─────────────────────────────────────────── */ -.peg-track { - display: flex; - align-items: center; - gap: 3px; - flex-shrink: 0; -} - -.peg-hole { - width: 10px; - height: 10px; - border-radius: 50%; - border: 1.5px solid rgba(138,106,40,0.45); - background: rgba(0,0,0,0.06); - flex-shrink: 0; - transition: background 0.3s ease-out, border-color 0.3s, box-shadow 0.3s; -} - -.peg-hole.filled { - background: var(--ui-gold); - border-color: var(--ui-gold-dark); - box-shadow: 0 0 4px rgba(200,164,72,0.6); -} - -.bredouille-badge { - font-size: 0.62rem; - font-weight: 500; - color: #fff8e0; - background: linear-gradient(135deg, #c88800, #8a5800); - border: 1px solid rgba(200,164,72,0.5); - border-radius: 3px; - padding: 0.1em 0.4em; - letter-spacing: 0.06em; - cursor: default; - box-shadow: 0 1px 3px rgba(0,0,0,0.25); -} - -/* ── Merged scoreboard (both players, above board) ──────────────────── */ -.merged-score-panel { - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.5rem 1.25rem 0.45rem; - font-size: 0.88rem; - box-shadow: 0 2px 6px rgba(0,0,0,0.25); - width: 100%; - border-top: 2px solid var(--ui-gold-dark); - display: flex; - flex-direction: column; - gap: 0.2rem; -} - -.score-row { - display: flex; - align-items: center; - gap: 1rem; -} - -.score-row-name { - width: 120px; - flex-shrink: 0; - display: flex; - align-items: baseline; - gap: 0.35rem; - overflow: hidden; -} - -.you-tag { - font-family: var(--font-ui); - font-size: 0.7rem; - color: #887766; - font-style: italic; - white-space: nowrap; - flex-shrink: 0; -} - -/* ── Jackpot points counter ─────────────────────────────────────────── */ -.pts-counter-wrap { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - width: 72px; - flex-shrink: 0; - padding-bottom: 4px; -} - -.pts-ghost-bar-track { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 3px; - background: rgba(0,0,0,0.07); - border-radius: 2px; - overflow: hidden; -} - -.pts-ghost-bar-fill { - height: 100%; - background: rgba(58,107,42,0.45); - border-radius: 2px; -} - -.pts-ghost-bar-opp { - background: rgba(122,30,42,0.4); -} - -.pts-counter-row { - display: flex; - align-items: baseline; - gap: 0.1rem; -} - -.pts-counter { - font-family: var(--font-display); - font-size: 1.9rem; - font-weight: 600; - color: var(--ui-ink); - line-height: 1; - font-variant-numeric: tabular-nums; - min-width: 1.4em; - text-align: right; -} - -.pts-max { - font-family: var(--font-ui); - font-size: 0.7rem; - color: #998877; - line-height: 1; - padding-bottom: 2px; -} - -/* ── Hole pegs — larger and coloured (me = green, opp = red) ─────────── */ -.merged-score-panel .peg-track { - gap: 4px; -} - -.merged-score-panel .peg-hole { - width: 14px; - height: 14px; - border-radius: 50%; - border: 1.5px solid rgba(138,106,40,0.3); - background: rgba(0,0,0,0.06); - flex-shrink: 0; - transition: background 0.3s ease-out, border-color 0.3s, box-shadow 0.3s; -} - -.merged-score-panel .peg-hole.filled { - background: #5aab38; - border-color: #3a7828; - box-shadow: 0 0 5px rgba(90,171,56,0.55); -} - -.merged-score-panel .peg-hole.peg-opp.filled { - background: #c05030; - border-color: #8a3018; - box-shadow: 0 0 5px rgba(192,80,48,0.55); -} - -/* Peg pop-in animation when a new hole is scored */ -@keyframes peg-pop { - 0% { transform: scale(0.15); opacity: 0; } - 45% { transform: scale(1.55); } - 70% { transform: scale(0.88); } - 100% { transform: scale(1.0); opacity: 1; } -} - -.merged-score-panel .peg-hole.peg-new { - animation: peg-pop 0.52s cubic-bezier(0.22, 0.61, 0.36, 1) forwards; -} - -/* Thin separator between the two player rows */ -.score-row-sep { - height: 1px; - background: rgba(0,0,0,0.07); - margin: 0.05rem 0; -} - -/* ── Non-blocking hole flash (replaces old toast) ───────────────────── */ -@keyframes hole-flash-in-out { - 0% { opacity: 0; transform: translateY(-3px); } - 14% { opacity: 1; transform: translateY(0); } - 65% { opacity: 1; } - 100% { opacity: 0; transform: translateY(2px); } -} - -.hole-flash { - margin-left: auto; - flex-shrink: 0; - white-space: nowrap; - font-family: var(--font-display); - font-size: 0.88rem; - font-weight: 600; - color: var(--ui-green-accent); - letter-spacing: 0.05em; - animation: hole-flash-in-out 2.5s ease-out forwards; - pointer-events: none; -} - -.hole-flash.hole-flash-bredouille { - color: var(--ui-gold-dark); -} - -/* ── Game bottom strip — status, hints, buttons on cream ────────────── */ -.game-bottom-strip { - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.55rem 1.25rem 0.65rem; - width: 100%; - box-shadow: 0 2px 6px rgba(0,0,0,0.2); - border-top: 2px solid var(--ui-gold-dark); - display: flex; - flex-direction: column; - align-items: center; - gap: 0.35rem; - min-height: 3.2rem; -} - -/* Override text colours for the parchment background context */ -.game-bottom-strip .game-status { - color: var(--ui-ink); - text-shadow: none; - padding: 0; - font-size: 1.05rem; - width: auto; -} - -.game-bottom-strip .game-sub-prompt { - color: #887766; - padding: 0; - width: auto; -} - -/* ── Board + side panel ─────────────────────────────────────────────── */ -.board-and-panel { - position: relative; -} - -.side-panel { - position: absolute; - right: -8px; - top: 10px; - z-index: 20; - display: flex; - flex-direction: column; - gap: 0.5rem; - padding-top: 0.15rem; - pointer-events: none; -} - -.action-buttons { display: flex; flex-direction: column; gap: 0.5rem; } - -/* ── Dice bar ───────────────────────────────────────────────────────── */ -.dice-bar { - display: flex; - align-items: center; - gap: 0.6rem; - padding: 0.4rem 0.6rem; - background: rgba(42,21,8,0.15); - border-radius: 6px; - border: 1px solid rgba(200,164,72,0.2); - width: fit-content; -} - -/* ── Die face (SVG) ─────────────────────────────────────────────────── */ -@keyframes die-tumble { - 0% { transform: rotate(-45deg) scale(0.4) translateY(-8px); opacity: 0; } - 25% { transform: rotate(18deg) scale(1.22) translateY(0); opacity: 1; } - 45% { transform: rotate(-10deg) scale(0.91); } - 62% { transform: rotate(6deg) scale(1.06); } - 76% { transform: rotate(-3deg) scale(0.98); } - 88% { transform: rotate(1.5deg) scale(1.01); } - 100% { transform: rotate(0deg) scale(1); opacity: 1; } -} - -.die-face { - filter: drop-shadow(0 2px 3px rgba(0,0,0,0.3)); - animation: die-tumble 0.55s cubic-bezier(0.22, 0.61, 0.36, 1) both; -} - -.die-face rect { - fill: #fffef0; - stroke: #2a1a00; - stroke-width: 2; - transition: fill 0.18s, stroke 0.18s; -} -.die-face circle { - fill: #1a0a00; - transition: fill 0.18s; -} - -.bar-die-slot { - display: flex; - align-items: center; - justify-content: center; -} - -.die-face.die-double rect { stroke: var(--ui-gold); stroke-width: 2.5; } -.die-face.die-double { - filter: drop-shadow(0 0 6px rgba(200,164,72,0.7)) drop-shadow(0 2px 3px rgba(0,0,0,0.3)); -} - -.die-face.die-used { animation: none; opacity: 0.55; } -.die-face.die-used rect { fill: #d4d0c4; stroke: #9a8a70; } -.die-face.die-used circle { fill: #9a8a70; } - -.die-face .die-question { fill: #1a0a00; font-family: sans-serif; } -.die-face.die-used .die-question { fill: #9a8a70; } - -/* ── Jan panel ──────────────────────────────────────────────────────── */ -.jan-panel { - display: flex; - flex-direction: column; - gap: 2px; - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.4rem 0.9rem; - font-size: 0.88rem; - box-shadow: 0 1px 4px rgba(0,0,0,0.15); - min-width: 260px; - border-top: 2px solid rgba(138,106,40,0.35); -} - -.jan-row { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 2px 4px; - border-radius: 3px; -} -.jan-expandable { cursor: pointer; } -.jan-expandable:hover { background: rgba(0,0,0,0.05); } - -.jan-positive { color: #1a5c1a; } -.jan-negative { color: #8b1a1a; } - -.jan-label { flex: 1; } -.jan-tag { - font-size: 0.72rem; - padding: 0.1em 0.4em; - border-radius: 3px; - background: rgba(0,0,0,0.07); - color: #665544; - white-space: nowrap; -} -.jan-pts { font-weight: 600; text-align: right; min-width: 3rem; } - -.jan-moves { padding: 1px 4px 4px 1rem; display: flex; flex-direction: column; gap: 2px; } -.jan-moves.hidden { display: none; } -.jan-move-line { font-family: monospace; font-size: 0.78rem; color: #555; } - -/* ── Game-over overlay (§12) ────────────────────────────────────────── */ -.game-over-overlay { - position: fixed; - inset: 0; - background: rgba(0,0,0,0.65); - display: flex; - align-items: center; - justify-content: center; - z-index: 100; -} - -@keyframes game-over-appear { - from { transform: translateY(-24px) scale(0.94); opacity: 0; } - to { transform: translateY(0) scale(1); opacity: 1; } -} - -.game-over-box { - background: var(--ui-parchment); - border-radius: 8px; - padding: 2.5rem 3rem; - text-align: center; - box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 0 2px var(--ui-gold-dark); - display: flex; - flex-direction: column; - gap: 1.1rem; - min-width: 300px; - animation: game-over-appear 0.4s cubic-bezier(0.22, 0.61, 0.36, 1); -} - -.game-over-box h2 { - font-family: var(--font-display); - font-size: 2rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.06em; -} - -.game-over-winner { - font-family: var(--font-display); - font-size: 1.25rem; - color: var(--ui-green-accent); - font-style: italic; -} - -.game-over-score { - display: flex; - align-items: center; - justify-content: center; - gap: 0.75rem; - padding: 0.6rem 1rem; - background: rgba(0,0,0,0.05); - border-radius: 5px; - border: 1px solid rgba(138,106,40,0.2); -} - -.game-over-score-name { - font-family: var(--font-display); - font-size: 0.9rem; - color: #665544; - font-style: italic; - max-width: 80px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.game-over-score-nums { - font-family: var(--font-display); - font-size: 2.25rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.08em; - line-height: 1; -} - -.game-over-actions { display: flex; gap: 0.75rem; justify-content: center; } - -/* ── Score-area: position:relative wrapper for merged panel + scoring ── */ -.score-area { - position: relative; - width: 100%; -} - -/* ── Scoring panels container — right of the hole counter ───────────── */ -/* Stacked column, right-aligned, covering the free space in each row. */ -/* overflow:visible lets tall panels float over the board below. */ -.scoring-panels-container { - position: absolute; - top: 0; - bottom: 0; - right: 0; - display: flex; - flex-direction: column; - justify-content: space-around; - align-items: flex-end; - padding: 4px 8px; - z-index: 10; - pointer-events: none; - overflow: visible; -} - -/* ── Scoring notification panel (§6b) ───────────────────────────────── */ -@keyframes scoring-panel-enter { - from { opacity: 0; transform: translateX(10px); } - to { opacity: 1; transform: translateX(0); } -} - -.scoring-panel-wrapper { - pointer-events: auto; - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 3px; - animation: scoring-panel-enter 0.3s ease-out; -} - -/* "+" expand button: hidden while the panel is expanded */ -.scoring-panel-wrapper:not(.scoring-minimized) .scoring-expand-btn { - display: none; -} - -/* Full panel card: hidden once minimised */ -.scoring-panel-wrapper.scoring-minimized .scoring-panel { - display: none; -} - -/* "+" expand button ─────────────────────────────────────────────────── */ -.scoring-expand-btn { - font-family: var(--font-display); - font-size: 0.9rem; - line-height: 1; - background: var(--ui-parchment); - border: 1.5px solid var(--ui-gold-dark); - border-radius: 3px; - padding: 2px 7px; - cursor: pointer; - color: var(--ui-ink); - opacity: 0.72; - box-shadow: 0 1px 3px rgba(0,0,0,0.18); - transition: opacity 0.15s; -} -.scoring-expand-btn:hover { opacity: 1; } - -/* ── Panel head: scoring total + "−" collapse link ──────────────────── */ -.scoring-panel-head { - display: flex; - align-items: baseline; - gap: 0.5rem; -} - -.scoring-collapse-btn { - font-size: 0.78rem; - line-height: 1; - background: none; - border: none; - cursor: pointer; - color: rgba(0,0,0,0.35); - padding: 0 1px; - margin-left: auto; - flex-shrink: 0; - transition: color 0.15s; -} -.scoring-collapse-btn:hover { color: rgba(0,0,0,0.65); } - -/* ── Inner scoring card ─────────────────────────────────────────────── */ -.scoring-panel { - background: var(--ui-parchment); - border-radius: 5px; - padding: 0.45rem 0.85rem; - font-size: 0.84rem; - box-shadow: 0 2px 8px rgba(0,0,0,0.22); - border-left: 3px solid var(--ui-green-accent); - display: flex; - flex-direction: column; - gap: 4px; - width: 320px; -} - -.scoring-total { - font-family: var(--font-display); - font-weight: 600; - font-size: 1rem; - color: #1a5c1a; - white-space: nowrap; -} - -.scoring-jan-row { - display: flex; - align-items: center; - gap: 0.4rem; - padding: 2px 3px; - border-radius: 3px; - cursor: default; - white-space: nowrap; -} -.scoring-jan-row:hover { background: rgba(0,0,0,0.05); } - -.scoring-panel-opp { border-left-color: var(--board-rail); } -.scoring-panel-opp .scoring-total { color: var(--ui-red-accent); } - -.scoring-hole { - display: flex; - align-items: center; - gap: 0.4rem; - font-weight: 600; - color: var(--ui-red-accent); - margin-top: 3px; - padding-top: 4px; - border-top: 1px solid rgba(0,0,0,0.1); -} - -.hold-go-buttons { display: flex; gap: 0.5rem; margin-top: 4px; } - -@media (min-width: 1492px) { - .side-panel { - right: auto; - left: calc(100% + 1rem); - } -} - -/* ── Board wrapper ──────────────────────────────────────────────────── */ -.board-wrapper { - display: flex; - flex-direction: column; - gap: 3px; -} - -/* ── Zone labels (§2a) ──────────────────────────────────────────────── */ -.zone-labels-row { - display: flex; - gap: 4px; - padding: 0 8px; -} - -.zone-label { - font-family: var(--font-display); - font-size: 0.57rem; - font-style: italic; - color: rgba(240,228,192,0.48); - letter-spacing: 0.1em; - text-align: center; - pointer-events: none; - line-height: 1; -} - -.zone-label-quarter { width: 370px; flex-shrink: 0; } -.zone-label-bar { width: 68px; flex-shrink: 0; } - -/* ── Board ──────────────────────────────────────────────────────────── */ -.board { - background: var(--board-felt); - border: 4px solid var(--board-rail); - border-radius: 6px; - padding: 4px; - display: flex; - flex-direction: column; - gap: 4px; - user-select: none; - box-shadow: - 0 6px 16px rgba(0,0,0,0.5), - inset 0 1px 0 rgba(255,255,255,0.04); - position: relative; -} - -.board-row { display: flex; gap: 4px; } -.board-quarter { display: flex; gap: 2px; } - -.board-bar { - width: 68px; - background: var(--board-rail); - border-radius: 4px; - box-shadow: inset 0 0 6px rgba(0,0,0,0.5); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - overflow: visible; -} - -.board-center-bar { - height: 12px; - background: var(--board-rail); - border-radius: 2px; - box-shadow: inset 0 0 4px rgba(0,0,0,0.4); -} - -/* ── Fields (§1) ────────────────────────────────────────────────────── */ -.field { - --fc: var(--field-ivory); - width: 60px; - height: 180px; - background: transparent; - isolation: isolate; - border-radius: 3px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: flex-end; - padding: 4px 2px; - position: relative; -} - -.field::before { - content: ''; - position: absolute; - inset: 0; - z-index: -1; - background: var(--fc); - clip-path: polygon(0% 100%, 50% 0%, 100% 100%); - transition: background 0.12s; -} - -.top-row .field::before { - clip-path: polygon(0% 0%, 100% 0%, 50% 100%); -} - -.top-row .field { justify-content: flex-start; } - -/* ── Zone alternating colours ────────────────────────────────── */ -.board-quarter .field.zone-petit:nth-child(odd), -.board-quarter .field.zone-grand:nth-child(odd) { --fc: var(--field-burgundy); } -.board-quarter .field.zone-petit:nth-child(even), -.board-quarter .field.zone-grand:nth-child(even) { --fc: var(--field-ivory); } - -.board-quarter .field.zone-opponent:nth-child(odd), -.board-quarter .field.zone-retour:nth-child(odd) { --fc: var(--field-burgundy); } -.board-quarter .field.zone-opponent:nth-child(even), -.board-quarter .field.zone-retour:nth-child(even) { --fc: var(--field-ivory); } - -/* ── Point indicator: first N fields reflect each player's score & bredouille */ -.board-quarter .field.zone-petit.point-bredouille:nth-child(odd), -.board-quarter .field.zone-grand.point-bredouille:nth-child(odd) { --fc: var(--field-blue-light); } -.board-quarter .field.zone-petit.point-bredouille:nth-child(even), -.board-quarter .field.zone-grand.point-bredouille:nth-child(even) { --fc: var(--field-blue); } - -.board-quarter .field.zone-petit.point-no-bredouille:nth-child(odd), -.board-quarter .field.zone-grand.point-no-bredouille:nth-child(odd) { --fc: var(--field-blue-light); } -.board-quarter .field.zone-petit.point-no-bredouille:nth-child(even), -.board-quarter .field.zone-grand.point-no-bredouille:nth-child(even) { --fc: var(--field-blue); } - -.board-quarter .field.zone-opponent.point-bredouille:nth-child(odd), -.board-quarter .field.zone-retour.point-bredouille:nth-child(odd) { --fc: var(--field-blue-light); } -.board-quarter .field.zone-opponent.point-bredouille:nth-child(even), -.board-quarter .field.zone-retour.point-bredouille:nth-child(even) { --fc: var(--field-blue); } - -.board-quarter .field.zone-opponent.point-no-bredouille:nth-child(odd), -.board-quarter .field.zone-retour.point-no-bredouille:nth-child(odd) { --fc: var(--field-blue-light); } -.board-quarter .field.zone-opponent.point-no-bredouille:nth-child(even), -.board-quarter .field.zone-retour.point-no-bredouille:nth-child(even) { --fc: var(--field-blue); } - -.field.corner::after { - content: '♛'; - position: absolute; - z-index: -1; - bottom: 22px; - font-size: 0.7rem; - color: rgba(255,248,210,0.38); - pointer-events: none; - line-height: 1; -} -.top-row .field.corner::after { bottom: auto; top: 22px; } - -@keyframes corner-pulse { - 0%, 100% { filter: drop-shadow(0 0 0px rgba(200,164,72,0)); } - 50% { filter: drop-shadow(0 0 7px rgba(200,164,72,0.55)); } -} -.field.corner.corner-available { - animation: corner-pulse 1.5s ease-in-out infinite; -} - -@keyframes exit-glow { - 0%, 100% { filter: drop-shadow(0 0 0px rgba(232,192,96,0)); } - 50% { filter: drop-shadow(0 0 5px rgba(232,192,96,0.5)); } -} -.field.exit-eligible { - animation: exit-glow 2s ease-in-out infinite; -} - -/* ── Exit sign (§8c) — circle+arrow outside the board ──────────────── */ -.exit-btn { - pointer-events: none; - opacity: 0.3; - transition: opacity 0.2s, transform 0.15s; -} -.exit-btn.exit-active { - pointer-events: auto; - cursor: pointer; - opacity: 1; - animation: exit-btn-pulse 1.4s ease-in-out infinite; -} -.exit-btn.exit-active:hover { - transform: scale(1.1); -} -@keyframes exit-btn-pulse { - 0%, 100% { filter: drop-shadow(0 0 3px rgba(200,160,20,0.3)); } - 50% { filter: drop-shadow(0 0 9px rgba(200,160,20,0.85)); } -} - -.field.jan-hovered { - --fc: rgba(190, 140, 35, 0.8) !important; -} - -@keyframes hit-ripple { - from { transform: translate(-50%, -50%) scale(0.4); opacity: 0.9; } - to { transform: translate(-50%, -50%) scale(2.2); opacity: 0; } -} -.hit-ripple { - position: absolute; - left: 50%; - width: 36px; - height: 36px; - border-radius: 50%; - border: 2px solid rgba(200, 164, 72, 0.9); - pointer-events: none; - animation: hit-ripple 0.5s ease-out forwards; -} -.hit-ripple-top { top: 26px; } -.hit-ripple-bot { bottom: 26px; } - -.field.clickable { - cursor: pointer; -} -.field.clickable:hover { - --fc: rgba(200,170,50,0.18) !important; -} -.field.selected { - /* natural triangle color; tab is the indicator */ -} - -/* ── Tab indicators: small markers at the field's wide base ──────── */ -/* Bot-row: tabs hang below; top-row: tabs hang above. */ -/* The tab sits at ≈ -6px which lands on the board's wooden rail. */ - -.field.clickable::after, -.field.selected::after { - content: ''; - position: absolute; - left: 50%; - transform: translateX(-50%); - width: 22px; - height: 8px; - pointer-events: none; - z-index: 2; -} - -.bot-row .field.clickable::after, -.bot-row .field.selected::after { - bottom: -6px; - top: auto; - border-radius: 0 0 10px 10px; -} -.top-row .field.clickable::after, -.top-row .field.selected::after { - top: -6px; - bottom: auto; - border-radius: 10px 10px 0 0; -} - -/* Possible origin: hollow gold outline */ -.field.clickable:not(.dest):not(.selected)::after { - background: rgba(210,170,30,0.15); - border: 1.5px solid rgba(210,170,30,0.75); - box-shadow: 0 0 4px rgba(210,170,30,0.3); -} - -/* Selected origin: filled amber, breathing glow */ -.field.selected::after { - background: linear-gradient(to bottom, #e8b020, #c07808); - border: 1px solid rgba(255,225,65,0.55); - animation: tab-pulse 1.2s ease-in-out infinite; -} - -@keyframes tab-pulse { - 0%, 100% { box-shadow: 0 0 5px rgba(220,155,15,0.55), 0 0 2px rgba(255,220,50,0.3); } - 50% { box-shadow: 0 0 13px rgba(240,178,22,0.88), 0 0 6px rgba(255,230,60,0.6); } -} - -/* Valid destination: soft ivory/pearl */ -.field.clickable.dest:not(.selected)::after { - background: rgba(240,230,205,0.88); - border: 1.5px solid rgba(190,165,105,0.65); - box-shadow: 0 0 3px rgba(190,165,105,0.2); -} -.field.clickable.dest:not(.selected):hover::after { - background: rgba(228,210,162,0.95); - border-color: rgba(210,175,40,0.72); - box-shadow: 0 0 7px rgba(210,175,40,0.42); -} - -.field-num { - font-size: 0.58rem; - color: rgba(0,0,0,0.28); - position: absolute; - bottom: 3px; - line-height: 1; - font-variant-numeric: tabular-nums; -} - -.board-quarter .field.zone-petit:nth-child(odd) .field-num, -.board-quarter .field.zone-grand:nth-child(odd) .field-num, -.board-quarter .field.zone-retour:nth-child(odd) .field-num, -.board-quarter .field.zone-opponent:nth-child(odd) .field-num { - color: rgba(240,215,190,0.38); -} - -.field.corner .field-num { color: rgba(255,248,200,0.4); } -.top-row .field-num { bottom: auto; top: 3px; } - -/* ── Checkers ───────────────────────────────────────────────────────── */ -.checker-stack { - display: flex; - flex-direction: column; - align-items: center; -} - -.checker { - width: 40px; - height: 40px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.78rem; - font-weight: 600; - flex-shrink: 0; -} - -.checker + .checker { margin-top: -4px; } - -.checker.white { - background-image: - radial-gradient(ellipse 50% 35% at 36% 30%, - rgba(255,255,255,0.65) 0%, transparent 100%), - radial-gradient(circle, - transparent 68%, rgba(160,130,70,0.22) 68.5%, - rgba(160,130,70,0.22) 71.5%, transparent 72%), - radial-gradient(circle, - transparent 43%, rgba(160,130,70,0.17) 43.5%, - rgba(160,130,70,0.17) 46.5%, transparent 47%), - radial-gradient(circle at 38% 32%, - #ffffff 0%, var(--checker-white) 52%, #c0b288 100%); - border: 1.8px solid var(--checker-ring); - box-shadow: - 0 2px 6px rgba(0,0,0,0.4), - inset 0 -1px 3px rgba(0,0,0,0.15); - color: #443322; -} - -.checker.black { - background-image: - radial-gradient(ellipse 40% 28% at 36% 30%, - rgba(110,65,30,0.38) 0%, transparent 100%), - radial-gradient(circle, - transparent 68%, rgba(200,164,72,0.18) 68.5%, - rgba(200,164,72,0.18) 71.5%, transparent 72%), - radial-gradient(circle, - transparent 43%, rgba(200,164,72,0.13) 43.5%, - rgba(200,164,72,0.13) 46.5%, transparent 47%), - radial-gradient(circle at 38% 32%, - #4a2e1a 0%, #1c1008 45%, var(--checker-black) 100%); - border: 1.8px solid var(--checker-ring); - box-shadow: - 0 2px 6px rgba(0,0,0,0.55), - inset 0 -1px 3px rgba(0,0,0,0.4); - color: #c8b898; -} - -/* ── Hole toast (§6a) ───────────────────────────────────────────────── */ -@keyframes toast-rise { - from { transform: translate(-50%, -40%); opacity: 0; } - to { transform: translate(-50%, -50%); opacity: 1; } -} -@keyframes toast-fade { - from { opacity: 1; } - to { opacity: 0; pointer-events: none; } -} - -.hole-toast { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: rgba(22,10,2,0.93); - border: 2px solid var(--ui-gold); - border-radius: 8px; - padding: 1.5rem 3.5rem; - text-align: center; - z-index: 50; - pointer-events: none; - box-shadow: - 0 12px 40px rgba(0,0,0,0.65), - 0 0 0 1px rgba(200,164,72,0.25), - inset 0 1px 0 rgba(200,164,72,0.1); - animation: - toast-rise 0.25s cubic-bezier(0.22, 0.61, 0.36, 1), - toast-fade 0.5s ease-in 1.4s forwards; -} - -.hole-toast-title { - font-family: var(--font-display); - font-size: 3.25rem; - font-weight: 600; - color: var(--ui-gold); - letter-spacing: 0.1em; - line-height: 1; -} - -.hole-toast-count { - font-family: var(--font-display); - font-size: 1.1rem; - color: rgba(200,164,72,0.68); - margin-top: 0.35rem; - letter-spacing: 0.06em; -} - -.hole-toast-bredouille { - font-family: var(--font-ui); - font-size: 0.75rem; - letter-spacing: 0.08em; - color: rgba(200,164,72,0.55); - margin-top: 0.4rem; - text-transform: uppercase; -} - -@keyframes bredouille-shimmer { - 0%, 100% { box-shadow: 0 12px 40px rgba(0,0,0,0.65), 0 0 0 2px rgba(200,164,72,0.4), inset 0 0 0 rgba(200,164,72,0); } - 50% { box-shadow: 0 12px 40px rgba(0,0,0,0.65), 0 0 0 4px rgba(200,164,72,0.7), inset 0 0 24px rgba(200,164,72,0.08); } -} -.hole-toast.hole-toast-bredouille { - border-width: 2.5px; - border-color: var(--ui-gold); - padding: 2rem 4rem; - animation: - toast-rise 0.3s cubic-bezier(0.22, 0.61, 0.36, 1), - bredouille-shimmer 0.9s ease-in-out 0.3s 2, - toast-fade 0.5s ease-in 2.2s forwards; -} -.hole-toast.hole-toast-bredouille .hole-toast-title { font-size: 3.75rem; } -.hole-toast.hole-toast-bredouille .hole-toast-bredouille { - font-size: 0.85rem; - color: rgba(200,164,72,0.8); - letter-spacing: 0.14em; -} - -/* ── Checker slide animation (§4a) ─────────────────────────────────── */ -@keyframes checker-slide-in { - from { transform: translate(var(--slide-dx, 0px), var(--slide-dy, 0px)); } - to { transform: none; } -} -.checker.arriving { - animation: checker-slide-in 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94); -} -.field:has(.checker.arriving) { - isolation: auto; - z-index: 10; - position: relative; -} - -/* ── Checker lift on selected field (§4b) ───────────────────────────── */ -.field.selected .checker-stack { - transform: translateY(-5px); - filter: drop-shadow(0 8px 12px rgba(0,0,0,0.6)); - transition: transform 0.12s ease-out, filter 0.12s ease-out; -} - -/* ── Action buttons below board (§10c) ──────────────────────────────── */ -.board-actions { - display: flex; - gap: 0.55rem; - justify-content: center; - align-items: center; - flex-wrap: wrap; - min-height: 2rem; -} - -/* ── Pre-game ceremony overlay ──────────────────────────────────────────── */ -.ceremony-overlay { - position: fixed; - inset: 0; - background: rgba(0,0,0,0.65); - display: flex; - align-items: center; - justify-content: center; - z-index: 100; -} - -.ceremony-box { - background: var(--ui-parchment); - border-radius: 8px; - padding: 2.5rem 3rem; - text-align: center; - box-shadow: 0 12px 40px rgba(0,0,0,0.5), 0 0 0 2px var(--ui-gold-dark); - display: flex; - flex-direction: column; - align-items: center; - gap: 1.4rem; - min-width: 300px; - animation: game-over-appear 0.4s cubic-bezier(0.22, 0.61, 0.36, 1); -} - -.ceremony-box h2 { - font-family: var(--font-display); - font-size: 1.8rem; - font-weight: 600; - color: var(--ui-ink); - letter-spacing: 0.06em; -} - -.ceremony-dice { - display: flex; - gap: 3rem; - align-items: flex-end; -} - -.ceremony-die-slot { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; -} - -.ceremony-die-label { - font-family: var(--font-ui); - font-size: 0.85rem; - color: var(--ui-ink); - font-weight: 500; -} - -.ceremony-tie { - font-family: var(--font-display); - font-size: 1rem; - color: var(--ui-red-accent); - font-style: italic; -} - -.ceremony-result { - font-family: var(--font-display); - font-size: 1.15rem; - font-weight: 600; - color: var(--ui-gold-dark); - letter-spacing: 0.04em; -} - -/* ── Nickname modal (anonymous player name chooser) ─────────────────── */ -.nickname-backdrop { - position: fixed; - inset: 0; - background: rgba(0,0,0,0.6); - display: flex; - align-items: center; - justify-content: center; - z-index: 300; -} - -.nickname-modal { - background: var(--ui-parchment); - border-radius: 8px; - padding: 2rem 2rem 1.75rem; - width: min(360px, 90vw); - display: flex; - flex-direction: column; - gap: 1rem; - box-shadow: - 0 20px 60px rgba(0,0,0,0.55), - 0 0 0 1px rgba(200,164,72,0.35), - 0 0 0 5px rgba(42,21,8,0.9), - 0 0 0 6px rgba(200,164,72,0.2); - animation: game-over-appear 0.25s cubic-bezier(0.22, 0.61, 0.36, 1); -} - -.nickname-modal-title { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 600; - color: var(--ui-ink); - text-align: center; - letter-spacing: 0.04em; -} - -.nickname-modal-hint { - font-family: var(--font-ui); - font-size: 0.8rem; - color: rgba(42,26,8,0.6); - text-align: center; - margin-bottom: -0.25rem; -} - -.nickname-modal-alt { - text-align: center; - font-size: 0.8rem; - color: rgba(42,26,8,0.55); - padding-top: 0.5rem; - border-top: 1px solid rgba(138,106,40,0.2); -} - -.nickname-modal-alt a { - color: var(--ui-gold-dark); - text-decoration: none; - font-weight: 500; -} - -.nickname-modal-alt a:hover { text-decoration: underline; } - -/* ── Game hamburger button (☰ → ✕ animation) ────────────────────────── */ -.game-hamburger { - position: fixed; - top: 0.6rem; - left: 0.6rem; - z-index: 251; - width: 36px; - height: 36px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 5px; - background: var(--board-rail); - border: 1px solid rgba(200,164,72,0.35); - border-radius: 5px; - cursor: pointer; - transition: background 0.15s, border-color 0.15s; -} -.game-hamburger:hover { - background: #3d1f0a; - border-color: rgba(200,164,72,0.65); -} - -.hb-bar { - display: block; - width: 16px; - height: 2px; - background: var(--ui-parchment); - border-radius: 1px; - transition: transform 0.25s cubic-bezier(0.22, 0.61, 0.36, 1), opacity 0.2s; - transform-origin: center; -} -/* Top bar rotates down to form \ */ -.game-hamburger-open .hb-top { transform: translateY(7px) rotate(45deg); } -/* Middle bar fades out */ -.game-hamburger-open .hb-mid { opacity: 0; transform: scaleX(0); } -/* Bottom bar rotates up to form / */ -.game-hamburger-open .hb-bot { transform: translateY(-7px) rotate(-45deg); } - -/* ── Game sidebar ────────────────────────────────────────────────────── */ -.game-sidebar { - position: fixed; - top: 0; - left: 0; - height: 100vh; - width: 280px; - z-index: 250; - background: var(--board-rail); - border-right: 1px solid rgba(200,164,72,0.25); - display: flex; - flex-direction: column; - transform: translateX(-100%); - transition: transform 0.25s cubic-bezier(0.22, 0.61, 0.36, 1); - overflow-y: auto; -} -.game-sidebar-open { - transform: translateX(0); -} - -.game-sidebar-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 1rem 1rem; - border-bottom: 1px solid rgba(200,164,72,0.2); - flex-shrink: 0; -} - -.game-sidebar-brand { - font-family: var(--font-display); - font-size: 1.3rem; - font-weight: 600; - color: var(--ui-gold); - letter-spacing: 0.06em; - margin-left: 45px; -} - -.game-sidebar-close { - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: 1px solid rgba(200,164,72,0.25); - border-radius: 4px; - color: var(--ui-parchment); - font-size: 0.85rem; - cursor: pointer; - opacity: 0.65; - transition: opacity 0.15s; -} -.game-sidebar-close:hover { opacity: 1; } - -.game-sidebar-section { - padding: 0.9rem 1rem; - border-bottom: 1px solid rgba(200,164,72,0.12); - display: flex; - flex-direction: row; - gap: 0.55rem; -} - -.game-sidebar-label { - font-size: 0.7rem; - font-family: var(--font-ui); - letter-spacing: 0.07em; - text-transform: uppercase; - color: rgba(242,232,208,0.45); -} - -.game-sidebar-link { - font-family: var(--font-ui); - font-size: 0.85rem; - color: var(--ui-parchment); - text-decoration: none; - opacity: 0.8; - transition: opacity 0.15s; -} -.game-sidebar-link:hover { opacity: 1; text-decoration: underline; text-underline-offset: 2px; } - -.game-sidebar-btn { - font-family: var(--font-ui); - font-size: 0.82rem; - padding: 0.4rem 0.75rem; - border: 1px solid rgba(200,164,72,0.35); - border-radius: 4px; - background: rgba(200,164,72,0.1); - color: var(--ui-parchment); - cursor: pointer; - text-align: left; - transition: background 0.15s; -} -.game-sidebar-btn:hover { background: rgba(200,164,72,0.22); } - -.game-sidebar-btn-newgame { - background: rgba(58,107,42,0.25); - border-color: rgba(58,107,42,0.55); - font-weight: 500; -} -.game-sidebar-btn-newgame:hover { background: rgba(58,107,42,0.42); } - -.game-sidebar-qr { - width: 100%; - height: auto; - aspect-ratio: 1; - max-width: 200px; - margin: 0 auto; -} diff --git a/clients/web/locales/en.json b/clients/web/locales/en.json deleted file mode 100644 index 03ba37c..0000000 --- a/clients/web/locales/en.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "room_name_placeholder": "Room name", - "create_room": "Create Room", - "join_room": "Join Room", - "connecting": "Connecting…", - "game_over": "Game over", - "waiting_for_opponent": "Waiting for opponent…", - "your_turn_roll": "Your turn — roll the dice", - "hold_or_go": "Hold or Go?", - "select_move": "Move a checker ({{ n }} of 2)", - "your_turn": "Your turn", - "opponent_turn": "Opponent's turn", - "room_label": "Room: {{ id }}", - "quit": "Quit", - "roll_dice": "Roll dice", - "go": "Go", - "empty_move": "Empty move", - "cancel_move": "Cancel move", - "debug_section": "Debug", - "take_snapshot": "Take snapshot", - "snapshot_copied": "Copied!", - "replay_snapshot": "Replay snapshot", - "replay_paste_hint": "Paste a snapshot JSON to start a bot game from that position.", - "replay_start": "Start", - "replay_invalid_state": "Invalid snapshot — paste the JSON copied by Take snapshot.", - "cancel": "Cancel", - "you_suffix": " (you)", - "points_label": "Points", - "holes_label": "Holes", - "bredouille_title": "Can bredouille", - "jan_double": "double", - "jan_simple": "simple", - "jan_filled_quarter": "Quarter filled", - "jan_true_hit_small": "True hit (small jan)", - "jan_true_hit_big": "True hit (big jan)", - "jan_true_hit_corner": "True hit (opp. corner)", - "jan_first_exit": "First to exit", - "jan_six_tables": "Six tables", - "jan_two_tables": "Two tables", - "jan_mezeas": "Mezeas", - "jan_false_hit_small": "False hit (small jan)", - "jan_false_hit_big": "False hit (big jan)", - "jan_contre_two": "Contre two tables", - "jan_contre_mezeas": "Contre mezeas", - "jan_helpless_man": "Helpless man", - "play_vs_bot": "Play vs Bot", - "vs_bot_label": "vs Bot", - "you_win": "You win!", - "opp_wins": "{{ name }} wins!", - "play_again": "Play again", - "after_opponent_roll": "Opponent rolled", - "after_opponent_go": "Opponent chose to continue", - "after_opponent_move": "Opponent moved — your turn", - "after_opponent_pre_game_roll": "Opponent rolled — your turn", - "pre_game_roll_title": "Who goes first?", - "pre_game_roll_btn": "Roll", - "pre_game_roll_tie": "Tie! Roll again", - "toss_you_first": "You go first!", - "toss_opp_first": "{{ name }} goes first!", - "pre_game_roll_your_die": "Your die", - "pre_game_roll_opp_die": "Opponent's die", - "continue_btn": "Continue", - "scored_pts": "+{{ n }} pts", - "hole_made": "Hole! {{ holes }}/12", - "bredouille_applied": "Bredouille!", - "hold": "Hold", - "opp_scored_pts": "Opponent +{{ n }} pts", - "opp_hole_made": "Opponent hole! {{ holes }}/12", - "hint_move": "Click a highlighted field to move a checker", - "hint_hold_or_go": "Hold to keep points — Go to reset the setting", - "hint_continue": "Click Continue when ready", - "anonymous_name": "Anonymous", - "login_failed": "Invalid username or password.", - "sign_in": "Sign in", - "sign_out": "Sign out", - "create_account": "Create account", - "account_title": "Account", - "label_username": "Username", - "label_username_or_email": "Username or email", - "label_password": "Password", - "label_confirm_password": "Confirm password", - "passwords_do_not_match": "Passwords do not match.", - "label_email": "Email", - "forgot_password_link": "Forgot password?", - "forgot_password_title": "Reset password", - "forgot_password_email_label": "Email address", - "forgot_password_submit": "Send reset link", - "forgot_password_sent": "If an account with this email exists, a reset link has been sent to that address.", - "reset_password_title": "New password", - "new_password_label": "New password", - "reset_password_submit": "Reset password", - "reset_password_success": "Password reset successfully. You can now sign in.", - "reset_password_invalid": "This reset link is invalid or has expired.", - "verify_email_title": "Email verification", - "verify_email_checking": "Verifying your email…", - "verify_email_success": "Your email has been verified.", - "verify_email_invalid": "This verification link is invalid or has expired.", - "email_not_verified_banner": "Please verify your email address — check your inbox.", - "resend_verification": "Resend verification email", - "verification_email_resent": "Verification email sent.", - "loading": "Loading…", - "member_since": "Member since", - "stat_games": "Games", - "stat_wins": "Wins", - "stat_losses": "Losses", - "stat_draws": "Draws", - "game_history_title": "Game History", - "no_games": "No games recorded yet.", - "col_room": "Room", - "col_started": "Started", - "col_ended": "Ended", - "col_outcome": "Outcome", - "col_detail": "Detail", - "prev_page": "← Prev", - "next_page": "Next →", - "page_label": "Page", - "view_link": "View", - "outcome_win": "win", - "outcome_loss": "loss", - "outcome_draw": "draw", - "players_header": "Players", - "col_player": "Player", - "score_header": "Score", - "game_ongoing": "ongoing", - "anonymous_player": "anonymous", - "started_label": "Started", - "ended_label": "Ended", - "room_detail_title": "Room", - "share_link": "Share this link to invite an opponent", - "copy_link": "Copy link", - "link_copied": "Copied!", - "scan_qr": "or scan the QR code", - "join_code_label": "Join by code", - "join_code_placeholder": "Room code", - "share_btn": "Share", - "nickname_modal_title": "Choose your nickname", - "nickname_modal_hint": "You will play as:", - "nickname_modal_play": "Play", - "nickname_modal_or": "or", - "nickname_modal_sign_in": "Sign in", - "nickname_modal_register": "Create account", - "new_game": "New game", - "language": "Language" -} diff --git a/clients/web/locales/fr.json b/clients/web/locales/fr.json deleted file mode 100644 index aae9c52..0000000 --- a/clients/web/locales/fr.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "room_name_placeholder": "Nom de la salle", - "create_room": "Inviter un adversaire", - "join_room": "Rejoindre", - "connecting": "Connexion en cours…", - "game_over": "Partie terminée", - "waiting_for_opponent": "En attente de l'adversaire…", - "your_turn_roll": "À votre tour — lancez les dés", - "hold_or_go": "Tenir ou s'en aller ?", - "select_move": "Déplacez une dame ({{ n }} sur 2)", - "your_turn": "Votre tour", - "opponent_turn": "Tour de l'adversaire", - "room_label": "Salle : {{ id }}", - "quit": "Quitter", - "roll_dice": "Lancer les dés", - "go": "S'en aller", - "empty_move": "Mouvement impossible", - "cancel_move": "Annuler le déplacement", - "debug_section": "Debug", - "take_snapshot": "Prendre un instantané", - "snapshot_copied": "Copié !", - "replay_snapshot": "Rejouer un instantané", - "replay_paste_hint": "Collez un instantané JSON pour démarrer une partie contre le bot depuis cette position.", - "replay_start": "Démarrer", - "replay_invalid_state": "Instantané invalide — collez le JSON copié par « Prendre un instantané ».", - "cancel": "Annuler", - "you_suffix": " (vous)", - "points_label": "Points", - "holes_label": "Trous", - "bredouille_title": "Peut faire bredouille", - "jan_double": "double", - "jan_simple": "simple", - "jan_filled_quarter": "Remplissage", - "jan_true_hit_small": "Battage à vrai (petit jan)", - "jan_true_hit_big": "Battage à vrai (grand jan)", - "jan_true_hit_corner": "Battage coin adverse", - "jan_first_exit": "Premier sorti", - "jan_six_tables": "Jan de six tables", - "jan_two_tables": "Jan de deux tables", - "jan_mezeas": "Jan de mézéas", - "jan_false_hit_small": "Battage à faux (petit jan)", - "jan_false_hit_big": "Battage à faux (grand jan)", - "jan_contre_two": "Contre jan de deux tables", - "jan_contre_mezeas": "Contre jan de mezeas", - "jan_helpless_man": "Dame impuissante", - "play_vs_bot": "Jouer contre le bot", - "vs_bot_label": "contre le bot", - "you_win": "Vous avez gagné !", - "opp_wins": "{{ name }} a gagné !", - "play_again": "Rejouer", - "after_opponent_roll": "L'adversaire a lancé les dés", - "after_opponent_go": "L'adversaire s'en va", - "after_opponent_move": "L'adversaire a joué — à vous", - "after_opponent_pre_game_roll": "L'adversaire a lancé — à vous", - "pre_game_roll_title": "Qui joue en premier ?", - "pre_game_roll_btn": "Lancer", - "pre_game_roll_tie": "Égalité ! Relancez", - "toss_you_first": "Vous commencez !", - "toss_opp_first": "{{ name }} commence !", - "pre_game_roll_your_die": "Votre dé", - "pre_game_roll_opp_die": "Dé adverse", - "continue_btn": "Continuer", - "scored_pts": "+{{ n }} pts", - "hole_made": "Trou ! {{ holes }}/12", - "bredouille_applied": "Bredouille !", - "hold": "Tenir", - "opp_scored_pts": "Adversaire +{{ n }} pts", - "opp_hole_made": "Trou adverse ! {{ holes }}/12", - "hint_move": "Cliquez un champ surligné pour déplacer", - "hint_hold_or_go": "Tenir pour garder les points — S'en aller pour repartir", - "hint_continue": "Cliquez Continuer quand vous êtes prêt", - "anonymous_name": "Anonyme", - "login_failed": "Identifiant ou mot de passe incorrect.", - "sign_in": "Se connecter", - "sign_out": "Se déconnecter", - "create_account": "Créer un compte", - "account_title": "Compte", - "label_username": "Nom d'utilisateur", - "label_username_or_email": "Nom d'utilisateur ou email", - "label_password": "Mot de passe", - "label_confirm_password": "Confirmer le mot de passe", - "passwords_do_not_match": "Les mots de passe ne correspondent pas.", - "label_email": "Email", - "forgot_password_link": "Mot de passe oublié ?", - "forgot_password_title": "Réinitialiser le mot de passe", - "forgot_password_email_label": "Adresse email", - "forgot_password_submit": "Envoyer le lien", - "forgot_password_sent": "Si un compte avec cet email existe, un lien de réinitialisation a été envoyé à cette adresse.", - "reset_password_title": "Nouveau mot de passe", - "new_password_label": "Nouveau mot de passe", - "reset_password_submit": "Réinitialiser", - "reset_password_success": "Mot de passe réinitialisé. Vous pouvez maintenant vous connecter.", - "reset_password_invalid": "Ce lien est invalide ou a expiré.", - "verify_email_title": "Vérification de l'email", - "verify_email_checking": "Vérification en cours…", - "verify_email_success": "Votre email a été vérifié.", - "verify_email_invalid": "Ce lien de vérification est invalide ou a expiré.", - "email_not_verified_banner": "Veuillez vérifier votre adresse email — consultez votre boîte de réception.", - "resend_verification": "Renvoyer l'email de vérification", - "verification_email_resent": "Email de vérification envoyé.", - "loading": "Chargement…", - "member_since": "Membre depuis", - "stat_games": "Parties", - "stat_wins": "Victoires", - "stat_losses": "Défaites", - "stat_draws": "Nuls", - "game_history_title": "Historique", - "no_games": "Aucune partie enregistrée.", - "col_room": "Salle", - "col_started": "Début", - "col_ended": "Fin", - "col_outcome": "Résultat", - "col_detail": "Détail", - "prev_page": "← Précédent", - "next_page": "Suivant →", - "page_label": "Page", - "view_link": "Voir", - "outcome_win": "victoire", - "outcome_loss": "défaite", - "outcome_draw": "nul", - "players_header": "Joueurs", - "col_player": "Joueur", - "score_header": "Score", - "game_ongoing": "en cours", - "anonymous_player": "anonyme", - "started_label": "Début", - "ended_label": "Fin", - "room_detail_title": "Salle", - "share_link": "Partagez ce lien pour inviter un adversaire", - "copy_link": "Copier le lien", - "link_copied": "Copié !", - "scan_qr": "ou scannez le QR code", - "join_code_label": "Rejoindre avec un code", - "join_code_placeholder": "Code de la salle", - "share_btn": "Partager", - "nickname_modal_title": "Choisissez votre pseudo", - "nickname_modal_hint": "Vous jouerez sous le nom de :", - "nickname_modal_play": "Jouer", - "nickname_modal_or": "ou", - "nickname_modal_sign_in": "Se connecter", - "nickname_modal_register": "Créer un compte", - "new_game": "Nouvelle partie", - "language": "Langue" -} diff --git a/clients/web/src/api.rs b/clients/web/src/api.rs deleted file mode 100644 index 9e0f57c..0000000 --- a/clients/web/src/api.rs +++ /dev/null @@ -1,253 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[cfg(debug_assertions)] -pub const HTTP_BASE: &str = "http://localhost:8080"; -#[cfg(not(debug_assertions))] -pub const HTTP_BASE: &str = ""; - -fn url(path: &str) -> String { - format!("{HTTP_BASE}{path}") -} - -// ── Response types ──────────────────────────────────────────────────────────── - -#[derive(Clone, Debug, Deserialize)] -pub struct MeResponse { - pub id: i64, - pub username: String, - #[serde(default)] - pub email_verified: bool, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct UserProfile { - pub id: i64, - pub username: String, - pub created_at: i64, - pub total_games: i64, - pub wins: i64, - pub losses: i64, - pub draws: i64, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct GameSummary { - pub id: i64, - pub game_id: String, - pub room_code: String, - pub started_at: i64, - pub ended_at: Option, - pub result: Option, - pub outcome: Option, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct GamesResponse { - pub games: Vec, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct Participant { - pub player_id: i64, - pub outcome: Option, - pub username: Option, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct GameDetail { - pub id: i64, - pub game_id: String, - pub room_code: String, - pub started_at: i64, - pub ended_at: Option, - pub result: Option, - pub participants: Vec, -} - -// ── Request bodies ──────────────────────────────────────────────────────────── - -#[derive(Serialize)] -pub struct RegisterBody<'a> { - pub username: &'a str, - pub email: &'a str, - pub password: &'a str, -} - -#[derive(Serialize)] -pub struct LoginBody<'a> { - pub username: &'a str, - pub password: &'a str, -} - -// ── Fetch helpers ───────────────────────────────────────────────────────────── - -pub async fn get_me() -> Result { - let resp = gloo_net::http::Request::get(&url("/auth/me")) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - resp.json::().await.map_err(|e| e.to_string()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn post_login(username: &str, password: &str) -> Result { - let body = LoginBody { username, password }; - let resp = gloo_net::http::Request::post(&url("/auth/login")) - .credentials(web_sys::RequestCredentials::Include) - .json(&body) - .map_err(|e| e.to_string())? - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - resp.json::().await.map_err(|e| e.to_string()) - } else { - let text = resp.text().await.unwrap_or_default(); - Err(text) - } -} - -pub async fn post_register(username: &str, email: &str, password: &str) -> Result { - let body = RegisterBody { username, email, password }; - let resp = gloo_net::http::Request::post(&url("/auth/register")) - .credentials(web_sys::RequestCredentials::Include) - .json(&body) - .map_err(|e| e.to_string())? - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 201 { - resp.json::().await.map_err(|e| e.to_string()) - } else { - let text = resp.text().await.unwrap_or_default(); - Err(text) - } -} - -pub async fn post_logout() -> Result<(), String> { - let resp = gloo_net::http::Request::post(&url("/auth/logout")) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 204 { - Ok(()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn get_user_profile(username: &str) -> Result { - let resp = gloo_net::http::Request::get(&url(&format!("/users/{username}"))) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - resp.json::().await.map_err(|e| e.to_string()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn get_user_games(username: &str, page: i64) -> Result { - let resp = gloo_net::http::Request::get(&url(&format!( - "/users/{username}/games?page={page}&per_page=20" - ))) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - resp.json::().await.map_err(|e| e.to_string()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn get_game_detail(id: i64) -> Result { - let resp = gloo_net::http::Request::get(&url(&format!("/games/{id}"))) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - resp.json::().await.map_err(|e| e.to_string()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn get_verify_email(token: &str) -> Result<(), String> { - let resp = gloo_net::http::Request::get(&url(&format!("/auth/verify-email?token={token}"))) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - Ok(()) - } else { - let text = resp.text().await.unwrap_or_default(); - Err(text) - } -} - -pub async fn post_resend_verification() -> Result<(), String> { - let resp = gloo_net::http::Request::post(&url("/auth/resend-verification")) - .credentials(web_sys::RequestCredentials::Include) - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - Ok(()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn post_forgot_password(email: &str) -> Result<(), String> { - let body = serde_json::json!({ "email": email }); - let resp = gloo_net::http::Request::post(&url("/auth/forgot-password")) - .credentials(web_sys::RequestCredentials::Include) - .json(&body) - .map_err(|e| e.to_string())? - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - Ok(()) - } else { - Err(format!("status {}", resp.status())) - } -} - -pub async fn post_reset_password(token: &str, new_password: &str) -> Result<(), String> { - let body = serde_json::json!({ "token": token, "new_password": new_password }); - let resp = gloo_net::http::Request::post(&url("/auth/reset-password")) - .credentials(web_sys::RequestCredentials::Include) - .json(&body) - .map_err(|e| e.to_string())? - .send() - .await - .map_err(|e| e.to_string())?; - if resp.status() == 200 { - Ok(()) - } else { - let text = resp.text().await.unwrap_or_default(); - Err(text) - } -} - -// ── Utilities ───────────────────────────────────────────────────────────────── - -pub fn format_ts(ts: i64) -> String { - let ms = (ts * 1000) as f64; - let date = js_sys::Date::new(&wasm_bindgen::JsValue::from_f64(ms)); - date.to_locale_string("en-GB", &wasm_bindgen::JsValue::UNDEFINED) - .as_string() - .unwrap_or_default() -} diff --git a/clients/web/src/app.rs b/clients/web/src/app.rs deleted file mode 100644 index 3819b61..0000000 --- a/clients/web/src/app.rs +++ /dev/null @@ -1,747 +0,0 @@ -use futures::channel::mpsc; -use futures::{FutureExt, StreamExt}; -use gloo_storage::{LocalStorage, Storage}; -use leptos::prelude::*; -use leptos::task::spawn_local; -use leptos_router::components::{Route, Router, Routes, A}; -use leptos_router::hooks::use_location; -use leptos_router::path; -use serde::{Deserialize, Serialize}; - -use backbone_lib::session::{ConnectError, GameSession, RoomConfig, RoomRole, SessionEvent}; -use backbone_lib::traits::ViewStateUpdate; - -use crate::api; -use crate::game::components::{ConnectingScreen, GameScreen}; -use crate::game::session::{ - compute_last_moves, patch_player_name, push_or_show, run_local_bot_game, - run_local_bot_game_with_backend, -}; -use crate::game::trictrac::backend::TrictracBackend; -use crate::game::trictrac::types::{GameDelta, PlayerAction, ScoredEvent, SerStage, ViewState}; -use crate::i18n::*; -use crate::portal::{ - account::AccountPage, forgot_password::ForgotPasswordPage, game_detail::GameDetailPage, - lobby::LobbyPage, profile::ProfilePage, reset_password::ResetPasswordPage, - verify_email::VerifyEmailPage, -}; -use trictrac_store::CheckerMove; - -use std::collections::VecDeque; - -const RELAY_URL: &str = "ws://localhost:8080/ws"; -const GAME_ID: &str = "trictrac"; -const STORAGE_KEY: &str = "trictrac_session"; - -/// The state the UI needs to render the game screen. -#[derive(Clone, PartialEq)] -pub struct GameUiState { - pub view_state: ViewState, - /// 0 = host, 1 = guest - pub player_id: u16, - pub room_id: String, - pub is_bot_game: bool, - pub waiting_for_confirm: bool, - pub pause_reason: Option, - pub my_scored_event: Option, - pub opp_scored_event: Option, - pub last_moves: Option<(CheckerMove, CheckerMove)>, - /// True on the echo screen state set alongside a pending item — suppresses dice - /// roll animation and sound since they already played on the pending screen. - pub suppress_dice_anim: bool, -} - -/// Reason the UI is paused waiting for the player to click Continue. -#[derive(Clone, Debug, PartialEq)] -pub enum PauseReason { - AfterOpponentRoll, - AfterOpponentGo, - AfterOpponentMove, - AfterOpponentPreGameRoll, -} - -/// Which screen is currently shown (used to toggle game overlay). -#[derive(Clone, PartialEq)] -pub enum Screen { - Login { error: Option }, - Connecting, - Playing(GameUiState), -} - -/// Commands sent from UI event handlers into the network task. -pub enum NetCommand { - CreateRoom { - room: String, - }, - JoinRoom { - room: String, - }, - Reconnect { - relay_url: String, - game_id: String, - room_id: String, - token: u64, - host_state: Option>, - }, - PlayVsBot, - /// Start a bot game with the board/score position from a previously taken snapshot. - ReplaySnapshot(ViewState), - Action(PlayerAction), - Disconnect, -} - -#[derive(Serialize, Deserialize)] -struct StoredSession { - relay_url: String, - game_id: String, - room_id: String, - token: u64, - #[serde(default)] - is_host: bool, - #[serde(default)] - view_state: Option, -} - -fn save_session(session: &StoredSession) { - LocalStorage::set(STORAGE_KEY, session).ok(); -} - -fn load_session() -> Option { - LocalStorage::get::(STORAGE_KEY).ok() -} - -fn clear_session() { - LocalStorage::delete(STORAGE_KEY); -} - -async fn submit_game_result(room_code: String, game_state: ViewState) { - let [score_pl1, score_pl2] = game_state.scores; - let result_str = format!("{:?} - {:?}", score_pl1.holes, score_pl2.holes); - let outcomes = if score_pl1.holes < score_pl2.holes { - [("0", "loss"), ("1", "win")] - } else if score_pl2.holes < score_pl1.holes { - [("0", "win"), ("1", "loss")] - } else { - [("0", "draw"), ("1", "draw")] - }; - let body = serde_json::json!({ - "room_code": room_code, - "game_id": GAME_ID, - "result": result_str, - "outcomes": std::collections::HashMap::from(outcomes), - }); - let _ = gloo_net::http::Request::post(&format!("{}/games/result", api::HTTP_BASE)) - .credentials(web_sys::RequestCredentials::Include) - .json(&body) - .unwrap() - .send() - .await; -} - -#[component] -pub fn App() -> impl IntoView { - let i18n = use_i18n(); - let stored = load_session(); - let initial_screen = if stored.is_some() { - Screen::Connecting - } else { - Screen::Login { error: None } - }; - let screen: RwSignal = RwSignal::new(initial_screen); - provide_context(screen); - - // Auth: fetch once on load; shared by nav + game + portal components. - let auth_username: RwSignal> = RwSignal::new(None); - let auth_email_verified: RwSignal = RwSignal::new(false); - provide_context(auth_username); - provide_context(auth_email_verified); - // Set to true once get_me resolves (success or failure) so lobby can - // decide immediately whether to show the nickname modal. - let auth_loaded: RwSignal = RwSignal::new(false); - provide_context(auth_loaded); - // Nickname chosen by an anonymous player; used instead of "Anonymous". - let anon_nickname: RwSignal> = RwSignal::new(None); - provide_context(anon_nickname); - spawn_local(async move { - if let Ok(me) = api::get_me().await { - auth_username.set(Some(me.username)); - auth_email_verified.set(me.email_verified); - } - auth_loaded.set(true); - }); - - let (cmd_tx, mut cmd_rx) = mpsc::unbounded::(); - let pending: RwSignal> = RwSignal::new(VecDeque::new()); - provide_context(pending); - provide_context(cmd_tx.clone()); - - if let Some(s) = stored { - let host_state = s - .view_state - .as_ref() - .and_then(|vs| serde_json::to_vec(vs).ok()); - cmd_tx - .unbounded_send(NetCommand::Reconnect { - relay_url: s.relay_url, - game_id: s.game_id, - room_id: s.room_id, - token: s.token, - host_state, - }) - .ok(); - } - - spawn_local(async move { - loop { - let mut snapshot_init: Option = None; - let remote_config: Option<(RoomConfig, bool)> = loop { - match cmd_rx.next().await { - Some(NetCommand::PlayVsBot) => break None, - Some(NetCommand::ReplaySnapshot(vs)) => { - snapshot_init = Some(vs); - break None; - } - Some(NetCommand::CreateRoom { room }) => { - break Some(( - RoomConfig { - relay_url: RELAY_URL.to_string(), - game_id: GAME_ID.to_string(), - room_id: room, - rule_variation: 0, - role: RoomRole::Create, - reconnect_token: None, - host_state: None, - }, - false, - )); - } - Some(NetCommand::JoinRoom { room }) => { - break Some(( - RoomConfig { - relay_url: RELAY_URL.to_string(), - game_id: GAME_ID.to_string(), - room_id: room, - rule_variation: 0, - role: RoomRole::Join, - reconnect_token: None, - host_state: None, - }, - false, - )); - } - Some(NetCommand::Reconnect { - relay_url, - game_id, - room_id, - token, - host_state, - }) => { - break Some(( - RoomConfig { - relay_url, - game_id, - room_id, - rule_variation: 0, - role: RoomRole::Join, - reconnect_token: Some(token), - host_state, - }, - true, - )); - } - _ => {} - } - }; - - if remote_config.is_none() { - let player_name = auth_username - .get_untracked() - .or_else(|| anon_nickname.get_untracked()) - .unwrap_or_else(|| untrack(|| t_string!(i18n, anonymous_name).to_string())); - loop { - let restart = match snapshot_init.take() { - Some(vs) => { - let backend = TrictracBackend::from_view_state(vs, &player_name); - run_local_bot_game_with_backend( - screen, - &mut cmd_rx, - pending, - player_name.clone(), - backend, - ) - .await - } - None => { - run_local_bot_game(screen, &mut cmd_rx, pending, player_name.clone()) - .await - } - }; - if !restart { - break; - } - } - pending.update(|q| q.clear()); - screen.set(Screen::Login { error: None }); - continue; - } - let (config, is_reconnect) = remote_config.unwrap(); - - screen.set(Screen::Connecting); - - let room_id_for_storage = config.room_id.clone(); - let mut session: GameSession = - match GameSession::connect::(config).await { - Ok(s) => s, - Err(ConnectError::WebSocket(e) | ConnectError::Handshake(e)) => { - if is_reconnect { - clear_session(); - } - screen.set(Screen::Login { error: Some(e) }); - continue; - } - }; - - if !session.is_host { - save_session(&StoredSession { - relay_url: RELAY_URL.to_string(), - game_id: GAME_ID.to_string(), - room_id: room_id_for_storage.clone(), - token: session.reconnect_token, - is_host: false, - view_state: None, - }); - } - - let is_host = session.is_host; - let player_id = session.player_id; - let reconnect_token = session.reconnect_token; - let my_name = auth_username - .get_untracked() - .or_else(|| anon_nickname.get_untracked()) - .unwrap_or_else(|| t_string!(i18n, anonymous_name).to_string()); - // Announce our name to the host backend so it can broadcast it to - // the opponent. Done once immediately after connecting. - session.send_action(PlayerAction::SetName(my_name.clone())); - let mut vs = ViewState::default_with_names("", ""); - let mut result_submitted = false; - - loop { - futures::select! { - cmd = cmd_rx.next().fuse() => match cmd { - Some(NetCommand::Action(action)) => { - session.send_action(action); - } - _ => { - clear_session(); - session.disconnect(); - pending.update(|q| q.clear()); - screen.set(Screen::Login { error: None }); - break; - } - }, - event = session.next_event().fuse() => match event { - Some(SessionEvent::Update(u)) => { - let prev_vs = vs.clone(); - match u { - ViewStateUpdate::Full(state) => vs = state, - ViewStateUpdate::Incremental(delta) => vs.apply_delta(&delta), - } - patch_player_name(&mut vs, player_id, &my_name); - - if is_host && !result_submitted && vs.stage == SerStage::Ended { - result_submitted = true; - let room = room_id_for_storage.clone(); - let gs = vs.clone(); - spawn_local(submit_game_result(room, gs)); - } - - if is_host { - save_session(&StoredSession { - relay_url: RELAY_URL.to_string(), - game_id: GAME_ID.to_string(), - room_id: room_id_for_storage.clone(), - token: reconnect_token, - is_host: true, - view_state: Some(vs.clone()), - }); - } - let is_own_move = prev_vs.active_mp_player == Some(player_id); - push_or_show( - &prev_vs, - GameUiState { - view_state: vs.clone(), - player_id, - room_id: room_id_for_storage.clone(), - is_bot_game: false, - waiting_for_confirm: false, - pause_reason: None, - my_scored_event: None, - opp_scored_event: None, - last_moves: compute_last_moves(&prev_vs, &vs, is_own_move), - suppress_dice_anim: false, - }, - pending, - screen, - ); - } - Some(SessionEvent::Disconnected(reason)) => { - pending.update(|q| q.clear()); - screen.set(Screen::Login { error: reason }); - break; - } - None => { - pending.update(|q| q.clear()); - screen.set(Screen::Login { error: None }); - break; - } - } - } - } - } - }); - - view! { - - -
- "Page not found."

}> - - - - - - - -
-
- - -
- } -} - -/// Renders the full-screen game overlay, but only when the current route is "/". -/// This lets the user navigate to profile/account pages while a game is running. -#[component] -fn GameOverlay( - pending: RwSignal>, - screen: RwSignal, -) -> impl IntoView { - let location = use_location(); - - // Memoize the front of the pending queue so that pushing a new item to the back - // does not re-mount GameScreen (and replay dice animation/sound) when the displayed - // state (the front) hasn't changed. - let pending_front = Memo::new(move |_| pending.with(|q| q.front().cloned())); - - move || { - if location.pathname.get() != "/" { - return view! {}.into_any(); - } - if let Some(state) = pending_front.get() { - return view! { -
- } - .into_any(); - } - match screen.get() { - Screen::Playing(state) => view! { -
- } - .into_any(), - Screen::Connecting => view! { -
- } - .into_any(), - _ => view! {}.into_any(), - } - } -} - -/// Persistent hamburger button + left sidebar — visible on every page. -#[component] -fn SiteHamburger() -> impl IntoView { - let i18n = use_i18n(); - let auth_username = - use_context::>>().unwrap_or_else(|| RwSignal::new(None)); - let screen = use_context::>().expect("Screen context not found"); - let cmd_tx = use_context::>() - .expect("cmd_tx not found in context"); - - let sidebar_open = RwSignal::new(false); - let snapshot_copied = RwSignal::new(false); - let replay_open = RwSignal::new(false); - let replay_text = RwSignal::new(String::new()); - let replay_error = RwSignal::new(false); - - let cmd_tx_newgame = cmd_tx.clone(); - let cmd_tx_snapshot = cmd_tx.clone(); - let cmd_tx_replay = cmd_tx.clone(); - - view! { - // ── Hamburger button (☰ → ✕ animation) ─────────────────────────────── - - - // ── Left sidebar ────────────────────────────────────────────────────── -
- -
- "Trictrac" - -
- - -
-
- - // Language switcher - //
- // - // - // - // {t!(i18n, language)} - //
- // - // - //
- //
- -
- - // Auth -
- - - - - {move || match auth_username.get() { - Some(u) => { - let href = format!("/profile/{u}"); - view! { - - {u} - - - }.into_any() - }, - None => view! { - - {t!(i18n, sign_in)} - - }.into_any(), - }} -
- - // ── Debug section ───────────────────────────────────────────────── -
- {t!(i18n, debug_section)} - - // "Take snapshot" — only visible while a game is in progress - {move || { - let Screen::Playing(ref state) = screen.get() else { return None; }; - let vs = state.view_state.clone(); - let tx = cmd_tx_snapshot.clone(); - Some(view! { - - }) - }} - - // "Replay snapshot" — always visible - -
-
- - // ── Replay snapshot modal ───────────────────────────────────────────── -
-
-

{t!(i18n, replay_snapshot)}

-

- {t!(i18n, replay_paste_hint)} -

-