diff --git a/Cargo.lock b/Cargo.lock index 5a6ddc0..7594e42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[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" @@ -75,6 +81,24 @@ 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" @@ -146,6 +170,12 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + [[package]] name = "arc-swap" version = "1.9.1" @@ -155,6 +185,17 @@ dependencies = [ "rustversion", ] +[[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", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -173,6 +214,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[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" @@ -359,6 +409,49 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "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", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" +dependencies = [ + "arrayvec", +] + [[package]] name = "backon" version = "1.6.0" @@ -398,7 +491,7 @@ dependencies = [ "bitflags 2.11.1", "cexpr", "clang-sys", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -422,6 +515,12 @@ 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" @@ -434,6 +533,15 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + [[package]] name = "blake3" version = "1.8.5" @@ -494,6 +602,12 @@ dependencies = [ "piper", ] +[[package]] +name = "built" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" + [[package]] name = "bumpalo" version = "3.20.3" @@ -520,6 +634,12 @@ dependencies = [ "syn", ] +[[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" @@ -631,7 +751,7 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "rand_core", + "rand_core 0.10.1", ] [[package]] @@ -741,6 +861,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -918,6 +1044,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -933,6 +1068,16 @@ 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" @@ -1030,7 +1175,7 @@ dependencies = [ "curve25519-dalek-derive", "digest", "fiat-crypto", - "rand_core", + "rand_core 0.10.1", "rustc_version", "serde", "subtle", @@ -1316,7 +1461,7 @@ checksum = "20449acd54b660981ae5caa2bcb56d1fe7f25f2e37a38ec507400fab034d4bb6" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.10.1", "serde", "sha2", "signature", @@ -1380,6 +1525,26 @@ dependencies = [ "syn", ] +[[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", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1442,12 +1607,42 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fiat-crypto" version = "0.3.0" @@ -1466,6 +1661,16 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" +[[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_next_after" version = "1.0.0" @@ -1764,7 +1969,7 @@ dependencies = [ "js-sys", "libc", "r-efi 6.0.0", - "rand_core", + "rand_core 0.10.1", "wasip2", "wasip3", "wasm-bindgen", @@ -1780,6 +1985,16 @@ dependencies = [ "polyval", ] +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gl_generator" version = "0.14.0" @@ -2013,7 +2228,7 @@ dependencies = [ "idna", "ipnet", "jni", - "rand", + "rand 0.10.1", "rustls", "thiserror 2.0.18", "tinyvec", @@ -2035,7 +2250,7 @@ dependencies = [ "jni", "once_cell", "prefix-trie", - "rand", + "rand 0.10.1", "ring", "thiserror 2.0.18", "tinyvec", @@ -2060,7 +2275,7 @@ dependencies = [ "ndk-context", "once_cell", "parking_lot", - "rand", + "rand 0.10.1", "resolv-conf", "rustls", "smallvec", @@ -2222,6 +2437,7 @@ dependencies = [ "iced_runtime", "iced_widget", "iced_winit", + "image", "thiserror 2.0.18", ] @@ -2280,6 +2496,8 @@ dependencies = [ "half", "iced_core", "iced_futures", + "image", + "kamadak-exif", "log", "lyon_path", "raw-window-handle", @@ -2530,12 +2748,52 @@ dependencies = [ "hyper", "hyper-util", "log", - "rand", + "rand 0.10.1", "tokio", "url", "xmltree", ] +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" + [[package]] name = "indexmap" version = "2.14.0" @@ -2557,6 +2815,17 @@ dependencies = [ "generic-array", ] +[[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", +] + [[package]] name = "ipconfig" version = "0.3.4" @@ -2613,7 +2882,7 @@ dependencies = [ "pin-project", "portable-atomic", "portmapper", - "rand", + "rand 0.10.1", "reqwest", "rustc-hash 2.1.2", "rustls", @@ -2646,7 +2915,7 @@ dependencies = [ "ed25519-dalek", "getrandom 0.4.2", "n0-error", - "rand", + "rand 0.10.1", "serde", "sha2", "url", @@ -2668,7 +2937,7 @@ dependencies = [ "n0-error", "n0-future", "ndk-context", - "rand", + "rand 0.10.1", "reqwest", "rustls", "simple-dns", @@ -2702,7 +2971,7 @@ dependencies = [ "n0-error", "n0-future", "postcard", - "rand", + "rand 0.10.1", "serde", "tokio", "tokio-util", @@ -2764,7 +3033,7 @@ dependencies = [ "num_enum", "pin-project", "postcard", - "rand", + "rand 0.10.1", "reqwest", "rustls", "rustls-pki-types", @@ -2818,6 +3087,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -2904,6 +3182,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kamadak-exif" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1130d80c7374efad55a117d715a3af9368f0fa7a2c54573afc15a188cd984837" +dependencies = [ + "mutate_once", +] + [[package]] name = "khronos-egl" version = "6.0.0" @@ -2943,12 +3230,28 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + [[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.8.9" @@ -3072,6 +3375,15 @@ dependencies = [ "tracing-subscriber", ] +[[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.16.4" @@ -3169,6 +3481,16 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.8.1" @@ -3214,6 +3536,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.0" @@ -3242,6 +3574,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "mundy" version = "0.2.3" @@ -3267,6 +3609,12 @@ dependencies = [ "zbus", ] +[[package]] +name = "mutate_once" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" + [[package]] name = "n0-error" version = "1.0.0-rc.0" @@ -3497,6 +3845,12 @@ dependencies = [ "wmi", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.30.1" @@ -3509,6 +3863,15 @@ dependencies = [ "libc", ] +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nom" version = "7.1.3" @@ -3528,6 +3891,12 @@ 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 = "noq" version = "1.0.0-rc.0" @@ -3563,7 +3932,7 @@ dependencies = [ "getrandom 0.4.2", "identity-hash", "lru-slab", - "rand", + "rand 0.10.1", "rand_pcg", "ring", "rustc-hash 2.1.2", @@ -3599,12 +3968,53 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +[[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", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -4176,6 +4586,12 @@ 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 = "peerspeak" version = "0.1.0" @@ -4190,7 +4606,7 @@ dependencies = [ "iroh-gossip", "opus", "pipewire", - "rand", + "rand 0.10.1", "ringbuf", "serde", "serde_json", @@ -4330,6 +4746,19 @@ dependencies = [ "time", ] +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "3.11.0" @@ -4391,7 +4820,7 @@ dependencies = [ "n0-future", "netwatch", "num_enum", - "rand", + "rand 0.10.1", "serde", "smallvec", "socket2", @@ -4442,6 +4871,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "prefix-trie" version = "0.8.4" @@ -4492,6 +4930,40 @@ name = "profiling" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[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 = "quick-xml" @@ -4523,6 +4995,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + [[package]] name = "rand" version = "0.10.1" @@ -4531,7 +5013,26 @@ checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", "getrandom 0.4.2", - "rand_core", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", ] [[package]] @@ -4546,7 +5047,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" dependencies = [ - "rand_core", + "rand_core 0.10.1", ] [[package]] @@ -4561,12 +5062,82 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +[[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", + "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.4", + "rand_chacha", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "read-fonts" version = "0.35.0" @@ -4704,6 +5275,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + [[package]] name = "ring" version = "0.17.14" @@ -5094,6 +5671,12 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "simd_cesu8" version = "1.1.1" @@ -5104,6 +5687,15 @@ dependencies = [ "simdutf8", ] +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -5540,6 +6132,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + [[package]] name = "time" version = "0.3.47" @@ -5714,7 +6320,7 @@ dependencies = [ "getrandom 0.4.2", "http", "httparse", - "rand", + "rand 0.10.1", "ring", "rustls-pki-types", "sha1_smol", @@ -6003,6 +6609,17 @@ dependencies = [ "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", +] + [[package]] name = "valuable" version = "0.1.1" @@ -6390,6 +7007,12 @@ 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 = "27.0.1" @@ -7126,6 +7749,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yazi" version = "0.2.1" @@ -7322,6 +7951,30 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +[[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.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "5.11.0" diff --git a/Cargo.toml b/Cargo.toml index 465bafd..46ba8c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ async-trait = "0.1.89" base64 = "0.22.1" bytes = "1.11.1" dirs = "6.0.0" -iced = { version = "0.14.0", features = ["canvas"] } +iced = { version = "0.14.0", features = ["canvas", "image"] } iroh = "1.0.0-rc.0" iroh-gossip = "0.99.0" opus = "0.3.1" diff --git a/assets/avatars/preset1.png b/assets/avatars/preset1.png new file mode 100644 index 0000000..88eaa93 Binary files /dev/null and b/assets/avatars/preset1.png differ diff --git a/assets/avatars/preset2.png b/assets/avatars/preset2.png new file mode 100644 index 0000000..6198895 Binary files /dev/null and b/assets/avatars/preset2.png differ diff --git a/assets/avatars/preset3.png b/assets/avatars/preset3.png new file mode 100644 index 0000000..3b34e6a Binary files /dev/null and b/assets/avatars/preset3.png differ diff --git a/assets/avatars/preset4.png b/assets/avatars/preset4.png new file mode 100644 index 0000000..415ecac Binary files /dev/null and b/assets/avatars/preset4.png differ diff --git a/assets/avatars/preset5.png b/assets/avatars/preset5.png new file mode 100644 index 0000000..eae416d Binary files /dev/null and b/assets/avatars/preset5.png differ diff --git a/assets/avatars/preset6.png b/assets/avatars/preset6.png new file mode 100644 index 0000000..861c19a Binary files /dev/null and b/assets/avatars/preset6.png differ diff --git a/src/app/mod.rs b/src/app/mod.rs index c35d41e..c154f29 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -163,6 +163,8 @@ pub enum AppMessage { SelectRoomLayout(RoomLayout), /// Choose a UI theme (applied live + persisted). SelectTheme(AppTheme), + /// Choose our avatar (monogram or a preset); applied live + persisted (W4). + SelectAvatar(crate::avatar::Avatar), /// Toggle the Chat drawer open/closed (drawer layout). ToggleDrawerChat, /// Start/stop sharing our own screen (spawns/kills a pixelpass host). @@ -465,6 +467,7 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { input_device, output_device, echo_cancellation: state.config.echo_cancellation_enabled, + avatar: state.config.avatar.clone(), }); } } @@ -483,6 +486,7 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { input_device, output_device, echo_cancellation: state.config.echo_cancellation_enabled, + avatar: state.config.avatar.clone(), }); } AppMessage::LeavePressed => { @@ -780,6 +784,12 @@ fn update(state: &mut AppState, message: AppMessage) -> Task { state.config.theme = theme; state.config.save(); } + AppMessage::SelectAvatar(avatar) => { + state.config.avatar = avatar.clone(); + state.config.save(); + // Re-announce to the room if we're in a call (no-op otherwise). + let _ = state.controller.send(CoreCommand::SetAvatar(avatar)); + } AppMessage::ToggleDrawerChat => { state.drawer_chat_open = !state.drawer_chat_open; } @@ -1216,6 +1226,51 @@ fn view(state: &AppState) -> Element<'_, AppMessage> { .spacing(10) .width(iced::Length::Fill); + // Inline avatar chooser (W4): the monogram fallback plus the bundled + // presets, each a clickable tile. Same SelectAvatar message, applied live + // + persisted (and re-announced to the room). + let avatar_choice = |a: crate::avatar::Avatar, label: String| -> Element<'_, AppMessage> { + let selected = state.config.avatar == a; + let preview = avatar_view(&a, &state.name, &state.self_id, 52.0); + column![ + button( + container(preview) + .center_x(iced::Length::Fixed(60.0)) + .center_y(iced::Length::Fixed(60.0)) + ) + .on_press(AppMessage::SelectAvatar(a)) + .padding(2) + .style(b_style( + if selected { color_surface } else { Color::TRANSPARENT }, + color_surface, + color_text, + 8.0, + )), + text(label) + .size(11) + .color(if selected { color_blue } else { color_subtext }), + ] + .spacing(4) + .align_x(iced::alignment::Horizontal::Center) + .into() + }; + let mut avatar_tiles: Vec> = + vec![avatar_choice(crate::avatar::Avatar::Monogram, "Monogram".to_string())]; + for i in 0..crate::avatar::PRESET_COUNT { + avatar_tiles.push(avatar_choice( + crate::avatar::Avatar::Preset(i), + format!("Preset {}", i + 1), + )); + } + let avatar_section = column![ + iced::widget::Row::with_children(avatar_tiles).spacing(12), + text("Shown next to your name in the room and chat. Applies live.") + .size(11) + .color(color_subtext), + ] + .spacing(10) + .width(iced::Length::Fill); + // Reusable category header: a coloured title with a thin full-width // divider beneath, so each class of settings reads as its own section. let section_header = |title: &'static str| -> Element<'_, AppMessage> { @@ -1335,6 +1390,11 @@ fn view(state: &AppState) -> Element<'_, AppMessage> { theme_section, vertical_space(section_gap), + // --- Avatar --- + section_header("Avatar"), + avatar_section, + vertical_space(section_gap), + // --- Notifications & Sounds --- section_header("Notifications & Sounds"), column![ @@ -1602,7 +1662,7 @@ fn view(state: &AppState) -> Element<'_, AppMessage> { let self_card = container( column![ row![ - avatar_badge(&state.name, &state.self_id, 34.0), + avatar_view(&state.config.avatar, &state.name, &state.self_id, 34.0), text(format!("{} (You)", &state.name)).size(16).color(color_text), horizontal_space(), if state.is_muted { @@ -1729,7 +1789,7 @@ fn view(state: &AppState) -> Element<'_, AppMessage> { let mut card_content = column![ row![ - avatar_badge(&peer.name, &peer_id.to_string(), 38.0), + avatar_view(&peer.avatar, &peer.name, &peer_id.to_string(), 38.0), column![ text(&peer.name).size(16).color(color_text), text(format!("ID: {}", short_id(&peer_id.to_string()))).size(11).color(color_subtext) @@ -1940,11 +2000,27 @@ fn view(state: &AppState) -> Element<'_, AppMessage> { .width(iced::Length::Fill); // Small avatar keyed on the sender's id (falls back to name); the // " (You)" suffix on our own echoes is stripped for clean initials. + // Resolve the sender's chosen avatar: our own from config, a peer's + // from their current presence (looked up by id), else monogram. let av_key = m.from.as_deref().unwrap_or(m.name.as_str()); let av_name = m.name.split(" (").next().unwrap_or(m.name.as_str()); + let av = if m.mine { + state.config.avatar.clone() + } else { + m.from + .as_deref() + .and_then(|f| { + state + .peers + .iter() + .find(|(k, _)| k.to_string() == f) + .map(|(_, v)| v.avatar.clone()) + }) + .unwrap_or_default() + }; chat_col = chat_col.push( row![ - avatar_badge(av_name, av_key, 22.0), + avatar_view(&av, av_name, av_key, 22.0), text(format!("{}:", m.name)).size(12).color(name_color), body, ] @@ -2781,6 +2857,25 @@ fn avatar_badge<'a>(name: &str, key: &str, size: f32) -> Element<'a, AppMessage> .into() } +/// Render a participant's avatar (W4): the chosen preset image if any, else the +/// monogram fallback. `name`/`key` feed the monogram; `size` is the diameter. +fn avatar_view<'a>( + avatar: &crate::avatar::Avatar, + name: &str, + key: &str, + size: f32, +) -> Element<'a, AppMessage> { + match avatar.preset_png() { + Some(png) => iced::widget::image( + iced::widget::image::Handle::from_bytes(bytes::Bytes::from_static(png)), + ) + .width(iced::Length::Fixed(size)) + .height(iced::Length::Fixed(size)) + .into(), + None => avatar_badge(name, key, size), + } +} + /// Centered "icon + label" content for a full-width control-panel button. The /// icon takes the button's foreground colour so it matches the label. fn btn_content<'a>(kind: IconKind, label: &'a str, color: Color) -> Element<'a, AppMessage> { diff --git a/src/avatar.rs b/src/avatar.rs index 7b644d3..ff6602e 100644 --- a/src/avatar.rs +++ b/src/avatar.rs @@ -1,4 +1,5 @@ -//! Avatar helpers: deterministic monogram + colour for a participant. +//! Avatar helpers: a participant's chosen [`Avatar`] plus the deterministic +//! monogram + colour used as the fallback. //! //! Every participant gets a visual avatar (W4). When they haven't chosen a preset //! or uploaded an image, we fall back to a *monogram*: their initial(s) on a @@ -6,6 +7,46 @@ //! name where the id isn't available, e.g. a chat line). Pure + dependency-free //! so it's unit-testable and needs no image decoding for the common case. +use serde::{Deserialize, Serialize}; + +/// Number of bundled preset avatars (`preset1.png`..`preset6.png`). +pub const PRESET_COUNT: u8 = 6; + +/// The raw PNG bytes of each bundled preset, embedded so they're available +/// offline and identical on every peer. Index 0 = preset 1, etc. +pub const PRESET_PNGS: [&[u8]; PRESET_COUNT as usize] = [ + include_bytes!("../assets/avatars/preset1.png"), + include_bytes!("../assets/avatars/preset2.png"), + include_bytes!("../assets/avatars/preset3.png"), + include_bytes!("../assets/avatars/preset4.png"), + include_bytes!("../assets/avatars/preset5.png"), + include_bytes!("../assets/avatars/preset6.png"), +]; + +/// A participant's chosen avatar. Rides the gossip presence plane (`PeerState`) +/// and is persisted in `AppConfig`. `Monogram` is the default fallback (rendered +/// from name + a colour key, no image). `Preset(i)` selects a bundled preset by +/// zero-based index. (Custom uploaded images are W4 Phase 3 — a future variant.) +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +pub enum Avatar { + /// Initials-on-colour fallback. + #[default] + Monogram, + /// A bundled preset by zero-based index; out-of-range falls back to monogram. + Preset(u8), +} + +impl Avatar { + /// The embedded PNG bytes for this avatar, or `None` when it's a monogram + /// (or an out-of-range preset index, which renders as a monogram). + pub fn preset_png(&self) -> Option<&'static [u8]> { + match self { + Avatar::Preset(i) => PRESET_PNGS.get(*i as usize).copied(), + Avatar::Monogram => None, + } + } +} + /// Curated palette the monogram background is chosen from. Picked to be distinct /// and legible under the app's (mostly dark) themes; the matching text colour is /// decided per-background by [`use_dark_text_on`]. Order is part of the stable @@ -92,6 +133,32 @@ mod tests { let _ = color_for_key(""); } + #[test] + fn avatar_default_is_monogram() { + assert_eq!(Avatar::default(), Avatar::Monogram); + assert!(Avatar::default().preset_png().is_none()); + } + + #[test] + fn preset_png_in_range_and_out_of_range() { + // Every declared preset index resolves to embedded bytes. + for i in 0..PRESET_COUNT { + assert!(Avatar::Preset(i).preset_png().is_some(), "preset {i} missing"); + } + // Out-of-range index gracefully yields None (→ monogram fallback). + assert!(Avatar::Preset(PRESET_COUNT).preset_png().is_none()); + assert!(Avatar::Preset(250).preset_png().is_none()); + } + + #[test] + fn avatar_serde_round_trips_and_defaults() { + // Round-trips through JSON (it rides presence + persists in config). + for a in [Avatar::Monogram, Avatar::Preset(0), Avatar::Preset(5)] { + let s = serde_json::to_string(&a).unwrap(); + assert_eq!(serde_json::from_str::(&s).unwrap(), a); + } + } + #[test] fn dark_text_chosen_on_light_backgrounds() { assert!(use_dark_text_on((0xFF, 0xFF, 0xFF))); // white bg → dark text diff --git a/src/bin/test_net.rs b/src/bin/test_net.rs index c80740c..9298541 100644 --- a/src/bin/test_net.rs +++ b/src/bin/test_net.rs @@ -62,6 +62,7 @@ async fn main() -> Result<(), Box> { is_muted: false, addr: endpoint_a.addr(), sharing: None, + avatar: Default::default(), }; room_a.join(&ticket_str, state_a, vec![]).await?; println!("Node A joined topic."); @@ -80,6 +81,7 @@ async fn main() -> Result<(), Box> { is_muted: false, addr: endpoint_b.addr(), sharing: None, + avatar: Default::default(), }; room_b.join(&ticket_str, state_b, vec![]).await?; println!("Node B joined topic."); diff --git a/src/config.rs b/src/config.rs index 8976e75..2d6a248 100644 --- a/src/config.rs +++ b/src/config.rs @@ -176,6 +176,9 @@ pub struct AppConfig { /// Chosen UI colour theme. #[serde(default)] pub theme: AppTheme, + /// Our chosen avatar (W4): monogram fallback or a bundled preset. + #[serde(default)] + pub avatar: crate::avatar::Avatar, /// What a call recording captures (mixed / per-peer stems / both). #[serde(default)] pub recording_mode: RecordingMode, @@ -233,6 +236,7 @@ impl Default for AppConfig { chat_drawer_width: default_chat_drawer_width(), room_layout: RoomLayout::default(), theme: AppTheme::default(), + avatar: crate::avatar::Avatar::default(), recording_mode: RecordingMode::default(), custom_sound_self_join: None, custom_sound_peer_join: None, diff --git a/src/core/messages.rs b/src/core/messages.rs index 5922d3e..f93556f 100644 --- a/src/core/messages.rs +++ b/src/core/messages.rs @@ -4,9 +4,11 @@ use iroh::EndpointId; #[derive(Debug, Clone)] pub enum CoreCommand { - Join { name: String, ticket: String, input_device: Option, output_device: Option, echo_cancellation: bool }, + Join { name: String, ticket: String, input_device: Option, output_device: Option, echo_cancellation: bool, avatar: crate::avatar::Avatar }, Leave, ToggleMute, + /// Change our avatar (W4) and re-announce it to the room over presence. + SetAvatar(crate::avatar::Avatar), ToggleDeafen, SetPttMode(bool), SetPttActive(bool), diff --git a/src/core/mod.rs b/src/core/mod.rs index ea13640..130b6b8 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -454,6 +454,9 @@ async fn run_core_loop( // Peers locally muted by us: decoded for level metering but not mixed. let locally_muted = Arc::new(Mutex::new(HashSet::::new())); let mut current_name = "Anonymous".to_string(); + // Our chosen avatar (W4), set on Join and changeable via SetAvatar; included + // in every self-state we announce over presence. + let mut current_avatar = crate::avatar::Avatar::default(); let mut network_mode = NetworkMode::default(); // Pixelpass binary override (config), and the ticket of our own active screen // share (rides our presence so the room — incl. late joiners — can watch). @@ -466,8 +469,9 @@ async fn run_core_loop( while let Some(cmd) = cmd_rx.recv().await { match cmd { - CoreCommand::Join { name, ticket, input_device, output_device, echo_cancellation } => { + CoreCommand::Join { name, ticket, input_device, output_device, echo_cancellation, avatar } => { current_name = name.clone(); + current_avatar = avatar; // Finalize any recording before tearing down the old session — its // capture/mixer feeders are about to stop. @@ -561,6 +565,7 @@ async fn run_core_loop( is_muted: is_muted.load(Ordering::Relaxed), addr: endpoint.addr(), sharing: None, + avatar: current_avatar.clone(), }; // Retain peers across leave so a rejoin can dial them (A8). A @@ -1066,6 +1071,23 @@ async fn run_core_loop( is_muted: new_state, addr: session.endpoint.addr(), sharing: current_sharing.clone(), + avatar: current_avatar.clone(), + }; + let _ = session.room_state.update_self_state(self_state).await; + } + } + + CoreCommand::SetAvatar(avatar) => { + current_avatar = avatar; + // Re-announce presence so the room (incl. late joiners, via the + // retained presence) picks up the new avatar (W4). + if let Some(session) = &active_session { + let self_state = PeerState { + name: current_name.clone(), + is_muted: is_muted.load(Ordering::Relaxed), + addr: session.endpoint.addr(), + sharing: current_sharing.clone(), + avatar: current_avatar.clone(), }; let _ = session.room_state.update_self_state(self_state).await; } @@ -1274,6 +1296,7 @@ async fn run_core_loop( is_muted: is_muted.load(Ordering::Relaxed), addr: session.endpoint.addr(), sharing: Some(ticket), + avatar: current_avatar.clone(), }; let _ = session.room_state.update_self_state(self_state).await; let _ = ui_tx.send(UiEvent::ScreenShareStarted).await; @@ -1298,6 +1321,7 @@ async fn run_core_loop( is_muted: is_muted.load(Ordering::Relaxed), addr: session.endpoint.addr(), sharing: None, + avatar: current_avatar.clone(), }; let _ = session.room_state.update_self_state(self_state).await; } diff --git a/src/network/gossip.rs b/src/network/gossip.rs index b06b1a1..2cad05c 100644 --- a/src/network/gossip.rs +++ b/src/network/gossip.rs @@ -493,6 +493,7 @@ mod tests { is_muted: true, addr, sharing: None, + avatar: crate::avatar::Avatar::default(), } } diff --git a/src/network/mod.rs b/src/network/mod.rs index 5026d9f..c609c98 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -34,6 +34,11 @@ pub struct PeerState { /// older configs / peers that predate the field still deserialize. #[serde(default)] pub sharing: Option, + /// This peer's chosen avatar (W4). Rides presence so it reaches everyone + /// (incl. late joiners) with no server, like `sharing`. Defaulted so older + /// peers/configs that predate the field still deserialize (→ monogram). + #[serde(default)] + pub avatar: crate::avatar::Avatar, } #[derive(Debug, Clone)] @@ -180,6 +185,7 @@ mod tests { is_muted: false, addr, sharing: None, + avatar: crate::avatar::Avatar::default(), } }