Compare commits
5
Commits
9d1cc771fc
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f151e38b4
|
||
|
|
8d54e6e954
|
||
|
|
fa8bd86281
|
||
|
|
6379e97db4
|
||
|
|
d376983296
|
Generated
+312
-324
@@ -49,9 +49,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.102"
|
version = "1.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atk"
|
name = "atk"
|
||||||
@@ -99,9 +99,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-lc-rs"
|
name = "aws-lc-rs"
|
||||||
version = "1.17.0"
|
version = "1.17.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00"
|
checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-sys",
|
"aws-lc-sys",
|
||||||
"untrusted 0.7.1",
|
"untrusted 0.7.1",
|
||||||
@@ -110,14 +110,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aws-lc-sys"
|
name = "aws-lc-sys"
|
||||||
version = "0.41.0"
|
version = "0.43.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
|
checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cmake",
|
"cmake",
|
||||||
"dunce",
|
"dunce",
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -155,9 +156,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.13.0"
|
version = "2.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
@@ -218,9 +219,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.25.0"
|
version = "1.25.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
|
checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
@@ -230,9 +231,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.12.0"
|
version = "1.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593"
|
checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@@ -243,7 +244,7 @@ version = "0.18.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
|
checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"cairo-sys-rs",
|
"cairo-sys-rs",
|
||||||
"glib",
|
"glib",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -264,9 +265,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "camino"
|
name = "camino"
|
||||||
version = "1.2.3"
|
version = "1.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4ce8d3bd5823c7504d3f579f13e7b2f3da252fcb938c594d5680ee508bf846f"
|
checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
@@ -291,7 +292,7 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -306,9 +307,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.65"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
|
checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"jobserver",
|
"jobserver",
|
||||||
@@ -351,9 +352,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chacha20"
|
name = "chacha20"
|
||||||
version = "0.10.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures 0.3.0",
|
"cpufeatures 0.3.0",
|
||||||
@@ -432,7 +433,7 @@ version = "0.25.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
@@ -445,7 +446,7 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -494,27 +495,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.15"
|
version = "0.5.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-queue"
|
name = "crossbeam-queue"
|
||||||
version = "0.3.12"
|
version = "0.3.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
@@ -546,7 +547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -596,7 +597,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -607,14 +608,14 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dbus"
|
name = "dbus"
|
||||||
version = "0.9.11"
|
version = "0.9.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73"
|
checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"libdbus-sys",
|
"libdbus-sys",
|
||||||
@@ -648,7 +649,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -688,7 +689,7 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"libc",
|
"libc",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -702,7 +703,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -725,7 +726,7 @@ checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -811,14 +812,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embed-resource"
|
name = "embed-resource"
|
||||||
version = "3.0.9"
|
version = "3.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb"
|
checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.3+spec-1.1.0",
|
||||||
"vswhom",
|
"vswhom",
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
@@ -888,9 +889,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.4.1"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fdeflate"
|
name = "fdeflate"
|
||||||
@@ -927,17 +928,6 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flume"
|
|
||||||
version = "0.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"spin",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -979,7 +969,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1014,9 +1004,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -1029,9 +1019,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -1039,15 +1029,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-executor"
|
name = "futures-executor"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
|
checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@@ -1067,38 +1057,38 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.32"
|
version = "0.3.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -1314,7 +1304,7 @@ version = "0.18.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
|
checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-executor",
|
"futures-executor",
|
||||||
@@ -1342,7 +1332,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1357,9 +1347,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.3"
|
version = "0.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gobject-sys"
|
name = "gobject-sys"
|
||||||
@@ -1421,7 +1411,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1513,9 +1503,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "1.0.1"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http",
|
"http",
|
||||||
@@ -1523,9 +1513,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body-util"
|
name = "http-body-util"
|
||||||
version = "0.1.3"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -1542,9 +1532,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.10.1"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498"
|
checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1776,20 +1766,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inotify"
|
name = "inotify"
|
||||||
version = "0.11.2"
|
version = "0.11.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1"
|
checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"inotify-sys",
|
"inotify-sys",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inotify-sys"
|
name = "inotify-sys"
|
||||||
version = "0.1.5"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -1898,24 +1888,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.34"
|
version = "0.1.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.4.3",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.102"
|
version = "0.3.103"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
|
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -1950,7 +1940,7 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
|
checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"serde",
|
"serde",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
@@ -1971,7 +1961,7 @@ version = "1.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
|
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2119,9 +2109,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.186"
|
version = "0.2.188"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
checksum = "22053b6a34f84abc97f9129e61334f40174659a1b9bd18c970b83db6a9a6348b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libdbus-sys"
|
name = "libdbus-sys"
|
||||||
@@ -2159,9 +2149,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
|
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
@@ -2200,9 +2190,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.32"
|
version = "0.4.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markup5ever"
|
name = "markup5ever"
|
||||||
@@ -2217,12 +2207,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mdns-sd"
|
name = "mdns-sd"
|
||||||
version = "0.20.0"
|
version = "0.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "892f96f6d2ebe1ea641279f986ac52a2a6bac71e8f743bb258315cfe2bd7e88e"
|
checksum = "f18d8ec9d1869796fb2910d95f4d957072df0b6a22e247a1d760d8b4c805e17a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"flume 0.11.1",
|
"flume",
|
||||||
"if-addrs",
|
"if-addrs",
|
||||||
"log",
|
"log",
|
||||||
"mio",
|
"mio",
|
||||||
@@ -2232,9 +2222,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.2"
|
version = "2.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
@@ -2272,9 +2262,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.2.1"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
|
checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -2299,7 +2289,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"png 0.18.1",
|
"png 0.18.1",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2309,7 +2299,7 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
|
checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"jni-sys 0.3.1",
|
"jni-sys 0.3.1",
|
||||||
"log",
|
"log",
|
||||||
"ndk-sys",
|
"ndk-sys",
|
||||||
@@ -2339,7 +2329,7 @@ version = "8.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"fsevent-sys",
|
"fsevent-sys",
|
||||||
"inotify",
|
"inotify",
|
||||||
"kqueue",
|
"kqueue",
|
||||||
@@ -2357,7 +2347,7 @@ version = "2.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
|
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2423,7 +2413,7 @@ dependencies = [
|
|||||||
"proc-macro-crate 3.5.0",
|
"proc-macro-crate 3.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2451,7 +2441,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
@@ -2464,7 +2454,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
]
|
]
|
||||||
@@ -2485,7 +2475,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"dispatch2",
|
"dispatch2",
|
||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
@@ -2496,7 +2486,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"dispatch2",
|
"dispatch2",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
@@ -2529,7 +2519,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"objc2-core-graphics",
|
"objc2-core-graphics",
|
||||||
@@ -2556,7 +2546,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"libc",
|
"libc",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -2569,7 +2559,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
@@ -2580,7 +2570,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
@@ -2592,7 +2582,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-cloud-kit",
|
"objc2-cloud-kit",
|
||||||
@@ -2623,7 +2613,7 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f"
|
checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
@@ -2639,14 +2629,13 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "open"
|
name = "open"
|
||||||
version = "5.3.5"
|
version = "5.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c"
|
checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"is-wsl",
|
"is-wsl",
|
||||||
"libc",
|
"libc",
|
||||||
"pathdiff",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2719,12 +2708,6 @@ dependencies = [
|
|||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pathdiff"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -2772,7 +2755,7 @@ dependencies = [
|
|||||||
"phf_shared",
|
"phf_shared",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2798,9 +2781,9 @@ checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plist"
|
name = "plist"
|
||||||
version = "1.9.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1"
|
checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"indexmap 2.14.0",
|
"indexmap 2.14.0",
|
||||||
@@ -2828,7 +2811,7 @@ version = "0.18.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"fdeflate",
|
"fdeflate",
|
||||||
"flate2",
|
"flate2",
|
||||||
@@ -2891,7 +2874,7 @@ version = "3.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml_edit 0.25.12+spec-1.1.0",
|
"toml_edit 0.25.13+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2920,27 +2903,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.106"
|
version = "1.0.107"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.39.4"
|
version = "0.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
|
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.45"
|
version = "1.0.47"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -2972,9 +2955,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.10.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
|
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"getrandom 0.4.3",
|
"getrandom 0.4.3",
|
||||||
@@ -3027,7 +3010,7 @@ version = "0.5.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3038,34 +3021,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ref-cast"
|
name = "ref-cast"
|
||||||
version = "1.0.25"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
|
checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ref-cast-impl",
|
"ref-cast-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ref-cast-impl"
|
name = "ref-cast-impl"
|
||||||
version = "1.0.25"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 3.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.4"
|
version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3075,9 +3058,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.14"
|
version = "0.4.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3164,9 +3147,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.2"
|
version = "2.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
|
checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
@@ -3183,7 +3166,7 @@ version = "1.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@@ -3192,9 +3175,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.40"
|
version = "0.23.42"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
|
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"log",
|
"log",
|
||||||
@@ -3207,9 +3190,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.14.1"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@@ -3228,15 +3211,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-codec"
|
name = "s2n-codec"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a650d3f187901f3519ec8a1fe7da3faccc0b2fb40f350eda2c7851fdf2bda0f6"
|
checksum = "5323db3697b61f4f5161c346573a9089cf6b4e13656991a5e84c18dc91bc17d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -3245,16 +3228,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic"
|
name = "s2n-quic"
|
||||||
version = "1.82.0"
|
version = "1.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c27c34127facefcd3e5530c4de5739a62cd4a593710b1194dacbd8e884b6be92"
|
checksum = "54598beca69a3970deaa988edcb2a00f5812433db085f23b08f3ebe40a3bf86d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cuckoofilter",
|
"cuckoofilter",
|
||||||
"futures",
|
"futures",
|
||||||
"hash_hasher",
|
"hash_hasher",
|
||||||
"rand 0.10.1",
|
"rand 0.10.2",
|
||||||
"s2n-codec",
|
"s2n-codec",
|
||||||
"s2n-quic-core",
|
"s2n-quic-core",
|
||||||
"s2n-quic-crypto",
|
"s2n-quic-crypto",
|
||||||
@@ -3269,9 +3252,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-core"
|
name = "s2n-quic-core"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79fbc3f06797d985363f74de105d18554b5a272b924b166d73a6564943da1230"
|
checksum = "1ebf9897515906f85528b3301f6f54d9d7b02827a9f5a2e13fcd5c965161571e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@@ -3291,9 +3274,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-crypto"
|
name = "s2n-quic-crypto"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e58ea5aa39eecc29559d1e1bb4a5d55a747fa7b80cff5a3400c57489510644e3"
|
checksum = "fc67b81d13e30e2b5d69a154fa312bfe523ec3a95c81a694c15c902a210b80de"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -3305,9 +3288,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-platform"
|
name = "s2n-quic-platform"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4eebb6007139cfffdf3d473d39f01a214032c339432a6293b16b0f7b25343f40"
|
checksum = "6a488f217c6dec4eac1a0c61d25af5235a2abcd03af31ae2f3bc047901caad80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -3320,9 +3303,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-rustls"
|
name = "s2n-quic-rustls"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb0084afa65eefae2c37d9ab44118a14dfc5bb78dbf997c0f5176f7cf8d2e633"
|
checksum = "7349312d82172938036b92119f666ba93ce9bed7cbfeedc968115574932398fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -3334,9 +3317,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-tls"
|
name = "s2n-quic-tls"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91150b25ce824ffea581b449ad04acf9b4aef2fa68a46f667cdc9cc6f7b87823"
|
checksum = "093191572f12842106d8e37326d1a99dd5d6c96bfcdd14c1718e6b6ae51730cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"errno",
|
"errno",
|
||||||
@@ -3349,9 +3332,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-tls-default"
|
name = "s2n-quic-tls-default"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1f5ae64863972facee778dc80a24317e613f035296631f267b71f225e569c22"
|
checksum = "3488ac2d7fab0b4921e412dbc8599419f8d1bfd6f9c4422fdb4ef8b5e4a33c68"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"s2n-quic-rustls",
|
"s2n-quic-rustls",
|
||||||
"s2n-quic-tls",
|
"s2n-quic-tls",
|
||||||
@@ -3359,9 +3342,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-quic-transport"
|
name = "s2n-quic-transport"
|
||||||
version = "0.82.0"
|
version = "0.83.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b82fca53ce1734cc1d1dca96cc9ceb65ed528f27cb43b7de865215b6cf17908"
|
checksum = "899b3ffff460a7d4e8b8ed0933aca1b4d309a1e52bfffdd9fb54bd78e3c922b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
@@ -3377,9 +3360,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-tls"
|
name = "s2n-tls"
|
||||||
version = "0.3.38"
|
version = "0.3.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6188b4ff1e1fe554f3ed53ef81c550760006ff8003f08c3539447afcd20b6e8b"
|
checksum = "db76c77f8c280a581031ad88f8bc09c18581b11d785f530025d135dd22c03b9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"hex",
|
"hex",
|
||||||
@@ -3390,9 +3373,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "s2n-tls-sys"
|
name = "s2n-tls-sys"
|
||||||
version = "0.3.38"
|
version = "0.3.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbe6d97b0ca3d01056eb1446912799181f1e1ec6922601f80446f4f0337edb67"
|
checksum = "ce900e83d6cb0dee6623e6e0c955ce1973ad2994ce76c5ed577e27fa564cb82b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"cc",
|
"cc",
|
||||||
@@ -3456,7 +3439,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_derive_internals",
|
"serde_derive_internals",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3471,7 +3454,7 @@ version = "0.36.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c"
|
checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"log",
|
"log",
|
||||||
@@ -3496,9 +3479,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.229"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
@@ -3518,22 +3501,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_core"
|
name = "serde_core"
|
||||||
version = "1.0.228"
|
version = "1.0.229"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.228"
|
version = "1.0.229"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 3.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3544,14 +3527,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.150"
|
version = "1.0.151"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -3562,13 +3545,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_repr"
|
name = "serde_repr"
|
||||||
version = "0.1.20"
|
version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
|
checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 3.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3618,7 +3601,7 @@ dependencies = [
|
|||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3640,7 +3623,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3722,9 +3705,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
@@ -3746,20 +3729,20 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket-pktinfo"
|
name = "socket-pktinfo"
|
||||||
version = "0.3.2"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "927136cc2ae6a1b0e66ac6b1210902b75c3f726db004a73bc18686dcd0dcd22f"
|
checksum = "3e8e43b4bdce7cff8a4d3f8025ee38fce5ca138fab868ebbf9529c81328fbf9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"socket2",
|
"socket2",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.4"
|
version = "0.6.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
|
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
@@ -3815,9 +3798,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
]
|
]
|
||||||
@@ -3859,7 +3842,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -3876,7 +3859,7 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros-core",
|
"sqlx-macros-core",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3897,7 +3880,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
@@ -3909,7 +3892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c"
|
checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"flume 0.12.0",
|
"flume",
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -3921,7 +3904,7 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
@@ -3980,7 +3963,7 @@ dependencies = [
|
|||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4012,9 +3995,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.118"
|
version = "2.0.119"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a207d6d6a2b7fc470b80443726053f18a2481b7e1eee970597051596567987a3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4038,7 +4032,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4060,7 +4054,7 @@ version = "0.35.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
|
checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"block2",
|
"block2",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
@@ -4102,7 +4096,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4113,9 +4107,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "2.11.3"
|
version = "2.11.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2616f96cb644bf2c5c456d9de4d5d5100e592d7424c74d8b55c5cb96e359e93"
|
checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4152,7 +4146,7 @@ dependencies = [
|
|||||||
"tauri-runtime",
|
"tauri-runtime",
|
||||||
"tauri-runtime-wry",
|
"tauri-runtime-wry",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tray-icon",
|
"tray-icon",
|
||||||
"url",
|
"url",
|
||||||
@@ -4201,9 +4195,9 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"time",
|
"time",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -4219,7 +4213,7 @@ dependencies = [
|
|||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
"tauri-codegen",
|
"tauri-codegen",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
]
|
]
|
||||||
@@ -4242,9 +4236,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-dialog"
|
name = "tauri-plugin-dialog"
|
||||||
version = "2.7.1"
|
version = "2.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884"
|
checksum = "b2d3c1dbe38037e7f590cdf2492594d5ceebe031e7bc7e827509b22a999d2940"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
@@ -4254,7 +4248,7 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"tauri-plugin-fs",
|
"tauri-plugin-fs",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4277,8 +4271,8 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.3+spec-1.1.0",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4299,22 +4293,22 @@ dependencies = [
|
|||||||
"shared_child",
|
"shared_child",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-store"
|
name = "tauri-plugin-store"
|
||||||
version = "2.4.3"
|
version = "2.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c72dda16786eb4a3f903e43a17b64d8d78dc0f00fe2aa4b757c28f617a8630b"
|
checksum = "6708afbe549f176b712066e71648ba8fafba20789453718260c7ca356733cb0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@@ -4337,7 +4331,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webview2-com",
|
"webview2-com",
|
||||||
@@ -4346,9 +4340,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "2.11.3"
|
version = "2.11.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe41e015bf8fc4d6477ff4926a0ef769dc64ff34c7b0038b6f7cacae892acb5c"
|
checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@@ -4400,8 +4394,8 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"swift-rs",
|
"swift-rs",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.3+spec-1.1.0",
|
||||||
"url",
|
"url",
|
||||||
"urlpattern",
|
"urlpattern",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -4416,17 +4410,16 @@ checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.3+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendril"
|
name = "tendril"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24"
|
checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"new_debug_unreachable",
|
"new_debug_unreachable",
|
||||||
"utf-8",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4440,11 +4433,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.18",
|
"thiserror-impl 2.0.19",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4455,34 +4448,34 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.18"
|
version = "2.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 3.0.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.49"
|
version = "0.3.54"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469"
|
checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -4502,9 +4495,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.29"
|
version = "0.2.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d"
|
checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"time-core",
|
"time-core",
|
||||||
@@ -4522,9 +4515,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.11.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
|
checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec_macros",
|
"tinyvec_macros",
|
||||||
]
|
]
|
||||||
@@ -4537,9 +4530,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.52.3"
|
version = "1.53.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -4554,13 +4547,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.7.0"
|
version = "2.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4576,14 +4569,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.18"
|
version = "0.7.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
|
checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"libc",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@@ -4617,9 +4611,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "1.1.2+spec-1.1.0"
|
version = "1.1.3+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
|
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.14.0",
|
"indexmap 2.14.0",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
@@ -4627,7 +4621,7 @@ dependencies = [
|
|||||||
"toml_datetime 1.1.1+spec-1.1.0",
|
"toml_datetime 1.1.1+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"toml_writer",
|
"toml_writer",
|
||||||
"winnow 1.0.3",
|
"winnow 1.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4683,14 +4677,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.25.12+spec-1.1.0"
|
version = "0.25.13+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
|
checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap 2.14.0",
|
"indexmap 2.14.0",
|
||||||
"toml_datetime 1.1.1+spec-1.1.0",
|
"toml_datetime 1.1.1+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"winnow 1.0.3",
|
"winnow 1.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4699,14 +4693,14 @@ version = "1.1.2+spec-1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow 1.0.3",
|
"winnow 1.0.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_writer"
|
name = "toml_writer"
|
||||||
version = "1.1.1+spec-1.1.0"
|
version = "1.1.2+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
|
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
@@ -4729,7 +4723,7 @@ version = "0.6.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 2.13.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
@@ -4773,7 +4767,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4829,7 +4823,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"png 0.18.1",
|
"png 0.18.1",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4941,12 +4935,6 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf-8"
|
|
||||||
version = "0.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8_iter"
|
name = "utf8_iter"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -4955,9 +4943,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.23.3"
|
version = "1.24.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7"
|
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.4.3",
|
"getrandom 0.4.3",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -5051,9 +5039,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.125"
|
version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
|
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -5064,9 +5052,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.75"
|
version = "0.4.76"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280"
|
checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -5074,9 +5062,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.125"
|
version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
|
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
@@ -5084,22 +5072,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.125"
|
version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
|
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.125"
|
version = "0.2.126"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
|
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -5119,9 +5107,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.102"
|
version = "0.3.103"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d"
|
checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
@@ -5205,7 +5193,7 @@ checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5214,7 +5202,7 @@ version = "0.38.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
|
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"windows 0.61.3",
|
"windows 0.61.3",
|
||||||
"windows-core 0.61.2",
|
"windows-core 0.61.2",
|
||||||
]
|
]
|
||||||
@@ -5364,7 +5352,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5375,7 +5363,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5721,9 +5709,9 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "1.0.3"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
|
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -5783,7 +5771,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"soup3",
|
"soup3",
|
||||||
"tao-macros",
|
"tao-macros",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.19",
|
||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
@@ -5834,28 +5822,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.52"
|
version = "0.8.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f"
|
checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.52"
|
version = "0.8.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930"
|
checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5875,7 +5863,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5896,7 +5884,7 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5929,11 +5917,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.118",
|
"syn 2.0.119",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ async fn unrar_listing(program: &Path, archive: &Path) -> eyre::Result<RarListin
|
|||||||
.arg("lt")
|
.arg("lt")
|
||||||
.arg("-cfg-")
|
.arg("-cfg-")
|
||||||
.arg(archive)
|
.arg(archive)
|
||||||
|
.kill_on_drop(true)
|
||||||
.output()
|
.output()
|
||||||
.await?;
|
.await?;
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
@@ -329,6 +330,9 @@ async fn stream_unrar_entries(
|
|||||||
.arg(archive)
|
.arg(archive)
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
|
// Safety net: if this task is dropped before its cancel/error path runs
|
||||||
|
// (e.g. on shutdown), tokio still kills unrar instead of orphaning it.
|
||||||
|
.kill_on_drop(true)
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
|
|
||||||
let result = async {
|
let result = async {
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ use std::{
|
|||||||
fs::{self, OpenOptions},
|
fs::{self, OpenOptions},
|
||||||
io::{self, Read as _, Seek as _, SeekFrom, Write as _},
|
io::{self, Read as _, Seek as _, SeekFrom, Write as _},
|
||||||
path::{Component, Path, PathBuf},
|
path::{Component, Path, PathBuf},
|
||||||
sync::{Arc, Mutex, OnceLock},
|
sync::{
|
||||||
|
Arc,
|
||||||
|
Mutex,
|
||||||
|
OnceLock,
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
|
},
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,7 +32,10 @@ use lanspread_peer::{
|
|||||||
start_peer_with_options,
|
start_peer_with_options,
|
||||||
};
|
};
|
||||||
use tauri::{AppHandle, Emitter as _, Manager};
|
use tauri::{AppHandle, Emitter as _, Manager};
|
||||||
use tauri_plugin_shell::{ShellExt, process::Command};
|
use tauri_plugin_shell::{
|
||||||
|
ShellExt,
|
||||||
|
process::{Command, CommandChild, CommandEvent},
|
||||||
|
};
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
RwLock,
|
RwLock,
|
||||||
mpsc::{UnboundedReceiver, UnboundedSender},
|
mpsc::{UnboundedReceiver, UnboundedSender},
|
||||||
@@ -92,8 +100,32 @@ struct LanSpreadState {
|
|||||||
main_log_sink: OnceLock<MainLogSink>,
|
main_log_sink: OnceLock<MainLogSink>,
|
||||||
active_outbound_transfers: OutboundTransfers,
|
active_outbound_transfers: OutboundTransfers,
|
||||||
outbound_transfer_emit: Arc<RwLock<OutboundTransferEmitState>>,
|
outbound_transfer_emit: Arc<RwLock<OutboundTransferEmitState>>,
|
||||||
|
/// Live unrar sidecar processes, so they can be killed if the launcher exits
|
||||||
|
/// mid-unpack. The shell plugin does not kill spawned children on app exit,
|
||||||
|
/// and the OS does not cascade-kill child processes, so without this an
|
||||||
|
/// in-progress unrar keeps running after the launcher closes.
|
||||||
|
active_unrar_children: Arc<Mutex<UnrarChildRegistry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Live unrar sidecar children plus a shutdown latch.
|
||||||
|
///
|
||||||
|
/// Children are keyed by a monotonic id (not pid) so a finishing install never
|
||||||
|
/// deregisters another install's child after a pid is recycled. The
|
||||||
|
/// `shutting_down` latch closes a time-of-check/time-of-use hole: the exit kill
|
||||||
|
/// sweep drains the map only once, so a child registered *after* the sweep (an
|
||||||
|
/// install task caught between `spawn()` and registration, or a later archive in
|
||||||
|
/// a multi-archive install — `unpack_archives` does not observe the shutdown
|
||||||
|
/// token) would be orphaned. Once the latch is set under this mutex, registration
|
||||||
|
/// kills the child immediately instead of inserting it where nothing will reap it.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct UnrarChildRegistry {
|
||||||
|
shutting_down: bool,
|
||||||
|
children: HashMap<u64, CommandChild>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Monotonic id source for [`UnrarChildRegistry`] entries.
|
||||||
|
static UNRAR_CHILD_SEQ: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
struct InstallSettings {
|
struct InstallSettings {
|
||||||
account_name: String,
|
account_name: String,
|
||||||
@@ -1303,16 +1335,19 @@ async fn run_unrar_sidecar(
|
|||||||
paths: &UnrarPaths,
|
paths: &UnrarPaths,
|
||||||
started_at_ms: u64,
|
started_at_ms: u64,
|
||||||
) -> eyre::Result<()> {
|
) -> eyre::Result<()> {
|
||||||
let out = match sidecar
|
// Spawn (instead of `.output()`) so we keep a killable handle. The shell
|
||||||
|
// plugin's `output()` drops the `CommandChild` immediately and only drains
|
||||||
|
// the event channel, leaving the unrar process orphaned if the launcher
|
||||||
|
// exits before extraction finishes.
|
||||||
|
let (mut events, child) = match sidecar
|
||||||
.arg("x") // extract files
|
.arg("x") // extract files
|
||||||
.arg(&paths.archive)
|
.arg(&paths.archive)
|
||||||
.arg("-y") // Assume Yes on all queries
|
.arg("-y") // Assume Yes on all queries
|
||||||
.arg("-o") // Set overwrite mode
|
.arg("-o") // Set overwrite mode
|
||||||
.arg(&paths.destination_arg)
|
.arg(&paths.destination_arg)
|
||||||
.output()
|
.spawn()
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
Ok(out) => out,
|
Ok(spawned) => spawned,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let stderr = format!("failed to run unrar sidecar: {err}");
|
let stderr = format!("failed to run unrar sidecar: {err}");
|
||||||
record_unpack_failure(
|
record_unpack_failure(
|
||||||
@@ -1327,10 +1362,42 @@ async fn run_unrar_sidecar(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let stdout = clean_terminal_log(&String::from_utf8_lossy(&out.stdout));
|
// Register the live child so a launcher exit can kill it, and deregister it
|
||||||
let stderr = clean_terminal_log(&String::from_utf8_lossy(&out.stderr));
|
// automatically on every exit path via the RAII guard.
|
||||||
let status_code = out.status.code();
|
let registry = app_handle
|
||||||
let success = out.status.success();
|
.state::<LanSpreadState>()
|
||||||
|
.active_unrar_children
|
||||||
|
.clone();
|
||||||
|
let child_id = UNRAR_CHILD_SEQ.fetch_add(1, Ordering::Relaxed);
|
||||||
|
register_unrar_child(®istry, child_id, child);
|
||||||
|
let _child_guard = UnrarChildGuard { registry, child_id };
|
||||||
|
|
||||||
|
let mut stdout_bytes = Vec::new();
|
||||||
|
let mut stderr_bytes = Vec::new();
|
||||||
|
let mut status_code = None;
|
||||||
|
while let Some(event) = events.recv().await {
|
||||||
|
match event {
|
||||||
|
CommandEvent::Stdout(line) => {
|
||||||
|
stdout_bytes.extend(line);
|
||||||
|
stdout_bytes.push(b'\n');
|
||||||
|
}
|
||||||
|
CommandEvent::Stderr(line) => {
|
||||||
|
stderr_bytes.extend(line);
|
||||||
|
stderr_bytes.push(b'\n');
|
||||||
|
}
|
||||||
|
CommandEvent::Terminated(payload) => {
|
||||||
|
status_code = payload.code;
|
||||||
|
}
|
||||||
|
CommandEvent::Error(err) => {
|
||||||
|
log::warn!("unrar sidecar event error: {err}");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stdout = clean_terminal_log(&String::from_utf8_lossy(&stdout_bytes));
|
||||||
|
let stderr = clean_terminal_log(&String::from_utf8_lossy(&stderr_bytes));
|
||||||
|
let success = status_code == Some(0);
|
||||||
|
|
||||||
record_unpack_log(
|
record_unpack_log(
|
||||||
app_handle,
|
app_handle,
|
||||||
@@ -1360,6 +1427,79 @@ async fn run_unrar_sidecar(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tracks a spawned unrar sidecar so the launcher can kill it on shutdown.
|
||||||
|
///
|
||||||
|
/// If shutdown has already begun (or the registry is poisoned), the child is
|
||||||
|
/// killed immediately instead of inserted, since the exit kill sweep has already
|
||||||
|
/// run and would never reap a late registration.
|
||||||
|
fn register_unrar_child(
|
||||||
|
registry: &Arc<Mutex<UnrarChildRegistry>>,
|
||||||
|
child_id: u64,
|
||||||
|
child: CommandChild,
|
||||||
|
) {
|
||||||
|
let Ok(mut guard) = registry.lock() else {
|
||||||
|
// A poisoned registry means we can no longer guarantee the child is
|
||||||
|
// killed on exit, so kill it now rather than risk orphaning it.
|
||||||
|
let pid = child.pid();
|
||||||
|
log::warn!("unrar child registry is poisoned; killing pid {pid} immediately");
|
||||||
|
if let Err(err) = child.kill() {
|
||||||
|
log::warn!("Failed to kill untracked unrar child (pid {pid}): {err}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if guard.shutting_down {
|
||||||
|
drop(guard);
|
||||||
|
let pid = child.pid();
|
||||||
|
log::info!("Killing unrar child (pid {pid}) that spawned during shutdown");
|
||||||
|
if let Err(err) = child.kill() {
|
||||||
|
log::warn!("Failed to kill unrar child (pid {pid}) spawned during shutdown: {err}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
guard.children.insert(child_id, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an unrar sidecar from the registry when its `run_unrar_sidecar` call
|
||||||
|
/// returns, regardless of success, error, or early bail.
|
||||||
|
struct UnrarChildGuard {
|
||||||
|
registry: Arc<Mutex<UnrarChildRegistry>>,
|
||||||
|
child_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UnrarChildGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Ok(mut guard) = self.registry.lock() {
|
||||||
|
guard.children.remove(&self.child_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kills every in-progress unrar sidecar and latches the registry into shutdown
|
||||||
|
/// so any install task that spawns unrar after this point kills it on
|
||||||
|
/// registration. Called on app exit so a game install that is mid-extraction does
|
||||||
|
/// not leave `unrar` running after the launcher closes.
|
||||||
|
fn kill_active_unrar_children(app_handle: &AppHandle) {
|
||||||
|
let state = app_handle.state::<LanSpreadState>();
|
||||||
|
let children = {
|
||||||
|
let Ok(mut guard) = state.active_unrar_children.lock() else {
|
||||||
|
log::warn!("unrar child registry is poisoned; cannot kill children on shutdown");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
guard.shutting_down = true;
|
||||||
|
guard.children.drain().collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (_child_id, child) in children {
|
||||||
|
let pid = child.pid();
|
||||||
|
match child.kill() {
|
||||||
|
Ok(()) => log::info!("Killed in-progress unrar child (pid {pid}) on shutdown"),
|
||||||
|
Err(err) => log::warn!("Failed to kill unrar child (pid {pid}) on shutdown: {err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn record_unpack_failure(
|
async fn record_unpack_failure(
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
archive: String,
|
archive: String,
|
||||||
@@ -2249,6 +2389,10 @@ pub fn run() {
|
|||||||
.expect("error while building tauri application")
|
.expect("error while building tauri application")
|
||||||
.run(|app_handle, event| {
|
.run(|app_handle, event| {
|
||||||
if matches!(event, tauri::RunEvent::Exit) {
|
if matches!(event, tauri::RunEvent::Exit) {
|
||||||
|
// Kill unrar first: an in-progress extraction would otherwise keep
|
||||||
|
// running after the launcher closes, and killing it lets the
|
||||||
|
// install task unwind so the peer runtime can stop promptly.
|
||||||
|
kill_active_unrar_children(app_handle);
|
||||||
shutdown_peer_runtime(app_handle);
|
shutdown_peer_runtime(app_handle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+9
-4
@@ -23,9 +23,11 @@ design_handoff_softlan_launcher/
|
|||||||
│ └── design_reference/ the working prototype the spec describes
|
│ └── design_reference/ the working prototype the spec describes
|
||||||
│ ├── SoftLAN Launcher.html open this to see all screens/variants
|
│ ├── SoftLAN Launcher.html open this to see all screens/variants
|
||||||
│ ├── styles.css
|
│ ├── styles.css
|
||||||
│ ├── data.jsx mock catalog + format helpers
|
│ ├── data.jsx mock catalog + LAN peer roster + format helpers
|
||||||
│ ├── components.jsx Icon set, cards, modals, Settings dialog
|
│ ├── components.jsx Icon set, cards, modals, Settings dialog
|
||||||
│ └── launcher.jsx composes chrome + grid + modals
|
│ ├── ctp-chat.jsx Call-to-Play chat panel + shared helpers
|
||||||
|
│ ├── calltoplay.jsx Call-to-Play: quick bars, overlay, create form
|
||||||
|
│ └── launcher.jsx composes chrome + grid + modals + Call to Play
|
||||||
│
|
│
|
||||||
└── logo/ ← THE BRAND MARK
|
└── logo/ ← THE BRAND MARK
|
||||||
├── INTEGRATION.md how to ship the logo (component + assets + lockup)
|
├── INTEGRATION.md how to ship the logo (component + assets + lockup)
|
||||||
@@ -75,8 +77,11 @@ placeholder for it.
|
|||||||
## Status
|
## Status
|
||||||
|
|
||||||
- **Launcher:** high-fidelity, decisions locked. Variant **A** is the chosen
|
- **Launcher:** high-fidelity, decisions locked. Variant **A** is the chosen
|
||||||
chrome. Open questions (empty/error states, logs viewer, keyboard grid nav,
|
chrome. Includes **Call to Play** (rally the LAN around a game + time — live
|
||||||
German strings, "server running" state) are listed at the end of `SPEC.md`.
|
or scheduled, with RSVP, check-in window, and per-call chat). Open questions
|
||||||
|
(empty/error states, logs viewer, keyboard grid nav, German strings, "server
|
||||||
|
running" state, real-time transport for Call to Play) are listed at the end
|
||||||
|
of `SPEC.md`.
|
||||||
- **Logo:** final. Live component + static assets + horizontal lockup (dark and
|
- **Logo:** final. Live component + static assets + horizontal lockup (dark and
|
||||||
light) all included.
|
light) all included.
|
||||||
|
|
||||||
|
|||||||
+163
-4
@@ -32,6 +32,20 @@ The HTML mock includes two chrome variants — **A (single-row)** and **B (two-r
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Changes since v4
|
||||||
|
|
||||||
|
- **Call to Play added.** A whole new coordination layer on top of the library:
|
||||||
|
rally the LAN around a game and a time. Two flavors — **Play now** (give
|
||||||
|
people N minutes to ready up) and **Scheduled** ("Among Us at 20:00", collect
|
||||||
|
"I'm in" RSVPs, then a **check-in window** opens 15 min before start). Every
|
||||||
|
call carries a small group **chat**. Surfaced in three places: a **Call to
|
||||||
|
Play button** in the top bar (with an active-call badge), a persistent stack
|
||||||
|
of **quick bars** above the grid, and a full **overlay** with per-call cards
|
||||||
|
and a create form. Full spec in the new **"Call to Play"** section below.
|
||||||
|
New source files: `calltoplay.jsx`, `ctp-chat.jsx`. New data: a mock LAN
|
||||||
|
**peer roster** (`PEERS`) in `data.jsx`. New icons: `flag`, `chat`, `send`,
|
||||||
|
`clock`, `caretUp`, `caretDown`.
|
||||||
|
|
||||||
## Changes since v3
|
## Changes since v3
|
||||||
|
|
||||||
- **Game-folder button removed from the top bar.** Setting the games directory is a one-time action — it doesn't deserve permanent real estate in the chrome. The button is gone from both top-bar variants, freeing the right zone for the kebab menu alone (variant A) / the storage meter + kebab pair (variant B).
|
- **Game-folder button removed from the top bar.** Setting the games directory is a one-time action — it doesn't deserve permanent real estate in the chrome. The button is gone from both top-bar variants, freeing the right zone for the kebab menu alone (variant A) / the storage meter + kebab pair (variant B).
|
||||||
@@ -84,6 +98,7 @@ The default screen. A grid of game cards over a dark, gradient-tinted background
|
|||||||
- **Right zone (col 3, flex space-between with two sub-groups):**
|
- **Right zone (col 3, flex space-between with two sub-groups):**
|
||||||
- **Sort menu** (pinned left, hugging search) — 36 px button, same surface style as search. Label `Sort: <bold value>` plus 13 px sort-bars icon and 11 px chevron. Click reveals dropdown menu below. Options: `Name (A–Z)`, `Size (largest)`, `Recently Played`, `Status`. This is the only thing on the *left* side of the right zone — it's part of the search cluster, so it hugs the search.
|
- **Sort menu** (pinned left, hugging search) — 36 px button, same surface style as search. Label `Sort: <bold value>` plus 13 px sort-bars icon and 11 px chevron. Click reveals dropdown menu below. Options: `Name (A–Z)`, `Size (largest)`, `Recently Played`, `Status`. This is the only thing on the *left* side of the right zone — it's part of the search cluster, so it hugs the search.
|
||||||
- **Kebab menu** (`⋮`, pinned far-right) — 36×36 button with same surface as search. Menu items: `Settings` (opens Settings dialog), `Refresh library`, separator, `Unpack logs`, `About SoftLAN`. This is the only "app-level" control left in the top bar; the game-folder picker has moved into Settings.
|
- **Kebab menu** (`⋮`, pinned far-right) — 36×36 button with same surface as search. Menu items: `Settings` (opens Settings dialog), `Refresh library`, separator, `Unpack logs`, `About SoftLAN`. This is the only "app-level" control left in the top bar; the game-folder picker has moved into Settings.
|
||||||
|
- **Call to Play button** (`.ctp-btn`, sits just left of the kebab in the far-right sub-group) — 36 px pill, flag icon + `Call to Play` label. Carries an accent-filled **badge** with the count of active (non-started) calls. Opens the Call to Play overlay. See the **"Call to Play"** section for the full feature. In variant B it lives in row 1's right group, between the storage meter and the kebab.
|
||||||
|
|
||||||
**Narrow-window fallback** (container width < 1100 px): the grid is replaced by a single `display: flex; flex-wrap: nowrap; gap: 16px` row. All items align left-to-right in source order (brand → filter → search → sort → kebab). The search field becomes `flex: 1 1 auto` so it absorbs remaining slack. The geometric centering is abandoned at narrow widths because there isn't enough horizontal slack for it to read cleanly. Implement via container query (`@container launcher (max-width: 1100px)`) on the launcher root; a viewport media query is an acceptable fallback if you're not using container queries yet.
|
**Narrow-window fallback** (container width < 1100 px): the grid is replaced by a single `display: flex; flex-wrap: nowrap; gap: 16px` row. All items align left-to-right in source order (brand → filter → search → sort → kebab). The search field becomes `flex: 1 1 auto` so it absorbs remaining slack. The geometric centering is abandoned at narrow widths because there isn't enough horizontal slack for it to read cleanly. Implement via container query (`@container launcher (max-width: 1100px)`) on the launcher root; a viewport media query is an acceptable fallback if you're not using container queries yet.
|
||||||
|
|
||||||
@@ -416,6 +431,144 @@ Dot uses the live accent so it visually ties to the progress fill.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Call to Play
|
||||||
|
|
||||||
|
A coordination layer that sits on top of the library: **rally the people on the
|
||||||
|
LAN around a specific game at a specific time.** It answers the actual social
|
||||||
|
question at a LAN party ("what are we all playing, and when?") that a plain
|
||||||
|
library grid can't.
|
||||||
|
|
||||||
|
Source: `calltoplay.jsx` (the feature) + `ctp-chat.jsx` (per-call chat + shared
|
||||||
|
`ctpAvatarColor` / `ctpFmtClock` helpers). Styles are the `ctp-*` classes in
|
||||||
|
`styles.css`. Backed by the mock LAN **peer roster** `PEERS` in `data.jsx`.
|
||||||
|
|
||||||
|
### Two flavors of call
|
||||||
|
|
||||||
|
| | **Play now** | **Scheduled** |
|
||||||
|
|---|---|---|
|
||||||
|
| Set up with | game + max players + **duration** (5/10/15/30/60 min) | game + max players + **clock time** (24h, + which day) |
|
||||||
|
| Others respond | `Ready now` or `+N minutes` | `I'm in` (RSVP), then check in later |
|
||||||
|
| Resolves | when the roster fills **or** the timer runs out | at the scheduled time, after a check-in window |
|
||||||
|
| Who starts it | the **caller** decides the actual launch | the **caller**, once people have checked in |
|
||||||
|
|
||||||
|
**Check-in window.** `CHECKIN_LEAD_MS = 15 min`. A scheduled call sits in the
|
||||||
|
`scheduled` phase collecting RSVPs until 15 minutes before its start time, then
|
||||||
|
flips into the `checkin` phase: it lights up everywhere and everyone who said
|
||||||
|
"I'm in" is nudged to answer with the same `Ready now` / `+N minutes` states as
|
||||||
|
a play-now call. A play-now call is effectively "always in its check-in window."
|
||||||
|
|
||||||
|
**Every call carries a small group chat** (see "Per-call chat" below).
|
||||||
|
|
||||||
|
### Three surfaces
|
||||||
|
|
||||||
|
1. **Top-bar button** (`CallToPlayButton` / `.ctp-btn`) — flag icon + label +
|
||||||
|
an accent badge counting active (non-`started`) calls. Opens the overlay.
|
||||||
|
2. **Quick bars** (`CallToPlayTicker` / `.ctp-ticker-stack`) — a persistent
|
||||||
|
stack rendered at the top of the grid area, **one row per active call**.
|
||||||
|
Sorted **ready → starting-soon → the rest**, ties broken by whichever
|
||||||
|
resolves/starts soonest (`TICKER_RANK` = ready 0, soon 1, call/scheduled 2;
|
||||||
|
then by `deadline`). Clicking a row opens the overlay focused on that call.
|
||||||
|
3. **Overlay** (`CallToPlayOverlay`) — a modal (same scrim/panel treatment as
|
||||||
|
the other dialogs) with a header, a **Call a new match** button, the create
|
||||||
|
form, and a list of **nomination cards** (started calls sink to the bottom).
|
||||||
|
|
||||||
|
### Status model
|
||||||
|
|
||||||
|
Two derived values drive everything (`calltoplay.jsx`):
|
||||||
|
|
||||||
|
- `phaseOf(call)` → `'now'` (no `scheduledFor`) · `'scheduled'` (>15 min out) ·
|
||||||
|
`'checkin'` (within the 15-min lead).
|
||||||
|
- `statusOf(call)` (quick-bar/label status) → `'started'` · `'ready'`
|
||||||
|
(`readyCount >= maxPlayers`, or state `done`) · `'soon'` (`deadline - now ≤
|
||||||
|
15 min`) · `'scheduled'` (has a clock time) · `'call'` (a plain play-now call).
|
||||||
|
|
||||||
|
Quick-bar labels + LED colors: **SCHEDULED**, **CALL TO PLAY**, **STARTING
|
||||||
|
SOON**, **READY** (`TICKER_LABEL`), each with its own dot color via
|
||||||
|
`.ctp-ticker-dot[data-status]`.
|
||||||
|
|
||||||
|
**Per-participant ready state:** `ready` (explicitly readied, or their `readyAt`
|
||||||
|
countdown has elapsed) · `in` (RSVP'd to a scheduled call but not checked in
|
||||||
|
yet) · `pending` (checked in with a `+N minutes` buffer, counting down). Shown
|
||||||
|
as compact **ready-bubbles** in the quick bars (`MiniBubbles`: initials +
|
||||||
|
green ring/check when ready, `+Nm` tag when pending, dimmed when just "in") and
|
||||||
|
as larger `AvatarChip`s in the nomination card roster.
|
||||||
|
|
||||||
|
### Nomination card (`NominationCard`)
|
||||||
|
|
||||||
|
Top to bottom:
|
||||||
|
|
||||||
|
1. **Header** — square game cover + title + a sub-line: `Called by <creator> · N/M peers have it installed` (or `Scheduled by <creator> · starts at HH:MM · …`), and a **timer** on the right: a live `M:SS` countdown for play-now/check-in, the **clock time + "in N min"** for a scheduled call, `Ready`, or `Launching…`. Countdown urgency (`data-urgency` high/mid/low) tints it as time runs low.
|
||||||
|
2. **Check-in note** — only in the `checkin` phase: a clock icon + "Starting soon — check-in is open" (or a personalized nudge if you RSVP'd).
|
||||||
|
3. **Progress bar** — time remaining as a fill (accent, → green when done); hidden while a call is still in the far-out `scheduled` phase.
|
||||||
|
4. **Roster** — `readyCount/maxPlayers ready` (scheduled shows `N in · up to M players`; check-in adds `· K not checked in yet`), then avatar chips for each participant plus empty slots up to `maxPlayers`.
|
||||||
|
5. **Actions** — context-dependent on your role (**creator** / **participant** / **outsider**) and phase: `Ready now` + `+5/10/15/30m` buffer buttons, `I'm in` (RSVP), `Start now` / `Add 5 more minutes` (creator once resolved), `Leave` / `Can't make it`, or a status note.
|
||||||
|
6. **Chat** — the collapsible per-call chat panel.
|
||||||
|
7. **Cancel** — creators get a `Cancel this call` link with an inline confirm.
|
||||||
|
|
||||||
|
### Create form (`CreateNominationForm`)
|
||||||
|
|
||||||
|
Game search (typeahead over the catalog) → on pick, max-players defaults to the
|
||||||
|
game's parsed player cap (`parseMaxPlayers`). **When** toggles `Now` vs
|
||||||
|
`Schedule`. `Now` reveals the **Give people** duration chips. `Schedule` reveals
|
||||||
|
a **24-hour time picker** (hour/minute steppers **and** a "Type a time" free-text
|
||||||
|
field accepting `20:00` / `2000` / `9:30`) plus **day chips** (Today + next two
|
||||||
|
days). The confirm button reads `Call it — <game>` or `Schedule it — <game> ·
|
||||||
|
<day> <time>`.
|
||||||
|
|
||||||
|
### Per-call chat (`CtpChat`, `ctp-chat.jsx`)
|
||||||
|
|
||||||
|
Collapsed to a one-line toggle showing an unread badge + a preview of the last
|
||||||
|
message; expands to a compact IRC-style list (colored usernames via
|
||||||
|
`ctpAvatarColor`, `HH:MM` timestamps) + an input. All chat state is local to the
|
||||||
|
card. Usernames are colored deterministically by a hash of the name.
|
||||||
|
|
||||||
|
### Data shape
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Nomination = {
|
||||||
|
id: string;
|
||||||
|
gameId: string;
|
||||||
|
creator: string; // username of the caller
|
||||||
|
maxPlayers: number;
|
||||||
|
createdAt: number; // ms epoch
|
||||||
|
scheduledFor: number | null; // ms epoch clock time; null = play-now
|
||||||
|
deadline: number; // play-now: createdAt + durationMin; scheduled: === scheduledFor
|
||||||
|
participants: Record<string, { // keyed by username
|
||||||
|
status: 'ready' | 'in' | 'pending';
|
||||||
|
joinedAt: number;
|
||||||
|
readyAt?: number; // ms epoch a 'pending' buffer elapses
|
||||||
|
}>;
|
||||||
|
messages: { id: string; from: string; text: string; at: number }[];
|
||||||
|
state: 'open' | 'done' | 'started';
|
||||||
|
startedAt?: number;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The `useNominations({ username, seed })` hook owns the list and exposes
|
||||||
|
`createNomination`, `respond`, `rsvp`, `sendMessage`, `leave`, `cancel`,
|
||||||
|
`startNow`, `addTime`.
|
||||||
|
|
||||||
|
### Mock vs production
|
||||||
|
|
||||||
|
The mock **simulates other people** with a 1-second `setInterval`
|
||||||
|
(`tickNomination`): bots RSVP to scheduled calls, ready-up during check-in, walk
|
||||||
|
in late, and occasionally post a chat line — purely so the demo resolves
|
||||||
|
visibly (bots use second-scale buffers; real people use the minute-scale ones).
|
||||||
|
The mock also seeds a few representative calls on mount (a live call with chat,
|
||||||
|
a fresh call you started, a scheduled call whose check-in window just opened,
|
||||||
|
and one scheduled for later collecting RSVPs).
|
||||||
|
|
||||||
|
**In production, replace the simulation with a real-time LAN transport:** the
|
||||||
|
launcher instances broadcast nominations, responses, RSVPs, and chat messages to
|
||||||
|
each other (whatever the app uses — a small pub/sub over the LAN, a lightweight
|
||||||
|
signaling server, or Tauri-side networking). Swap the guts of `useNominations`
|
||||||
|
(the `setInterval` + local `setNoms`) for your networking layer that pushes the
|
||||||
|
same state shape; the rendering components need nothing else. The `username` that
|
||||||
|
identifies "you" comes from `settings.username` (the Profile setting), not a
|
||||||
|
prop default.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Filter controls — variant B (not used, kept for reference)
|
## Filter controls — variant B (not used, kept for reference)
|
||||||
|
|
||||||
The two-row chrome has a different filter style — **underlined tabs with counts**, like browser tabs:
|
The two-row chrome has a different filter style — **underlined tabs with counts**, like browser tabs:
|
||||||
@@ -570,7 +723,7 @@ Cover art in the design files is **stylized placeholder art** — generated enti
|
|||||||
|
|
||||||
In the production app, the launcher should ideally use real cover-art when available (fetch from IGDB / Steam / local game folder) and fall back to the placeholder generator for games without art. The placeholder generator is in `design_reference/components.jsx → GameCover`.
|
In the production app, the launcher should ideally use real cover-art when available (fetch from IGDB / Steam / local game folder) and fall back to the placeholder generator for games without art. The placeholder generator is in `design_reference/components.jsx → GameCover`.
|
||||||
|
|
||||||
The icon set (search, play, **server**, install, download, folder, kebab, sort, users, close, check, chevron, trash) is in `design_reference/components.jsx → Icon`. They are 12-14px inline SVGs using `currentColor`. Reuse as-is or substitute with the codebase's existing icon library at the same visual weight. The `server` glyph is new in this round — two stacked rounded rectangles with LED dots, used only on the Start Server button.
|
The icon set (search, play, **server**, install, download, folder, kebab, sort, users, close, check, chevron, trash, **flag**, **clock**, **chat**, **send**, **caretUp**, **caretDown**) is in `design_reference/components.jsx → Icon`. They are 12-14px inline SVGs using `currentColor`. Reuse as-is or substitute with the codebase's existing icon library at the same visual weight. The `server` glyph drives the Start Server button; `flag` / `clock` / `chat` / `send` / `caretUp` / `caretDown` are used by Call to Play.
|
||||||
|
|
||||||
Fonts to load:
|
Fonts to load:
|
||||||
```html
|
```html
|
||||||
@@ -587,17 +740,22 @@ Fonts to load:
|
|||||||
design_reference/
|
design_reference/
|
||||||
├── SoftLAN Launcher.html ← entry; wires React + Babel, mounts <App>
|
├── SoftLAN Launcher.html ← entry; wires React + Babel, mounts <App>
|
||||||
├── styles.css ← all visual styles (CSS custom props + components)
|
├── styles.css ← all visual styles (CSS custom props + components)
|
||||||
├── data.jsx ← mock GAMES array + filter/sort helpers + STORAGE mock
|
├── data.jsx ← mock GAMES array + PEERS roster + filter/sort helpers + STORAGE mock
|
||||||
├── components.jsx ← Icon, GameCover, StateChip, ActionButton, GameCard,
|
├── components.jsx ← Icon, GameCover, StateChip, ActionButton, GameCard,
|
||||||
│ SegmentedFilters, UnderlineFilters, SearchField,
|
│ SegmentedFilters, UnderlineFilters, SearchField,
|
||||||
│ SortMenu, StorageMeter, KebabMenu,
|
│ SortMenu, StorageMeter, KebabMenu,
|
||||||
│ GameDetailModal, SettingsDialog (incl. GameFolderField)
|
│ GameDetailModal, SettingsDialog (incl. GameFolderField)
|
||||||
└── launcher.jsx ← <Launcher> component composing chrome + grid + modals
|
├── ctp-chat.jsx ← CtpChat panel + ctpAvatarColor / ctpFmtClock helpers
|
||||||
|
├── calltoplay.jsx ← Call to Play: useNominations hook, quick bars,
|
||||||
|
│ overlay, nomination cards, create form
|
||||||
|
└── launcher.jsx ← <Launcher> component composing chrome + grid +
|
||||||
|
modals + Call to Play
|
||||||
```
|
```
|
||||||
|
|
||||||
To preview the design in a browser:
|
To preview the design in a browser:
|
||||||
1. Open `SoftLAN Launcher.html` in a static-server (e.g. `python -m http.server` from the folder).
|
1. Open `SoftLAN Launcher.html` in a static-server (e.g. `python -m http.server` from the folder).
|
||||||
2. You'll see a design canvas with all variants (A, B, C, D, E, F) side-by-side. Click an artboard's expand button to view it full-screen.
|
2. You'll see a design canvas with all variants side-by-side. Click an artboard's expand button to view it full-screen.
|
||||||
|
- **Call to Play** (top section) — the quick-bar main view, and the same with the overlay open (live call + chat, your call, a check-in nudge, and a scheduled RSVP). Click anything to interact live — create a call, ready up, RSVP, chat.
|
||||||
- **A / B** — chrome variants (A is the chosen direction)
|
- **A / B** — chrome variants (A is the chosen direction)
|
||||||
- **C** — detail overlay for an installed, server-capable game (Counter-Strike 1.6) → shows **Play + Start Server + Uninstall**
|
- **C** — detail overlay for an installed, server-capable game (Counter-Strike 1.6) → shows **Play + Start Server + Uninstall**
|
||||||
- **D** — detail overlay for a downloaded-but-not-installed game (CoD 4) → shows **Install + Delete from disk**
|
- **D** — detail overlay for a downloaded-but-not-installed game (CoD 4) → shows **Install + Delete from disk**
|
||||||
@@ -615,4 +773,5 @@ To preview the design in a browser:
|
|||||||
- **Progress state** — designed. See "Download progress" section above. The action-button slot is swapped for a live `DownloadProgress` component (card + modal variants with container-query fallback for narrow tiles). Wire it to your real progress events; the rendering layer is dev-ready.
|
- **Progress state** — designed. See "Download progress" section above. The action-button slot is swapped for a live `DownloadProgress` component (card + modal variants with container-query fallback for narrow tiles). Wire it to your real progress events; the rendering layer is dev-ready.
|
||||||
- **Keyboard arrow nav** — arrow keys should move focus between cards in the grid; not implemented in the mock but mentioned as a goal.
|
- **Keyboard arrow nav** — arrow keys should move focus between cards in the grid; not implemented in the mock but mentioned as a goal.
|
||||||
- **"Server running" state** — once Start Server actually spawns a process, the button should switch to a *running* state (live indicator dot + "Server running" label + click-to-stop). Not designed this round — flag for follow-up alongside whatever server-status panel the app grows.
|
- **"Server running" state** — once Start Server actually spawns a process, the button should switch to a *running* state (live indicator dot + "Server running" label + click-to-stop). Not designed this round — flag for follow-up alongside whatever server-status panel the app grows.
|
||||||
|
- **Call to Play real-time transport** — the feature is fully designed and interactive, but the mock fakes other players with a local `setInterval` simulation. Production needs a real LAN transport that broadcasts nominations / responses / RSVPs / chat between launcher instances and pushes the same state shape into `useNominations`. Notifications when a call you're in enters its check-in window (OS notification / tray) are also a follow-up.
|
||||||
- **German translations** — the language toggle is wired in Settings, but the catalog of translated UI strings hasn't been compiled. Stand up `react-i18next` (or equivalent) and seed `en.json` from the existing copy; `de.json` is a translation task for whoever owns localization.
|
- **German translations** — the language toggle is wired in Settings, but the catalog of translated UI strings hasn't been compiled. Stand up `react-i18next` (or equivalent) and seed `en.json` from the existing copy; `de.json` is a translation task for whoever owns localization.
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
<script type="text/babel" src="tweaks-panel.jsx"></script>
|
||||||
<script type="text/babel" src="data.jsx"></script>
|
<script type="text/babel" src="data.jsx"></script>
|
||||||
<script type="text/babel" src="components.jsx"></script>
|
<script type="text/babel" src="components.jsx"></script>
|
||||||
|
<script type="text/babel" src="ctp-chat.jsx"></script>
|
||||||
|
<script type="text/babel" src="calltoplay.jsx"></script>
|
||||||
<script type="text/babel" src="launcher.jsx"></script>
|
<script type="text/babel" src="launcher.jsx"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -32,7 +34,7 @@
|
|||||||
const { useState } = React;
|
const { useState } = React;
|
||||||
|
|
||||||
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
|
||||||
"accent": "#3b82f6",
|
"accent": "#a855f7",
|
||||||
"density": "normal",
|
"density": "normal",
|
||||||
"aspect": "square",
|
"aspect": "square",
|
||||||
"bg": "gradient",
|
"bg": "gradient",
|
||||||
@@ -47,9 +49,77 @@ function App() {
|
|||||||
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
|
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
|
||||||
const heroGame = GAMES.find(g => g.id === 'cs'); // installed + canHostServer → shows Play + Start Server + Uninstall
|
const heroGame = GAMES.find(g => g.id === 'cs'); // installed + canHostServer → shows Play + Start Server + Uninstall
|
||||||
|
|
||||||
|
// Seed data for the "Call to Play" showcase — a live call with chat, a
|
||||||
|
// fresh call started by you, a SCHEDULED call whose 15-minute check-in
|
||||||
|
// window just opened (highlighted, you still have to ready up), and one
|
||||||
|
// scheduled for later tonight that's still collecting "I'm in" RSVPs.
|
||||||
|
const ctpSeeds = React.useMemo(() => {
|
||||||
|
const now = Date.now();
|
||||||
|
const me = (t.username || 'you').trim() || 'you';
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
gameId: 'cs', creator: 'shadowfox', maxPlayers: 10, ageSec: 95, durationMin: 25,
|
||||||
|
participants: {
|
||||||
|
pixelqueen: { status: 'ready', joinedAt: now - 60000 },
|
||||||
|
kbmaster: { status: 'ready', joinedAt: now - 40000 },
|
||||||
|
nullptr: { status: 'pending', joinedAt: now - 20000, readyAt: now + 50000 },
|
||||||
|
},
|
||||||
|
messages: [
|
||||||
|
{ from: 'shadowfox', text: 'dust2, default rules — who is in?', agoSec: 90 },
|
||||||
|
{ from: 'kbmaster', text: 'in. someone seed the map pack for nullptr', agoSec: 60 },
|
||||||
|
{ from: 'nullptr', text: 'downloading, 2 min', agoSec: 35 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameId: 'l4d2', creator: me, maxPlayers: 4, ageSec: 15, durationMin: 6,
|
||||||
|
participants: {
|
||||||
|
afk_andy: { status: 'pending', joinedAt: now - 5000, readyAt: now + 35000 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameId: 'tf2', creator: 'wavedash', maxPlayers: 12, ageSec: 3600, scheduledInMin: 12,
|
||||||
|
participants: {
|
||||||
|
[me]: { status: 'in', joinedAt: now - 3000000 },
|
||||||
|
gg_val: { status: 'ready', joinedAt: now - 2000000 },
|
||||||
|
crit_happens: { status: 'in', joinedAt: now - 1500000 },
|
||||||
|
lowping_larry: { status: 'pending', joinedAt: now - 1000000, readyAt: now + 240000 },
|
||||||
|
},
|
||||||
|
messages: [
|
||||||
|
{ from: 'wavedash', text: 'payload maps tonight — check in when you are at your seat', agoSec: 300 },
|
||||||
|
{ from: 'gg_val', text: 'ready, lobby is open', agoSec: 120 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gameId: 'amongus', creator: 'pixelqueen', maxPlayers: 10, ageSec: 5400, scheduledInMin: 128,
|
||||||
|
participants: {
|
||||||
|
ctrlaltdave: { status: 'in', joinedAt: now - 4000000 },
|
||||||
|
afk_andy: { status: 'in', joinedAt: now - 3000000 },
|
||||||
|
},
|
||||||
|
messages: [
|
||||||
|
{ from: 'pixelqueen', text: 'right after the pizza break — bring headphones', agoSec: 4800 },
|
||||||
|
{ from: 'ctrlaltdave', text: 'in. can we start 10 min later?', agoSec: 3600 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<DesignCanvas>
|
<DesignCanvas>
|
||||||
|
<DCSection id="call-to-play" title="Call to Play — now, scheduled & chat"
|
||||||
|
subtitle="Anyone can call a match — right now (give people N minutes to ready up) or scheduled for a clock time ('Among Us at 20:00'). Scheduled calls collect 'I'm in' RSVPs; 15 minutes before start the call enters its check-in window and lights up everywhere, nudging everyone who's in to answer Ready now or +N minutes. Each call carries a small group chat. The quick bars lead with the status — SCHEDULED / CALL TO PLAY / STARTING SOON / READY, each with its own LED color — sorted ready → starting soon → the rest (soonest first), with per-player ready-bubbles and the latest chat line inline. Click anything to interact live.">
|
||||||
|
<DCArtboard id="ctp-quickbars" label="Main view — quick bars with ready-bubbles + check-in highlight" width={1340} height={840}>
|
||||||
|
<Launcher variant="single" tweaks={t} setTweak={setTweak}
|
||||||
|
initialFilter="all" initialSort="recent"
|
||||||
|
seedCallToPlay={ctpSeeds}/>
|
||||||
|
</DCArtboard>
|
||||||
|
<DCArtboard id="ctp-live" label="Overlay — live call, your call, check-in & scheduled RSVP + chat" width={1340} height={840}>
|
||||||
|
<Launcher variant="single" tweaks={t} setTweak={setTweak}
|
||||||
|
initialFilter="all" initialSort="recent"
|
||||||
|
seedCallToPlay={ctpSeeds} initialCtpOpen={true}/>
|
||||||
|
</DCArtboard>
|
||||||
|
</DCSection>
|
||||||
|
|
||||||
<DCSection id="chrome" title="Chrome variations"
|
<DCSection id="chrome" title="Chrome variations"
|
||||||
subtitle="Two ways to organize the top bar — pick whichever density of controls you prefer. Click any card to open the detail overlay, or the kebab menu to open Settings.">
|
subtitle="Two ways to organize the top bar — pick whichever density of controls you prefer. Click any card to open the detail overlay, or the kebab menu to open Settings.">
|
||||||
<DCArtboard id="single-row" label="A · Single-row + segmented filters" width={1340} height={840}>
|
<DCArtboard id="single-row" label="A · Single-row + segmented filters" width={1340} height={840}>
|
||||||
|
|||||||
@@ -0,0 +1,814 @@
|
|||||||
|
// calltoplay.jsx — "Call to Play": rally the LAN around a game + a time.
|
||||||
|
//
|
||||||
|
// Two flavors of call, one feature:
|
||||||
|
// · Play NOW — pick a game, max players, and how long to give people.
|
||||||
|
// Others respond "Ready now" or "+N minutes". Resolves early if the
|
||||||
|
// roster fills, or when the clock runs out — the CALLER starts it.
|
||||||
|
// · SCHEDULED — pick a game and a clock time ("Among Us at 20:00").
|
||||||
|
// People RSVP with "I'm in". 15 minutes before start the call enters
|
||||||
|
// its CHECK-IN window: it gets highlighted everywhere and everyone
|
||||||
|
// who's in is nudged to answer with the same ready / +N-minute states.
|
||||||
|
//
|
||||||
|
// Every call also carries a small group chat (see ctp-chat.jsx).
|
||||||
|
//
|
||||||
|
// Loaded after data.jsx + components.jsx + ctp-chat.jsx, before launcher.jsx.
|
||||||
|
|
||||||
|
const { useState, useEffect, useMemo, useRef, useLayoutEffect } = React;
|
||||||
|
|
||||||
|
const GAMES_ = window.GAMES;
|
||||||
|
const PEERS_ = window.PEERS;
|
||||||
|
const installedPeersFor_ = window.installedPeersFor;
|
||||||
|
const parseMaxPlayers_ = window.parseMaxPlayers;
|
||||||
|
const Icon_ = window.Icon;
|
||||||
|
const GameCover_ = window.GameCover;
|
||||||
|
const CtpChat_ = window.CtpChat;
|
||||||
|
const avatarColor = window.ctpAvatarColor;
|
||||||
|
const fmtClock = window.ctpFmtClock;
|
||||||
|
|
||||||
|
// Check-in opens this long before a scheduled call's start time — and any
|
||||||
|
// call (scheduled or not) counts as "starting soon" inside this window.
|
||||||
|
const CHECKIN_LEAD_MS = 15 * 60000;
|
||||||
|
|
||||||
|
function phaseOf(n, now) {
|
||||||
|
if (!n.scheduledFor) return 'now';
|
||||||
|
return now < n.scheduledFor - CHECKIN_LEAD_MS ? 'scheduled' : 'checkin';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick-bar status: READY > STARTING SOON > CALL TO PLAY / SCHEDULED.
|
||||||
|
function statusOf(n, now) {
|
||||||
|
if (n.state === 'started') return 'started';
|
||||||
|
if (n.state === 'done' || readyCountOf(n, now) >= n.maxPlayers) return 'ready';
|
||||||
|
if (n.deadline - now <= CHECKIN_LEAD_MS) return 'soon';
|
||||||
|
return n.scheduledFor ? 'scheduled' : 'call';
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmtCountdown(ms) {
|
||||||
|
if (ms <= 0) return '0:00';
|
||||||
|
const total = Math.ceil(ms / 1000);
|
||||||
|
const m = Math.floor(total / 60), s = total % 60;
|
||||||
|
return `${m}:${String(s).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
function fmtCountdownShort(ms) {
|
||||||
|
if (ms <= 0) return 'now';
|
||||||
|
const total = Math.ceil(ms / 1000);
|
||||||
|
if (total < 60) return `${total}s`;
|
||||||
|
return `${Math.ceil(total / 60)}m`;
|
||||||
|
}
|
||||||
|
function fmtUntil(ms) {
|
||||||
|
if (ms <= 60000) return 'in <1 min';
|
||||||
|
const min = Math.round(ms / 60000);
|
||||||
|
if (min < 60) return `in ${min} min`;
|
||||||
|
const h = Math.floor(min / 60), m = min % 60;
|
||||||
|
return m ? `in ${h}h ${m}m` : `in ${h}h`;
|
||||||
|
}
|
||||||
|
function isReady(p, now) {
|
||||||
|
if (!p) return false;
|
||||||
|
return p.status === 'ready' || (p.readyAt != null && now >= p.readyAt);
|
||||||
|
}
|
||||||
|
function readyCountOf(n, now) {
|
||||||
|
return Object.values(n.participants).filter(p => isReady(p, now)).length;
|
||||||
|
}
|
||||||
|
function inCountOf(n, now) {
|
||||||
|
return Object.values(n.participants).filter(p => !isReady(p, now) && p.status === 'in').length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Simulation — bots trickle into open calls (RSVP on scheduled ones,
|
||||||
|
// ready-up during check-in / now-calls) and occasionally post a chat
|
||||||
|
// line. Bots use second-scale buffers purely so the demo resolves
|
||||||
|
// visibly; real people would use minute-scale ones.
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
const CTP_BOT_LINES = [
|
||||||
|
'anyone need the game files? I can seed',
|
||||||
|
'gimme 5, grabbing food',
|
||||||
|
'who hosts?',
|
||||||
|
'voice is up — channel 2',
|
||||||
|
'ok, back at my desk',
|
||||||
|
'poke your neighbor, we need one more',
|
||||||
|
'default settings, no mods — right?',
|
||||||
|
'glhf',
|
||||||
|
];
|
||||||
|
|
||||||
|
function maybeBotChat(n, now) {
|
||||||
|
const msgs = n.messages || [];
|
||||||
|
const speakers = Object.keys(n.participants).filter(p => PEERS_.includes(p));
|
||||||
|
if (!speakers.length || msgs.length > 40 || Math.random() >= 0.03) return msgs;
|
||||||
|
const from = speakers[Math.floor(Math.random() * speakers.length)];
|
||||||
|
const text = CTP_BOT_LINES[Math.floor(Math.random() * CTP_BOT_LINES.length)];
|
||||||
|
return [...msgs, { id: `bm${now}${Math.random().toString(36).slice(2, 6)}`, from, text, at: now }];
|
||||||
|
}
|
||||||
|
|
||||||
|
function tickNomination(n, now) {
|
||||||
|
if (n.state !== 'open') return n;
|
||||||
|
const phase = phaseOf(n, now);
|
||||||
|
let participants = n.participants;
|
||||||
|
const joined = Object.keys(participants);
|
||||||
|
const available = PEERS_.filter(p => p !== n.creator && !joined.includes(p));
|
||||||
|
|
||||||
|
if (phase === 'scheduled') {
|
||||||
|
// RSVP trickle — far from start, people just say "I'm in".
|
||||||
|
if (available.length && joined.length < n.maxPlayers && Math.random() < 0.06) {
|
||||||
|
const bot = available[Math.floor(Math.random() * available.length)];
|
||||||
|
participants = { ...participants, [bot]: { status: 'in', joinedAt: now } };
|
||||||
|
}
|
||||||
|
return { ...n, participants, messages: maybeBotChat({ ...n, participants }, now) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check-in window: RSVP'd bots convert to ready / "+N minutes".
|
||||||
|
const inBots = joined.filter(p => PEERS_.includes(p) && participants[p].status === 'in');
|
||||||
|
if (phase === 'checkin' && inBots.length && Math.random() < 0.22) {
|
||||||
|
const bot = inBots[Math.floor(Math.random() * inBots.length)];
|
||||||
|
participants = {
|
||||||
|
...participants,
|
||||||
|
[bot]: Math.random() < 0.6
|
||||||
|
? { ...participants[bot], status: 'ready' }
|
||||||
|
: { ...participants[bot], status: 'pending', readyAt: now + (10 + Math.random() * 70) * 1000 },
|
||||||
|
};
|
||||||
|
} else if (available.length && joined.length < n.maxPlayers && Math.random() < 0.10) {
|
||||||
|
// Walk-ins can still join late.
|
||||||
|
const bot = available[Math.floor(Math.random() * available.length)];
|
||||||
|
const readyNow = Math.random() < 0.55;
|
||||||
|
participants = {
|
||||||
|
...participants,
|
||||||
|
[bot]: readyNow
|
||||||
|
? { status: 'ready', joinedAt: now }
|
||||||
|
: { status: 'pending', joinedAt: now, readyAt: now + (10 + Math.random() * 70) * 1000 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const messages = maybeBotChat({ ...n, participants }, now);
|
||||||
|
const readyCount = readyCountOf({ participants }, now);
|
||||||
|
const state = (readyCount >= n.maxPlayers || now >= n.deadline) ? 'done' : 'open';
|
||||||
|
return { ...n, participants, messages, state };
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSeedNomination({ gameId, creator, maxPlayers, ageSec = 0, durationMin = 10, scheduledInMin = null, participants = {}, messages = [] }) {
|
||||||
|
const now = Date.now() - ageSec * 1000;
|
||||||
|
const scheduledFor = scheduledInMin != null ? Date.now() + scheduledInMin * 60000 : null;
|
||||||
|
return {
|
||||||
|
id: `seed-${gameId}-${creator}`,
|
||||||
|
gameId, creator, maxPlayers,
|
||||||
|
createdAt: now,
|
||||||
|
scheduledFor,
|
||||||
|
deadline: scheduledFor != null ? scheduledFor : now + durationMin * 60000,
|
||||||
|
participants: { [creator]: { status: scheduledFor ? 'in' : 'ready', joinedAt: now }, ...participants },
|
||||||
|
messages: messages.map((m, i) => ({ id: `sm-${gameId}-${i}`, from: m.from, text: m.text, at: Date.now() - (m.agoSec || 0) * 1000 })),
|
||||||
|
state: 'open',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function useNominations({ username, seed = [] }) {
|
||||||
|
const [noms, setNoms] = useState(() => seed.map(makeSeedNomination));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(() => {
|
||||||
|
setNoms(prev => {
|
||||||
|
const now = Date.now();
|
||||||
|
return prev
|
||||||
|
.map(n => tickNomination(n, now))
|
||||||
|
.filter(n => !(n.state === 'started' && now - (n.startedAt || 0) > 2600));
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const createNomination = (gameId, maxPlayers, durationMin, scheduledFor = null) => {
|
||||||
|
const now = Date.now();
|
||||||
|
const nom = {
|
||||||
|
id: `n${now}${Math.random().toString(36).slice(2, 7)}`,
|
||||||
|
gameId, creator: username, maxPlayers,
|
||||||
|
createdAt: now,
|
||||||
|
scheduledFor,
|
||||||
|
deadline: scheduledFor != null ? scheduledFor : now + durationMin * 60000,
|
||||||
|
participants: { [username]: { status: scheduledFor ? 'in' : 'ready', joinedAt: now } },
|
||||||
|
messages: [],
|
||||||
|
state: 'open',
|
||||||
|
};
|
||||||
|
setNoms(prev => [nom, ...prev]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const respond = (id, choice) => {
|
||||||
|
setNoms(prev => prev.map(n => {
|
||||||
|
if (n.id !== id) return n;
|
||||||
|
const now = Date.now();
|
||||||
|
const participants = { ...n.participants };
|
||||||
|
participants[username] = choice === 'ready'
|
||||||
|
? { status: 'ready', joinedAt: (participants[username] || {}).joinedAt || now }
|
||||||
|
: { status: 'pending', joinedAt: (participants[username] || {}).joinedAt || now, readyAt: now + choice * 60000 };
|
||||||
|
return { ...n, participants };
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// RSVP for a scheduled call: "I'm in".
|
||||||
|
const rsvp = (id) => {
|
||||||
|
setNoms(prev => prev.map(n => n.id === id
|
||||||
|
? { ...n, participants: { ...n.participants, [username]: { status: 'in', joinedAt: Date.now() } } }
|
||||||
|
: n));
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendMessage = (id, text) => {
|
||||||
|
const t = String(text || '').trim();
|
||||||
|
if (!t) return;
|
||||||
|
setNoms(prev => prev.map(n => n.id === id
|
||||||
|
? { ...n, messages: [...(n.messages || []), { id: `m${Date.now()}${Math.random().toString(36).slice(2, 6)}`, from: username, text: t, at: Date.now() }] }
|
||||||
|
: n));
|
||||||
|
};
|
||||||
|
|
||||||
|
const leave = (id) => {
|
||||||
|
setNoms(prev => prev.map(n => {
|
||||||
|
if (n.id !== id) return n;
|
||||||
|
const participants = { ...n.participants };
|
||||||
|
delete participants[username];
|
||||||
|
return { ...n, participants };
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel = (id) => setNoms(prev => prev.filter(n => n.id !== id));
|
||||||
|
const startNow = (id) => setNoms(prev => prev.map(n => n.id === id ? { ...n, state: 'started', startedAt: Date.now() } : n));
|
||||||
|
const addTime = (id, minutes = 5) => setNoms(prev => prev.map(n => n.id === id ? { ...n, deadline: Date.now() + minutes * 60000, state: 'open' } : n));
|
||||||
|
|
||||||
|
return { nominations: noms, createNomination, respond, rsvp, sendMessage, leave, cancel, startNow, addTime };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Top-bar entry point
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function CallToPlayButton({ nominations, onClick }) {
|
||||||
|
const active = nominations.filter(n => n.state !== 'started');
|
||||||
|
return (
|
||||||
|
<button className="ctp-btn" onClick={onClick}>
|
||||||
|
<Icon_.flag/>
|
||||||
|
<span>Call to Play</span>
|
||||||
|
{active.length > 0 && <span className="ctp-badge">{active.length}</span>}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Mini ready-bubbles — the per-player state, compressed for the
|
||||||
|
// quick bars: green ring + check = ready, "+Nm" tag = needs minutes,
|
||||||
|
// dimmed = RSVP'd but not checked in yet.
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function MiniBubbles({ nomination: n, now, max = 6 }) {
|
||||||
|
const entries = Object.entries(n.participants);
|
||||||
|
const shown = entries.slice(0, max);
|
||||||
|
return (
|
||||||
|
<span className="ctp-ticker-bubbles">
|
||||||
|
{shown.map(([name, p]) => {
|
||||||
|
const ready = isReady(p, now);
|
||||||
|
const st = ready ? 'ready' : p.status === 'in' ? 'in' : 'pending';
|
||||||
|
const initials = name.replace(/[^a-z0-9]/gi, '').slice(0, 2).toUpperCase();
|
||||||
|
const title = ready ? `${name} — ready`
|
||||||
|
: st === 'in' ? `${name} — in, not checked in yet`
|
||||||
|
: `${name} — ready in ${fmtCountdownShort(p.readyAt - now)}`;
|
||||||
|
return (
|
||||||
|
<span key={name} className="ctp-mini" data-state={st} title={title} style={{ background: avatarColor(name) }}>
|
||||||
|
{initials}
|
||||||
|
{ready && <span className="ctp-mini-check"><Icon_.check/></span>}
|
||||||
|
{st === 'pending' && <i className="ctp-mini-tag">{fmtCountdownShort(p.readyAt - now)}</i>}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{entries.length > max && <span className="ctp-mini ctp-mini-more">+{entries.length - max}</span>}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Persistent quick bars — one row per active call, soonest first.
|
||||||
|
// Scheduled calls show their clock time; once check-in opens the row
|
||||||
|
// lights up to nudge everyone who said "I'm in".
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
const TICKER_LABEL = { scheduled: 'Scheduled', call: 'Call to Play', soon: 'Starting soon', ready: 'Ready' };
|
||||||
|
// Sort rank: ready first, starting-soon next, everything else after —
|
||||||
|
// ties broken by whichever resolves/starts soonest.
|
||||||
|
const TICKER_RANK = { ready: 0, soon: 1, call: 2, scheduled: 2 };
|
||||||
|
|
||||||
|
function CallToPlayTickerRow({ nomination: n, accent, onOpen }) {
|
||||||
|
const [, setTick] = useState(0);
|
||||||
|
useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 1000); return () => clearInterval(id); }, []);
|
||||||
|
const game = GAMES_.find(g => g.id === n.gameId);
|
||||||
|
if (!game) return null;
|
||||||
|
const now = Date.now();
|
||||||
|
const status = statusOf(n, now);
|
||||||
|
const ready = readyCountOf(n, now);
|
||||||
|
const total = Object.keys(n.participants).length;
|
||||||
|
const remaining = Math.max(0, n.deadline - now);
|
||||||
|
const msgs = n.messages || [];
|
||||||
|
const last = msgs[msgs.length - 1];
|
||||||
|
|
||||||
|
const count = status === 'scheduled' ? `${total} in` : `${ready}/${n.maxPlayers} ready`;
|
||||||
|
const time = status === 'ready' ? 'waiting to start'
|
||||||
|
: status === 'scheduled' ? `${fmtClock(n.scheduledFor)} · ${fmtUntil(n.scheduledFor - now)}`
|
||||||
|
: n.scheduledFor ? `starts ${fmtClock(n.scheduledFor)} · ${fmtCountdown(remaining)}`
|
||||||
|
: fmtCountdown(remaining);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button className="ctp-ticker" data-status={status} style={{ '--accent': accent }} onClick={() => onOpen(n.id)}>
|
||||||
|
<span className="ctp-ticker-dot" data-status={status}/>
|
||||||
|
<span className="ctp-ticker-label" data-status={status}>{TICKER_LABEL[status]}</span>
|
||||||
|
<span className="ctp-ticker-game">{game.title}</span>
|
||||||
|
<span className="ctp-ticker-by">by {n.creator}</span>
|
||||||
|
<span className="ctp-ticker-ready">{count}</span>
|
||||||
|
<span className="ctp-ticker-time">{time}</span>
|
||||||
|
{last ? (
|
||||||
|
<span className="ctp-ticker-chat" title={`${last.from}: ${last.text}`}>
|
||||||
|
<Icon_.chat/>
|
||||||
|
<b style={{ color: avatarColor(last.from) }}>{last.from}:</b>
|
||||||
|
<span className="ctp-ticker-chat-text">{last.text}</span>
|
||||||
|
</span>
|
||||||
|
) : <span className="ctp-ticker-chat"/>}
|
||||||
|
<MiniBubbles nomination={n} now={now}/>
|
||||||
|
<span className="ctp-ticker-cta">{status === 'soon' ? 'Check in' : 'View'}<Icon_.chevron/></span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CallToPlayTicker({ nominations, accent, onOpen }) {
|
||||||
|
const now = Date.now();
|
||||||
|
const active = nominations.filter(n => n.state !== 'started')
|
||||||
|
.sort((a, b) => TICKER_RANK[statusOf(a, now)] - TICKER_RANK[statusOf(b, now)] || a.deadline - b.deadline);
|
||||||
|
if (!active.length) return null;
|
||||||
|
return (
|
||||||
|
<div className="ctp-ticker-stack">
|
||||||
|
{active.map(n => <CallToPlayTickerRow key={n.id} nomination={n} accent={accent} onOpen={onOpen}/>)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Avatar chip — ready / counting-down / RSVP'd ("in")
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function AvatarChip({ name, participant, now }) {
|
||||||
|
const ready = isReady(participant, now);
|
||||||
|
const isIn = !ready && participant.status === 'in';
|
||||||
|
const remaining = participant.readyAt ? Math.max(0, participant.readyAt - now) : 0;
|
||||||
|
const initials = name.replace(/[^a-z0-9]/gi, '').slice(0, 2).toUpperCase();
|
||||||
|
const title = ready ? `${name} — ready`
|
||||||
|
: isIn ? `${name} — in, not checked in yet`
|
||||||
|
: `${name} — ready in ${fmtCountdown(remaining)}`;
|
||||||
|
return (
|
||||||
|
<div className={`ctp-avatar ${ready ? 'is-ready' : isIn ? 'is-in' : 'is-pending'}`} title={title}>
|
||||||
|
<span className="ctp-avatar-dot" style={{ background: avatarColor(name) }}>{initials}</span>
|
||||||
|
{ready
|
||||||
|
? <span className="ctp-avatar-check"><Icon_.check/></span>
|
||||||
|
: isIn
|
||||||
|
? <span className="ctp-avatar-in">in</span>
|
||||||
|
: <span className="ctp-avatar-pending">{fmtCountdownShort(remaining)}</span>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Nomination card
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function NominationCard({ nomination: n, username, accent, actions, focused }) {
|
||||||
|
const [, setTick] = useState(0);
|
||||||
|
useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 1000); return () => clearInterval(id); }, []);
|
||||||
|
const [confirmCancel, setConfirmCancel] = useState(false);
|
||||||
|
|
||||||
|
const game = GAMES_.find(g => g.id === n.gameId);
|
||||||
|
if (!game) return null;
|
||||||
|
const now = Date.now();
|
||||||
|
const entries = Object.entries(n.participants);
|
||||||
|
const readyCount = readyCountOf(n, now);
|
||||||
|
const inCount = inCountOf(n, now);
|
||||||
|
const isMe = username in n.participants;
|
||||||
|
const myStatus = n.participants[username];
|
||||||
|
const isCreator = n.creator === username;
|
||||||
|
const isDone = n.state === 'done';
|
||||||
|
const isStarted = n.state === 'started';
|
||||||
|
const phase = phaseOf(n, now);
|
||||||
|
const isSched = phase === 'scheduled' && !isDone && !isStarted;
|
||||||
|
const isCheckin = phase === 'checkin' && !isDone && !isStarted;
|
||||||
|
|
||||||
|
const windowStart = n.scheduledFor ? n.scheduledFor - CHECKIN_LEAD_MS : n.createdAt;
|
||||||
|
const totalMs = Math.max(1, n.deadline - windowStart);
|
||||||
|
const remaining = Math.max(0, n.deadline - now);
|
||||||
|
const pct = Math.max(0, Math.min(100, (remaining / totalMs) * 100));
|
||||||
|
const urgency = pct < 20 ? 'high' : pct < 50 ? 'mid' : 'low';
|
||||||
|
const peerCount = installedPeersFor_(game).length;
|
||||||
|
|
||||||
|
let actionsBody;
|
||||||
|
if (isStarted) {
|
||||||
|
actionsBody = <div className="ctp-note ctp-note-launch">Launching {game.title} for {n.maxPlayers} players…</div>;
|
||||||
|
} else if (isSched) {
|
||||||
|
if (isCreator) {
|
||||||
|
actionsBody = <div className="ctp-note">Scheduled for {fmtClock(n.scheduledFor)} — check-in opens 15 min before start.</div>;
|
||||||
|
} else if (isMe) {
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-me-status">You’re in — we’ll nudge you to check in 15 min before start</div>
|
||||||
|
<button className="ghost-btn ghost-danger" onClick={() => actions.leave(n.id)}>Can’t make it</button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<button className="act-btn act-play" onClick={() => actions.rsvp(n.id)}><Icon_.check/><span>I’m in</span></button>
|
||||||
|
<div className="ctp-note">Check-in opens 15 min before start</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (isCreator) {
|
||||||
|
if (isDone) {
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<button className="act-btn act-play" onClick={() => actions.startNow(n.id)}><Icon_.play/><span>Start now</span></button>
|
||||||
|
<button className="ghost-btn" onClick={() => actions.addTime(n.id, 5)}>Add 5 more minutes</button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else if (myStatus && myStatus.status === 'in') {
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<button className="act-btn act-play" onClick={() => actions.respond(n.id, 'ready')}><Icon_.play/><span>Ready now</span></button>
|
||||||
|
<div className="ctp-buffer-group">
|
||||||
|
{[5, 10, 15].map(min => (
|
||||||
|
<button key={min} className="ctp-buffer-btn" onClick={() => actions.respond(n.id, min)}>+{min}m</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
actionsBody = <div className="ctp-note">Waiting for players to ready up…</div>;
|
||||||
|
}
|
||||||
|
} else if (isDone) {
|
||||||
|
actionsBody = <div className="ctp-note">{readyCount >= n.maxPlayers ? 'Everyone’s ready.' : `It’s time — waiting for ${n.creator} to start.`}</div>;
|
||||||
|
} else if (isMe) {
|
||||||
|
if (myStatus.status === 'in') {
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-me-status">You said you’re in — check in:</div>
|
||||||
|
<button className="act-btn act-play" onClick={() => actions.respond(n.id, 'ready')}><Icon_.play/><span>Ready now</span></button>
|
||||||
|
<div className="ctp-buffer-group">
|
||||||
|
{[5, 10, 15].map(min => (
|
||||||
|
<button key={min} className="ctp-buffer-btn" onClick={() => actions.respond(n.id, min)}>+{min}m</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button className="ghost-btn ghost-danger" onClick={() => actions.leave(n.id)}>Leave</button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const ready = isReady(myStatus, now);
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-me-status">{ready ? 'You’re in — ready' : `You: ready in ${fmtCountdown(myStatus.readyAt - now)}`}</div>
|
||||||
|
{!ready && <button className="ghost-btn" onClick={() => actions.respond(n.id, 'ready')}>I’m ready now</button>}
|
||||||
|
<button className="ghost-btn ghost-danger" onClick={() => actions.leave(n.id)}>Leave</button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
actionsBody = (
|
||||||
|
<React.Fragment>
|
||||||
|
<button className="act-btn act-play" onClick={() => actions.respond(n.id, 'ready')}><Icon_.play/><span>Ready now</span></button>
|
||||||
|
<div className="ctp-buffer-group">
|
||||||
|
{[5, 10, 15, 30].map(min => (
|
||||||
|
<button key={min} className="ctp-buffer-btn" onClick={() => actions.respond(n.id, min)}>+{min}m</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const timerEl = isStarted ? <div className="ctp-card-timer" data-urgency="off">Launching…</div>
|
||||||
|
: isDone ? <div className="ctp-card-timer" data-urgency="off">Ready</div>
|
||||||
|
: isSched ? (
|
||||||
|
<div className="ctp-card-timer is-sched" data-urgency="off">
|
||||||
|
<span className="ctp-card-clock">{fmtClock(n.scheduledFor)}</span>
|
||||||
|
<span className="ctp-card-until">{fmtUntil(n.scheduledFor - now)}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: isCheckin ? (
|
||||||
|
<div className="ctp-card-timer is-sched" data-urgency={urgency}>
|
||||||
|
<span className="ctp-card-clock">{fmtCountdown(remaining)}</span>
|
||||||
|
<span className="ctp-card-until">starts {fmtClock(n.scheduledFor)}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
: <div className="ctp-card-timer" data-urgency={urgency}>{fmtCountdown(remaining)}</div>;
|
||||||
|
|
||||||
|
const subEl = n.scheduledFor
|
||||||
|
? <React.Fragment>Scheduled by <strong>{n.creator}</strong> · starts at {fmtClock(n.scheduledFor)} · {peerCount}/{PEERS_.length} peers have it installed</React.Fragment>
|
||||||
|
: <React.Fragment>Called by <strong>{n.creator}</strong> · {peerCount}/{PEERS_.length} peers have it installed</React.Fragment>;
|
||||||
|
|
||||||
|
const rosterLabel = isSched
|
||||||
|
? `${entries.length} in · up to ${n.maxPlayers} players`
|
||||||
|
: isCheckin && inCount > 0
|
||||||
|
? `${readyCount}/${n.maxPlayers} ready · ${inCount} not checked in yet`
|
||||||
|
: `${readyCount}/${n.maxPlayers} ready`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`ctp-card ${isDone ? 'is-done' : ''} ${isStarted ? 'is-started' : ''} ${isCheckin ? 'is-checkin' : ''} ${focused ? 'is-focused' : ''}`}>
|
||||||
|
<div className="ctp-card-top">
|
||||||
|
<div className="ctp-card-cover"><GameCover_ game={game} aspect="square"/></div>
|
||||||
|
<div className="ctp-card-info">
|
||||||
|
<div className="ctp-card-title">{game.title}</div>
|
||||||
|
<div className="ctp-card-sub">{subEl}</div>
|
||||||
|
</div>
|
||||||
|
{timerEl}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isCheckin && (
|
||||||
|
<div className="ctp-checkin-note">
|
||||||
|
<Icon_.clock/>
|
||||||
|
<span>{isMe && myStatus && myStatus.status === 'in'
|
||||||
|
? 'Starting soon — you said you’re in. Check in below.'
|
||||||
|
: 'Starting soon — check-in is open.'}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSched && (
|
||||||
|
<div className="ctp-progress">
|
||||||
|
<div className="ctp-progress-fill"
|
||||||
|
style={{ width: `${isStarted || isDone ? 100 : pct}%`, background: isDone || isStarted ? 'var(--ok)' : accent }}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="ctp-roster">
|
||||||
|
<div className="ctp-roster-count">{rosterLabel}</div>
|
||||||
|
<div className="ctp-avatars">
|
||||||
|
{entries.map(([name, p]) => <AvatarChip key={name} name={name} participant={p} now={now}/>)}
|
||||||
|
{Array.from({ length: Math.max(0, n.maxPlayers - entries.length) }).map((_, i) => (
|
||||||
|
<div key={`empty${i}`} className="ctp-avatar ctp-avatar-empty"/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ctp-actions">{actionsBody}</div>
|
||||||
|
|
||||||
|
<CtpChat_ nomination={n} username={username} disabled={isStarted}
|
||||||
|
onSend={(text) => actions.sendMessage(n.id, text)}/>
|
||||||
|
|
||||||
|
{isCreator && !isStarted && (
|
||||||
|
confirmCancel ? (
|
||||||
|
<div className="ctp-cancel-confirm">
|
||||||
|
<span>Cancel this call for everyone?</span>
|
||||||
|
<div className="ctp-cancel-confirm-btns">
|
||||||
|
<button className="ghost-btn ghost-danger" onClick={() => actions.cancel(n.id)}>Yes, cancel it</button>
|
||||||
|
<button className="ghost-btn" onClick={() => setConfirmCancel(false)}>No, keep it</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button className="ctp-cancel-link" onClick={() => setConfirmCancel(true)}>
|
||||||
|
<Icon_.trash/><span>Cancel this call</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Create-nomination form — "Now" or a scheduled clock time
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function CreateNominationForm({ accent, onCancel, onCreate }) {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
const [gameId, setGameId] = useState(null);
|
||||||
|
const [maxPlayers, setMaxPlayers] = useState(8);
|
||||||
|
const [duration, setDuration] = useState(10);
|
||||||
|
const [when, setWhen] = useState('now');
|
||||||
|
const [dayOffset, setDayOffset] = useState(0);
|
||||||
|
const nextSlot = () => {
|
||||||
|
const d = new Date(Date.now() + 40 * 60000);
|
||||||
|
d.setMinutes(d.getMinutes() <= 30 ? 30 : 60, 0, 0);
|
||||||
|
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
const [timeStr, setTimeStr] = useState(nextSlot); // committed "HH:MM" (24h)
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [timeDraft, setTimeDraft] = useState(timeStr);
|
||||||
|
const timeInputRef = useRef(null);
|
||||||
|
|
||||||
|
// Select-all when the type-a-time field appears, so the user can overtype
|
||||||
|
// immediately. Done in a layout effect (not onFocus+autoFocus) so it fires
|
||||||
|
// after the browser places the autofocus caret, and re-asserts if the
|
||||||
|
// per-second nominations tick re-renders the overlay.
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (editing && timeInputRef.current) {
|
||||||
|
timeInputRef.current.focus();
|
||||||
|
timeInputRef.current.select();
|
||||||
|
}
|
||||||
|
}, [editing]);
|
||||||
|
|
||||||
|
const [hStr, mStr] = timeStr.split(':');
|
||||||
|
|
||||||
|
// Nudge the time by whole hours or minute-steps, wrapping within 0–23:59.
|
||||||
|
const bump = (field, delta) => {
|
||||||
|
let [h, m] = timeStr.split(':').map(Number);
|
||||||
|
if (field === 'h') h = (h + delta + 24) % 24;
|
||||||
|
else {
|
||||||
|
let total = h * 60 + m + delta;
|
||||||
|
total = ((total % 1440) + 1440) % 1440;
|
||||||
|
h = Math.floor(total / 60); m = total % 60;
|
||||||
|
}
|
||||||
|
setTimeStr(`${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constrain free-text entry: digits + at most one colon, ≤2 digits per side
|
||||||
|
// (≤5 chars with a colon), ≤4 digits without one. Anything else is rejected
|
||||||
|
// (returns the previous draft) — so a colon can't be typed after 3 digits.
|
||||||
|
const sanitizeTime = (raw, prev) => {
|
||||||
|
const v = String(raw).replace(/[^\d:]/g, '');
|
||||||
|
if ((v.match(/:/g) || []).length > 1) return prev;
|
||||||
|
const ci = v.indexOf(':');
|
||||||
|
if (ci !== -1) {
|
||||||
|
if (v.slice(0, ci).length > 2 || v.slice(ci + 1).length > 2) return prev;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return v.length > 4 ? prev : v;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Accepts "20:00", "2000", "20", "9:30" — normalizes or reverts.
|
||||||
|
const commitDraft = () => {
|
||||||
|
const mm = String(timeDraft).trim().match(/^(\d{1,2})(?:[:.\s]?(\d{2}))?$/);
|
||||||
|
if (mm) {
|
||||||
|
const h = Number(mm[1]), min = Number(mm[2] || 0);
|
||||||
|
if (h <= 23 && min <= 59) setTimeStr(`${String(h).padStart(2, '0')}:${String(min).padStart(2, '0')}`);
|
||||||
|
}
|
||||||
|
setEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const DE_DAYS = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'];
|
||||||
|
const dayChips = [0, 1, 2].map(off => {
|
||||||
|
const d = new Date();
|
||||||
|
d.setDate(d.getDate() + off);
|
||||||
|
const label = off === 0 ? 'Today' : DE_DAYS[d.getDay()];
|
||||||
|
return { off, label };
|
||||||
|
});
|
||||||
|
|
||||||
|
const matches = useMemo(() => {
|
||||||
|
if (!query.trim()) return [];
|
||||||
|
const q = query.toLowerCase();
|
||||||
|
return GAMES_.filter(g => g.title.toLowerCase().includes(q)).slice(0, 6);
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
|
const selected = GAMES_.find(g => g.id === gameId);
|
||||||
|
|
||||||
|
const pickGame = (g) => {
|
||||||
|
setGameId(g.id);
|
||||||
|
setMaxPlayers(parseMaxPlayers_(g.players));
|
||||||
|
setQuery(g.title);
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduledTs = () => {
|
||||||
|
const [h, m] = timeStr.split(':').map(Number);
|
||||||
|
const d = new Date();
|
||||||
|
d.setDate(d.getDate() + dayOffset);
|
||||||
|
d.setHours(h || 0, m || 0, 0, 0);
|
||||||
|
if (d.getTime() < Date.now() + 60000) d.setDate(d.getDate() + 1);
|
||||||
|
return d.getTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ctp-create">
|
||||||
|
<div className="ctp-create-row">
|
||||||
|
<label className="ctp-create-label">Game</label>
|
||||||
|
<div className="search ctp-create-search" style={{ '--accent': accent }}>
|
||||||
|
<Icon_.search/>
|
||||||
|
<input type="text" placeholder="Search the catalog…" value={query}
|
||||||
|
onChange={(e) => { setQuery(e.target.value); setGameId(null); }}/>
|
||||||
|
</div>
|
||||||
|
{!selected && query.trim() && (
|
||||||
|
<div className="ctp-create-matches">
|
||||||
|
{matches.map(g => (
|
||||||
|
<button key={g.id} onClick={() => pickGame(g)}>
|
||||||
|
<span>{g.title}</span>
|
||||||
|
<span className="ctp-create-match-meta">{g.players} players</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
{matches.length === 0 && <div className="ctp-create-nomatch">No games match "{query}"</div>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selected && (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-create-row ctp-create-row-inline">
|
||||||
|
<label className="ctp-create-label">Max players</label>
|
||||||
|
<input type="number" className="ctp-create-num" min={2} max={64} value={maxPlayers}
|
||||||
|
onChange={(e) => setMaxPlayers(Math.max(2, Number(e.target.value) || 2))}/>
|
||||||
|
<span className="ctp-create-hint">Suggested for {selected.title}: {parseMaxPlayers_(selected.players)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="ctp-create-row">
|
||||||
|
<label className="ctp-create-label">When</label>
|
||||||
|
<div className="ctp-duration-opts">
|
||||||
|
<button className={`ctp-duration-btn ${when === 'now' ? 'is-active' : ''}`}
|
||||||
|
style={when === 'now' ? { background: accent, borderColor: accent } : undefined}
|
||||||
|
onClick={() => setWhen('now')}>Now</button>
|
||||||
|
<button className={`ctp-duration-btn ${when === 'later' ? 'is-active' : ''}`}
|
||||||
|
style={when === 'later' ? { background: accent, borderColor: accent } : undefined}
|
||||||
|
onClick={() => setWhen('later')}>Schedule</button>
|
||||||
|
</div>
|
||||||
|
{when === 'later' && (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-timepick">
|
||||||
|
{editing ? (
|
||||||
|
<input ref={timeInputRef} type="text" inputMode="numeric" maxLength={5} placeholder="20:00"
|
||||||
|
className="ctp-time-editinput" value={timeDraft}
|
||||||
|
onChange={(e) => setTimeDraft(prev => sanitizeTime(e.target.value, prev))}
|
||||||
|
onBlur={commitDraft}
|
||||||
|
onKeyDown={(e) => { if (e.key === 'Enter') commitDraft(); if (e.key === 'Escape') setEditing(false); }}/>
|
||||||
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-timepick-field">
|
||||||
|
<button type="button" className="ctp-time-step" aria-label="Hour up" onClick={() => bump('h', 1)}><Icon_.caretUp/></button>
|
||||||
|
<span className="ctp-time-cell">{hStr}</span>
|
||||||
|
<button type="button" className="ctp-time-step" aria-label="Hour down" onClick={() => bump('h', -1)}><Icon_.caretDown/></button>
|
||||||
|
</div>
|
||||||
|
<span className="ctp-time-colon">:</span>
|
||||||
|
<div className="ctp-timepick-field">
|
||||||
|
<button type="button" className="ctp-time-step" aria-label="Minute up" onClick={() => bump('m', 15)}><Icon_.caretUp/></button>
|
||||||
|
<span className="ctp-time-cell">{mStr}</span>
|
||||||
|
<button type="button" className="ctp-time-step" aria-label="Minute down" onClick={() => bump('m', -15)}><Icon_.caretDown/></button>
|
||||||
|
</div>
|
||||||
|
<button type="button" className="ctp-time-type"
|
||||||
|
onClick={() => { setTimeDraft(timeStr); setEditing(true); }}>Type a time</button>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="ctp-time-row ctp-day-row">
|
||||||
|
<span className="ctp-day-label">Day</span>
|
||||||
|
{dayChips.map(c => (
|
||||||
|
<button key={c.off} className={`ctp-duration-btn ctp-day-btn ${dayOffset === c.off ? 'is-active' : ''}`}
|
||||||
|
style={dayOffset === c.off ? { background: accent, borderColor: accent } : undefined}
|
||||||
|
onClick={() => setDayOffset(c.off)}>{c.label}</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<span className="ctp-create-hint">24-hour clock. People RSVP with “I’m in” — check-in to ready up opens 15 min before start.</span>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{when === 'now' && (
|
||||||
|
<div className="ctp-create-row">
|
||||||
|
<label className="ctp-create-label">Give people</label>
|
||||||
|
<div className="ctp-duration-opts">
|
||||||
|
{[5, 10, 15, 30, 60].map(min => (
|
||||||
|
<button key={min} className={`ctp-duration-btn ${duration === min ? 'is-active' : ''}`}
|
||||||
|
style={duration === min ? { background: accent, borderColor: accent } : undefined}
|
||||||
|
onClick={() => setDuration(min)}>{min}m</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="ctp-create-foot">
|
||||||
|
<button className="ghost-btn" onClick={onCancel}>Cancel</button>
|
||||||
|
<button className="act-btn act-play"
|
||||||
|
onClick={() => onCreate(selected.id, maxPlayers, duration, when === 'later' ? scheduledTs() : null)}>
|
||||||
|
{when === 'later'
|
||||||
|
? `Schedule it — ${selected.title} · ${dayOffset > 0 ? `${dayChips[dayOffset].label} ` : ''}${timeStr}`
|
||||||
|
: `Call it — ${selected.title}`}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
{!selected && (
|
||||||
|
<div className="ctp-create-foot">
|
||||||
|
<button className="ghost-btn" onClick={onCancel}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Full overlay
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function CallToPlayOverlay({ nominations, username, accent, onClose, actions, focusId }) {
|
||||||
|
const [showCreate, setShowCreate] = useState(false);
|
||||||
|
const sorted = [...nominations].sort((a, b) => (a.state === 'started' ? 1 : 0) - (b.state === 'started' ? 1 : 0) || a.deadline - b.deadline);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal-scrim" onClick={onClose}>
|
||||||
|
<div className="modal ctp-modal" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<button className="modal-close" onClick={onClose} aria-label="Close"><Icon_.close/></button>
|
||||||
|
<div className="ctp-head">
|
||||||
|
<h2>Call to Play</h2>
|
||||||
|
<p className="ctp-head-sub">Rally the LAN around a game and a time — right now, or scheduled for later with an “I’m in” RSVP. The caller decides when it actually starts.</p>
|
||||||
|
{!showCreate && (
|
||||||
|
<button className="act-btn act-play ctp-head-new" onClick={() => setShowCreate(true)}>
|
||||||
|
<Icon_.flag/><span>Call a new match</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="ctp-body">
|
||||||
|
{showCreate && (
|
||||||
|
<CreateNominationForm accent={accent}
|
||||||
|
onCancel={() => setShowCreate(false)}
|
||||||
|
onCreate={(gid, max, dur, sched) => { actions.createNomination(gid, max, dur, sched); setShowCreate(false); }}/>
|
||||||
|
)}
|
||||||
|
{sorted.length === 0 && !showCreate && (
|
||||||
|
<div className="ctp-empty">No active calls right now — be the one to start something.</div>
|
||||||
|
)}
|
||||||
|
{sorted.map(n => (
|
||||||
|
<NominationCard key={n.id} nomination={n} username={username} accent={accent} actions={actions}
|
||||||
|
focused={n.id === focusId}/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(window, {
|
||||||
|
useNominations, makeSeedNomination,
|
||||||
|
CallToPlayButton, CallToPlayTicker, CallToPlayOverlay,
|
||||||
|
NominationCard, CreateNominationForm,
|
||||||
|
});
|
||||||
@@ -20,6 +20,12 @@ const Icon = {
|
|||||||
check: (p) => <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m3 8 3.5 3.5L13 5"/></svg>,
|
check: (p) => <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m3 8 3.5 3.5L13 5"/></svg>,
|
||||||
chevron:(p) => <svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m4 6 4 4 4-4"/></svg>,
|
chevron:(p) => <svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m4 6 4 4 4-4"/></svg>,
|
||||||
trash: (p) => <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 4.5h10"/><path d="M5.5 4.5V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1.5"/><path d="M4.5 4.5 5 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1l.5-8.5"/></svg>,
|
trash: (p) => <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 4.5h10"/><path d="M5.5 4.5V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v1.5"/><path d="M4.5 4.5 5 13a1 1 0 0 0 1 1h4a1 1 0 0 0 1-1l.5-8.5"/></svg>,
|
||||||
|
flag: (p) => <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3.5 1.5v13"/><path d="M3.5 2.25c3-1.3 4.5 1.3 7.5 0v6.7c-3 1.3-4.5-1.3-7.5 0"/></svg>,
|
||||||
|
clock: (p) => <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="8" cy="8" r="6"/><path d="M8 4.5V8l2.5 1.5"/></svg>,
|
||||||
|
chat: (p) => <svg viewBox="0 0 16 16" width="13" height="13" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M2 3.5A1.5 1.5 0 0 1 3.5 2h9A1.5 1.5 0 0 1 14 3.5v6a1.5 1.5 0 0 1-1.5 1.5H6.5L3 14v-3h.5A1.5 1.5 0 0 1 2 9.5z"/></svg>,
|
||||||
|
send: (p) => <svg viewBox="0 0 16 16" width="12" height="12" fill="currentColor" {...p}><path d="M1.5 2 15 8 1.5 14l2.3-6z"/></svg>,
|
||||||
|
caretUp: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m4 10 4-4 4 4"/></svg>,
|
||||||
|
caretDown: (p) => <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m4 6 4 4 4-4"/></svg>,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ────────────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────────────
|
||||||
@@ -217,6 +223,10 @@ function GameCard({ game, accent, aspect, onOpen }) {
|
|||||||
<span>{fmtSize(game.size)}</span>
|
<span>{fmtSize(game.size)}</span>
|
||||||
<span className="card-dot">·</span>
|
<span className="card-dot">·</span>
|
||||||
<span>{game.tags[0]}</span>
|
<span>{game.tags[0]}</span>
|
||||||
|
<span className="card-dot">·</span>
|
||||||
|
<span className="card-peers" title={`${installedPeersFor(game).length} of ${PEERS.length} peers on the LAN have this installed`}>
|
||||||
|
<Icon.users/>{installedPeersFor(game).length}/{PEERS.length}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ActionButton state={game.state} accent={accent} full game={game}/>
|
<ActionButton state={game.state} accent={accent} full game={game}/>
|
||||||
</div>
|
</div>
|
||||||
@@ -293,6 +303,7 @@ const SORTS = [
|
|||||||
{ key: 'size', label: 'Size (largest)' },
|
{ key: 'size', label: 'Size (largest)' },
|
||||||
{ key: 'recent', label: 'Recently Played' },
|
{ key: 'recent', label: 'Recently Played' },
|
||||||
{ key: 'state', label: 'Status' },
|
{ key: 'state', label: 'Status' },
|
||||||
|
{ key: 'peers', label: 'Peers with it (most)' },
|
||||||
];
|
];
|
||||||
|
|
||||||
function SortMenu({ value, onChange, accent }) {
|
function SortMenu({ value, onChange, accent }) {
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// ctp-chat.jsx — shared Call-to-Play helpers (avatar colors, clock formatting)
|
||||||
|
// plus the per-call chat panel. Loaded after components.jsx, before calltoplay.jsx.
|
||||||
|
|
||||||
|
const { useState: useChatState, useEffect: useChatEffect, useRef: useChatRef } = React;
|
||||||
|
|
||||||
|
const CTP_AVATAR_COLORS = ['#60a5fa', '#34d399', '#c084fc', '#fbbf24', '#f472b6', '#38bdf8', '#a3e635', '#fb7185'];
|
||||||
|
const ctpHashName = (s) => [...String(s)].reduce((a, c) => a + c.charCodeAt(0), 0);
|
||||||
|
const ctpAvatarColor = (name) => CTP_AVATAR_COLORS[ctpHashName(name) % CTP_AVATAR_COLORS.length];
|
||||||
|
|
||||||
|
// 24h clock, LAN-party style: "20:00"
|
||||||
|
const ctpFmtClock = (ts) => {
|
||||||
|
const d = new Date(ts);
|
||||||
|
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// Per-call chat — collapsed to a one-line toggle with an unread badge
|
||||||
|
// and a preview of the last message; expands to a compact IRC-style
|
||||||
|
// message list + input. All state is local to the card.
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
function CtpChat({ nomination: n, username, onSend, disabled }) {
|
||||||
|
const IconX = window.Icon;
|
||||||
|
const msgs = n.messages || [];
|
||||||
|
const [open, setOpen] = useChatState(false);
|
||||||
|
const [seen, setSeen] = useChatState(msgs.length);
|
||||||
|
const [draft, setDraft] = useChatState('');
|
||||||
|
const listRef = useChatRef(null);
|
||||||
|
|
||||||
|
useChatEffect(() => { if (open) setSeen(msgs.length); }, [open, msgs.length]);
|
||||||
|
useChatEffect(() => {
|
||||||
|
if (open && listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight;
|
||||||
|
}, [open, msgs.length]);
|
||||||
|
|
||||||
|
const unread = Math.max(0, msgs.length - seen);
|
||||||
|
const last = msgs[msgs.length - 1];
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
const t = draft.trim();
|
||||||
|
if (!t) return;
|
||||||
|
onSend(t);
|
||||||
|
setDraft('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`ctp-chat ${open ? 'is-open' : ''}`}>
|
||||||
|
<button className="ctp-chat-toggle" onClick={() => setOpen(o => !o)}>
|
||||||
|
<IconX.chat/>
|
||||||
|
<span>Chat</span>
|
||||||
|
{msgs.length > 0 && <span className="ctp-chat-count">{msgs.length}</span>}
|
||||||
|
{!open && unread > 0 && <span className="ctp-chat-unread">{unread}</span>}
|
||||||
|
{!open && last && (
|
||||||
|
<span className="ctp-chat-preview">
|
||||||
|
<b style={{ color: ctpAvatarColor(last.from) }}>{last.from}:</b> {last.text}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span className="ctp-chat-chevron"><IconX.chevron/></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{open && (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ctp-chat-list" ref={listRef}>
|
||||||
|
{msgs.length === 0 && <div className="ctp-chat-empty">No messages yet — say hi.</div>}
|
||||||
|
{msgs.map(m => (
|
||||||
|
<div key={m.id} className={`ctp-chat-msg ${m.from === username ? 'is-me' : ''}`}>
|
||||||
|
<b style={{ color: ctpAvatarColor(m.from) }}>{m.from}</b>
|
||||||
|
<span className="ctp-chat-time">{ctpFmtClock(m.at)}</span>
|
||||||
|
<span className="ctp-chat-text"> {m.text}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{!disabled && (
|
||||||
|
<div className="ctp-chat-form">
|
||||||
|
<input type="text" className="ctp-chat-input" placeholder="Message the group…"
|
||||||
|
value={draft}
|
||||||
|
onChange={(e) => setDraft(e.target.value)}
|
||||||
|
onKeyDown={(e) => { if (e.key === 'Enter') submit(); }}/>
|
||||||
|
<button className="ctp-chat-send" onClick={submit} aria-label="Send"><IconX.send/></button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(window, { CtpChat, ctpAvatarColor, ctpFmtClock });
|
||||||
@@ -203,7 +203,37 @@ const STORAGE = {
|
|||||||
total: 512,
|
total: 512,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
// LAN roster (mock) — used by the peer-install indicator and by the
|
||||||
|
// Call to Play feature to simulate other people in the room.
|
||||||
|
// ────────────────────────────────────────────────────────────────────
|
||||||
|
const PEERS = [
|
||||||
|
'shadowfox', 'pixelqueen', 'kbmaster', 'nullptr', 'wavedash',
|
||||||
|
'crit_happens', 'afk_andy', 'lowping_larry', 'gg_val', 'ctrlaltdave',
|
||||||
|
];
|
||||||
|
|
||||||
|
const hashStr = (s) => [...String(s)].reduce((a, c) => a + c.charCodeAt(0), 0);
|
||||||
|
|
||||||
|
// Deterministic-but-varied subset of PEERS who "have this game installed" —
|
||||||
|
// stable per game id so the same game always shows the same roster/count.
|
||||||
|
function installedPeersFor(game) {
|
||||||
|
const h = hashStr(game.id);
|
||||||
|
const count = 1 + (h % PEERS.length); // 1..PEERS.length
|
||||||
|
const scored = PEERS.map((p, i) => ({ p, s: (h * 7 + i * 13 + hashStr(p)) % 97 }));
|
||||||
|
scored.sort((a, b) => a.s - b.s);
|
||||||
|
return scored.slice(0, count).map(x => x.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "2–16" / "1-8" → 16 / 8. Falls back to 8 if unparsable.
|
||||||
|
function parseMaxPlayers(playersStr) {
|
||||||
|
const m = String(playersStr).match(/(\d+)\s*$/);
|
||||||
|
return m ? parseInt(m[1], 10) : 8;
|
||||||
|
}
|
||||||
|
|
||||||
window.GAMES = GAMES;
|
window.GAMES = GAMES;
|
||||||
|
window.PEERS = PEERS;
|
||||||
|
window.installedPeersFor = installedPeersFor;
|
||||||
|
window.parseMaxPlayers = parseMaxPlayers;
|
||||||
window.STATE_META = STATE_META;
|
window.STATE_META = STATE_META;
|
||||||
window.ACTION_FOR_STATE = ACTION_FOR_STATE;
|
window.ACTION_FOR_STATE = ACTION_FOR_STATE;
|
||||||
window.countByFilter = countByFilter;
|
window.countByFilter = countByFilter;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ function applyFilterAndSort(games, filter, sort, query) {
|
|||||||
} else if (sort === 'recent') {
|
} else if (sort === 'recent') {
|
||||||
const order = { installed: 0, local: 1, none: 2 };
|
const order = { installed: 0, local: 1, none: 2 };
|
||||||
g = [...g].sort((a, b) => order[a.state] - order[b.state] || b.version.localeCompare(a.version));
|
g = [...g].sort((a, b) => order[a.state] - order[b.state] || b.version.localeCompare(a.version));
|
||||||
|
} else if (sort === 'peers') {
|
||||||
|
g = [...g].sort((a, b) => installedPeersFor(b).length - installedPeersFor(a).length || a.title.localeCompare(b.title));
|
||||||
}
|
}
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
@@ -25,6 +27,8 @@ function Launcher({
|
|||||||
initialFilter = 'all', initialSort = 'recent', initialQuery = '',
|
initialFilter = 'all', initialSort = 'recent', initialQuery = '',
|
||||||
initialOpenGame = null,
|
initialOpenGame = null,
|
||||||
initialSettingsOpen = false,
|
initialSettingsOpen = false,
|
||||||
|
seedCallToPlay = [],
|
||||||
|
initialCtpOpen = false,
|
||||||
}) {
|
}) {
|
||||||
const { density, aspect, accent, bg } = tweaks;
|
const { density, aspect, accent, bg } = tweaks;
|
||||||
const [filter, setFilter] = useState(initialFilter);
|
const [filter, setFilter] = useState(initialFilter);
|
||||||
@@ -32,6 +36,11 @@ function Launcher({
|
|||||||
const [query, setQuery] = useState(initialQuery);
|
const [query, setQuery] = useState(initialQuery);
|
||||||
const [openGame, setOpenGame] = useState(initialOpenGame);
|
const [openGame, setOpenGame] = useState(initialOpenGame);
|
||||||
const [settingsOpen, setSettingsOpen] = useState(initialSettingsOpen);
|
const [settingsOpen, setSettingsOpen] = useState(initialSettingsOpen);
|
||||||
|
const [ctpOpen, setCtpOpen] = useState(initialCtpOpen);
|
||||||
|
const [ctpFocusId, setCtpFocusId] = useState(null);
|
||||||
|
|
||||||
|
const nomUsername = tweaks.username && tweaks.username.trim() ? tweaks.username.trim() : 'you';
|
||||||
|
const ctp = useNominations({ username: nomUsername, seed: seedCallToPlay });
|
||||||
|
|
||||||
const counts = useMemo(() => countByFilter(GAMES), []);
|
const counts = useMemo(() => countByFilter(GAMES), []);
|
||||||
const list = useMemo(() => applyFilterAndSort(GAMES, filter, sort, query), [filter, sort, query]);
|
const list = useMemo(() => applyFilterAndSort(GAMES, filter, sort, query), [filter, sort, query]);
|
||||||
@@ -66,6 +75,7 @@ function Launcher({
|
|||||||
<SortMenu value={sort} onChange={setSort} accent={accent}/>
|
<SortMenu value={sort} onChange={setSort} accent={accent}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="topbar-right-trail">
|
<div className="topbar-right-trail">
|
||||||
|
<CallToPlayButton nominations={ctp.nominations} onClick={() => setCtpOpen(true)}/>
|
||||||
<KebabMenu items={menuItems}/>
|
<KebabMenu items={menuItems}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,6 +89,7 @@ function Launcher({
|
|||||||
</div>
|
</div>
|
||||||
<div className="topbar-row1-right">
|
<div className="topbar-row1-right">
|
||||||
<StorageMeter accent={accent}/>
|
<StorageMeter accent={accent}/>
|
||||||
|
<CallToPlayButton nominations={ctp.nominations} onClick={() => setCtpOpen(true)}/>
|
||||||
<KebabMenu items={menuItems}/>
|
<KebabMenu items={menuItems}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,6 +104,8 @@ function Launcher({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<main className="grid-wrap">
|
<main className="grid-wrap">
|
||||||
|
<CallToPlayTicker nominations={ctp.nominations} accent={accent}
|
||||||
|
onOpen={(id) => { setCtpFocusId(id); setCtpOpen(true); }}/>
|
||||||
{variant === 'single' && (
|
{variant === 'single' && (
|
||||||
<div className="results-bar">
|
<div className="results-bar">
|
||||||
<div className="results-count">
|
<div className="results-count">
|
||||||
@@ -113,6 +126,12 @@ function Launcher({
|
|||||||
{settingsOpen && setTweak && (
|
{settingsOpen && setTweak && (
|
||||||
<SettingsDialog settings={tweaks} onChange={setTweak} onClose={() => setSettingsOpen(false)}/>
|
<SettingsDialog settings={tweaks} onChange={setTweak} onClose={() => setSettingsOpen(false)}/>
|
||||||
)}
|
)}
|
||||||
|
{ctpOpen && (
|
||||||
|
<CallToPlayOverlay nominations={ctp.nominations} username={nomUsername} accent={accent}
|
||||||
|
focusId={ctpFocusId}
|
||||||
|
onClose={() => { setCtpOpen(false); setCtpFocusId(null); }}
|
||||||
|
actions={{ createNomination: ctp.createNomination, respond: ctp.respond, rsvp: ctp.rsvp, sendMessage: ctp.sendMessage, leave: ctp.leave, cancel: ctp.cancel, startNow: ctp.startNow, addTime: ctp.addTime }}/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -636,8 +636,14 @@
|
|||||||
font-size: 11.5px;
|
font-size: 11.5px;
|
||||||
color: var(--t-3);
|
color: var(--t-3);
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.card-meta .card-dot { opacity: 0.5; }
|
.card-meta .card-dot { opacity: 0.5; flex-shrink: 0; }
|
||||||
|
.card-peers {
|
||||||
|
display: inline-flex; align-items: center; gap: 3px;
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.card-peers svg { flex-shrink: 0; opacity: 0.75; }
|
||||||
|
|
||||||
.density-compact .card-body { padding: 9px 10px 10px; gap: 6px; }
|
.density-compact .card-body { padding: 9px 10px 10px; gap: 6px; }
|
||||||
.density-compact .card-title { font-size: 12.5px; }
|
.density-compact .card-title { font-size: 12.5px; }
|
||||||
@@ -1311,3 +1317,624 @@
|
|||||||
.folder-field.is-unset .folder-field-btn:hover {
|
.folder-field.is-unset .folder-field-btn:hover {
|
||||||
background: var(--accent, #3b82f6);
|
background: var(--accent, #3b82f6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════════════════════════════════════════════════════════════════
|
||||||
|
Call to Play — rally a game + a time
|
||||||
|
═══════════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
/* ─── Top-bar entry button ─── */
|
||||||
|
.ctp-btn {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex; align-items: center; gap: 8px;
|
||||||
|
height: 36px; padding: 0 14px;
|
||||||
|
background: color-mix(in srgb, var(--accent) 14%, var(--bg-2));
|
||||||
|
border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--bd-2));
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--t-1);
|
||||||
|
font: inherit; font-size: 12.5px; font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background .15s, border-color .15s;
|
||||||
|
}
|
||||||
|
.ctp-btn svg { color: var(--accent); flex-shrink: 0; }
|
||||||
|
.ctp-btn:hover {
|
||||||
|
background: color-mix(in srgb, var(--accent) 22%, var(--bg-2));
|
||||||
|
border-color: color-mix(in srgb, var(--accent) 65%, var(--bd-2));
|
||||||
|
}
|
||||||
|
.ctp-badge {
|
||||||
|
display: inline-grid; place-items: center;
|
||||||
|
min-width: 18px; height: 18px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
font-size: 10.5px; font-weight: 800;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Ticker strip ─── */
|
||||||
|
.ctp-ticker-stack {
|
||||||
|
display: flex; flex-direction: column; gap: 8px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.ctp-ticker-stack .ctp-ticker { margin-bottom: 0; }
|
||||||
|
.ctp-ticker {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
10px /* LED */
|
||||||
|
98px /* status */
|
||||||
|
220px /* game */
|
||||||
|
130px /* by */
|
||||||
|
88px /* count */
|
||||||
|
170px /* time */
|
||||||
|
minmax(0, 1fr) /* chat — sole flexible track, absorbs bubbles variance */
|
||||||
|
max-content /* bubbles */
|
||||||
|
82px; /* cta */
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 12px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: color-mix(in srgb, var(--accent) 10%, var(--bg-2));
|
||||||
|
border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--bd-2));
|
||||||
|
border-radius: 10px;
|
||||||
|
color: var(--t-1);
|
||||||
|
font: inherit; font-size: 12.5px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: background .15s, border-color .15s;
|
||||||
|
}
|
||||||
|
.ctp-ticker:hover { background: color-mix(in srgb, var(--accent) 16%, var(--bg-2)); }
|
||||||
|
.ctp-ticker-dot {
|
||||||
|
width: 8px; height: 8px; border-radius: 999px; flex-shrink: 0;
|
||||||
|
background: var(--accent);
|
||||||
|
box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent);
|
||||||
|
animation: ctp-tickerpulse 1.6s ease-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes ctp-tickerpulse {
|
||||||
|
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
|
||||||
|
70% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 0%, transparent); }
|
||||||
|
100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
|
||||||
|
}
|
||||||
|
.ctp-ticker-label {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 10.5px;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.ctp-ticker-game { font-weight: 700; color: var(--t-1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.ctp-ticker-by { color: var(--t-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.ctp-ticker-sep { display: none; }
|
||||||
|
.ctp-ticker-ready, .ctp-ticker-time { color: var(--t-2); font-variant-numeric: tabular-nums; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.ctp-ticker-more {
|
||||||
|
color: var(--t-3);
|
||||||
|
font-size: 11.5px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.ctp-ticker-cta {
|
||||||
|
display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.ctp-ticker-cta svg { transform: rotate(-90deg); }
|
||||||
|
|
||||||
|
/* ─── Overlay modal ─── */
|
||||||
|
.ctp-modal { width: min(720px, 100%); }
|
||||||
|
.ctp-head-row {
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
margin-right: 36px;
|
||||||
|
}
|
||||||
|
.ctp-head-new { height: 40px; padding: 0 18px; font-size: 13px; margin-top: 14px; width: 100%; }
|
||||||
|
.ctp-head { padding: 26px 28px 14px; border-bottom: 1px solid var(--bd-1); }
|
||||||
|
.ctp-head h2 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -0.01em; color: var(--t-1); }
|
||||||
|
.ctp-head-sub { margin: 6px 0 0; font-size: 12.5px; color: var(--t-3); max-width: 52ch; }
|
||||||
|
.ctp-body {
|
||||||
|
padding: 18px 24px 24px;
|
||||||
|
display: flex; flex-direction: column; gap: 14px;
|
||||||
|
max-height: 66vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.ctp-empty {
|
||||||
|
padding: 28px 8px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--t-3);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Nomination card ─── */
|
||||||
|
.ctp-card {
|
||||||
|
display: flex; flex-direction: column; gap: 12px;
|
||||||
|
padding: 14px;
|
||||||
|
background: rgba(255,255,255,0.025);
|
||||||
|
border: 1px solid var(--bd-1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.ctp-card.is-done { border-color: color-mix(in srgb, var(--ok) 45%, var(--bd-2)); }
|
||||||
|
.ctp-card.is-started { opacity: 0.6; }
|
||||||
|
.ctp-card.is-focused { border-color: var(--accent); animation: ctp-cardflash 1.4s ease-out 1; }
|
||||||
|
@keyframes ctp-cardflash {
|
||||||
|
0% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 55%, transparent); }
|
||||||
|
100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
|
||||||
|
}
|
||||||
|
.ctp-card-top { display: flex; align-items: flex-start; gap: 12px; position: relative; }
|
||||||
|
.ctp-card-cover {
|
||||||
|
position: relative;
|
||||||
|
width: 52px; height: 52px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.ctp-card-info { flex: 1; min-width: 0; }
|
||||||
|
.ctp-card-title { font-size: 14.5px; font-weight: 700; color: var(--t-1); }
|
||||||
|
.ctp-card-sub { margin-top: 3px; font-size: 11.5px; color: var(--t-3); }
|
||||||
|
.ctp-card-sub strong { color: var(--t-2); font-weight: 700; }
|
||||||
|
.ctp-card-timer {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--t-1);
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
.ctp-card-timer[data-urgency="mid"] { color: var(--warn); }
|
||||||
|
.ctp-card-timer[data-urgency="high"] { color: var(--danger); }
|
||||||
|
.ctp-card.is-done .ctp-card-timer { color: var(--ok); font-size: 14px; text-transform: uppercase; letter-spacing: 0.04em; }
|
||||||
|
.ctp-cancel-link {
|
||||||
|
display: inline-flex; align-items: center; gap: 6px;
|
||||||
|
align-self: flex-start;
|
||||||
|
margin-top: -2px;
|
||||||
|
padding: 6px 2px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: var(--t-3);
|
||||||
|
font: inherit; font-size: 11.5px; font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color .15s;
|
||||||
|
}
|
||||||
|
.ctp-cancel-link:hover { color: #fca5a5; }
|
||||||
|
.ctp-cancel-confirm {
|
||||||
|
display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: rgba(239,68,68,0.08);
|
||||||
|
border: 1px solid rgba(239,68,68,0.35);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 12.5px;
|
||||||
|
color: #fca5a5;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.ctp-cancel-confirm-btns { display: inline-flex; gap: 8px; flex-shrink: 0; }
|
||||||
|
.ctp-cancel-confirm-btns .ghost-btn { height: 32px; padding: 0 12px; font-size: 12px; }
|
||||||
|
|
||||||
|
.ctp-progress {
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: rgba(255,255,255,0.06);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.ctp-progress-fill { height: 100%; transition: width 1s linear; }
|
||||||
|
|
||||||
|
.ctp-roster { display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
.ctp-roster-count { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--t-3); }
|
||||||
|
.ctp-avatars { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||||
|
.ctp-avatar {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
.ctp-avatar-dot {
|
||||||
|
width: 30px; height: 30px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
color: white;
|
||||||
|
font-size: 10.5px; font-weight: 800;
|
||||||
|
letter-spacing: 0.01em;
|
||||||
|
}
|
||||||
|
.ctp-avatar.is-pending .ctp-avatar-dot { opacity: 0.55; }
|
||||||
|
.ctp-avatar-check {
|
||||||
|
position: absolute; bottom: -2px; right: -2px;
|
||||||
|
width: 14px; height: 14px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--ok);
|
||||||
|
color: #06240f;
|
||||||
|
border: 2px solid var(--bg-2);
|
||||||
|
}
|
||||||
|
.ctp-avatar-pending {
|
||||||
|
position: absolute; bottom: -6px; left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
font-size: 9px; font-weight: 700;
|
||||||
|
color: var(--t-2);
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.ctp-avatar-empty {
|
||||||
|
width: 30px; height: 30px;
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1.5px dashed var(--bd-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctp-actions {
|
||||||
|
display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
.ctp-actions .act-btn { height: 36px; padding: 0 16px; font-size: 12.5px; }
|
||||||
|
.ctp-actions .ghost-btn { height: 36px; padding: 0 14px; font-size: 12.5px; }
|
||||||
|
.ctp-note { font-size: 12.5px; color: var(--t-3); }
|
||||||
|
.ctp-note-launch { color: var(--ok); font-weight: 600; }
|
||||||
|
.ctp-me-status { font-size: 12.5px; font-weight: 600; color: var(--t-2); }
|
||||||
|
.ctp-buffer-group { display: inline-flex; gap: 6px; }
|
||||||
|
.ctp-buffer-btn {
|
||||||
|
height: 36px; padding: 0 11px;
|
||||||
|
background: rgba(255,255,255,0.04);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-1);
|
||||||
|
font: inherit; font-size: 12px; font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background .15s, border-color .15s;
|
||||||
|
}
|
||||||
|
.ctp-buffer-btn:hover { background: rgba(255,255,255,0.08); border-color: var(--bd-3); }
|
||||||
|
|
||||||
|
/* ─── Create-nomination form ─── */
|
||||||
|
.ctp-create-cta {
|
||||||
|
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||||
|
height: 48px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1.5px dashed var(--bd-3);
|
||||||
|
border-radius: 12px;
|
||||||
|
color: var(--t-2);
|
||||||
|
font: inherit; font-size: 13px; font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color .15s, color .15s, background .15s;
|
||||||
|
}
|
||||||
|
.ctp-create-cta:hover { border-color: var(--accent); color: var(--t-1); background: rgba(255,255,255,0.02); }
|
||||||
|
.ctp-create {
|
||||||
|
display: flex; flex-direction: column; gap: 14px;
|
||||||
|
padding: 16px;
|
||||||
|
background: rgba(255,255,255,0.03);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.ctp-create-row { display: flex; flex-direction: column; gap: 6px; position: relative; }
|
||||||
|
.ctp-create-row-inline { flex-direction: row; align-items: center; gap: 10px; flex-wrap: wrap; }
|
||||||
|
.ctp-create-label {
|
||||||
|
font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
|
||||||
|
color: var(--t-3);
|
||||||
|
}
|
||||||
|
.ctp-create-search { width: 100%; }
|
||||||
|
.ctp-create-matches {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
left: 0; right: 0;
|
||||||
|
z-index: 20;
|
||||||
|
max-height: 220px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 4px;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 16px 40px -8px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
.ctp-create-matches button {
|
||||||
|
display: flex; align-items: center; justify-content: space-between; gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 9px 10px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--t-1);
|
||||||
|
font: inherit; font-size: 12.5px; font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ctp-create-matches button:hover { background: rgba(255,255,255,0.06); }
|
||||||
|
.ctp-create-match-meta { color: var(--t-3); font-size: 11px; font-weight: 500; }
|
||||||
|
.ctp-create-nomatch { padding: 10px; font-size: 12px; color: var(--t-3); }
|
||||||
|
.ctp-create-num {
|
||||||
|
width: 70px; height: 34px;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-1);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-1);
|
||||||
|
font: inherit; font-size: 13px; font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.ctp-create-hint { font-size: 11.5px; color: var(--t-3); }
|
||||||
|
.ctp-duration-opts { display: inline-flex; gap: 6px; flex-wrap: wrap; }
|
||||||
|
.ctp-duration-btn {
|
||||||
|
height: 32px; padding: 0 13px;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-1);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-2);
|
||||||
|
font: inherit; font-size: 12.5px; font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background .15s, color .15s, border-color .15s;
|
||||||
|
}
|
||||||
|
.ctp-duration-btn:hover { color: var(--t-1); }
|
||||||
|
.ctp-duration-btn.is-active { color: white; }
|
||||||
|
.ctp-create-foot {
|
||||||
|
display: flex; justify-content: flex-end; gap: 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
.ctp-create-foot .act-btn,
|
||||||
|
.ctp-create-foot .ghost-btn { height: 40px; padding: 0 18px; }
|
||||||
|
.ctp-time-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
|
||||||
|
/* ─── Time stepper ─── */
|
||||||
|
.ctp-timepick {
|
||||||
|
display: flex; align-items: center; gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
.ctp-timepick-field {
|
||||||
|
display: flex; flex-direction: column; align-items: center; gap: 3px;
|
||||||
|
}
|
||||||
|
.ctp-time-step {
|
||||||
|
width: 54px; height: 24px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-1);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-2);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background .12s, color .12s, border-color .12s;
|
||||||
|
}
|
||||||
|
.ctp-time-step:hover { background: var(--bg-4); color: var(--t-1); border-color: var(--bd-3); }
|
||||||
|
.ctp-time-step:active { background: color-mix(in srgb, var(--accent) 30%, var(--bg-3)); }
|
||||||
|
.ctp-time-cell {
|
||||||
|
width: 54px; height: 44px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
border-radius: 9px;
|
||||||
|
font-size: 26px; font-weight: 700;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: var(--t-1);
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
.ctp-time-editinput {
|
||||||
|
width: 132px; height: 44px;
|
||||||
|
padding: 0;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--accent) 70%, var(--bd-2));
|
||||||
|
border-radius: 9px;
|
||||||
|
font: inherit; font-size: 26px; font-weight: 700;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: var(--t-1);
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
}
|
||||||
|
.ctp-time-editinput:focus { outline: none; }
|
||||||
|
.ctp-time-editinput::placeholder { color: var(--t-4); }
|
||||||
|
.ctp-time-colon { font-size: 26px; font-weight: 700; color: var(--t-2); padding-bottom: 2px; }
|
||||||
|
.ctp-time-type {
|
||||||
|
align-self: center;
|
||||||
|
margin-left: 8px;
|
||||||
|
height: 30px; padding: 0 12px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px dashed var(--bd-3);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-3);
|
||||||
|
font: inherit; font-size: 11.5px; font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color .12s, border-color .12s;
|
||||||
|
}
|
||||||
|
.ctp-time-type:hover { color: var(--t-1); border-color: var(--accent); }
|
||||||
|
.ctp-day-row { gap: 5px; }
|
||||||
|
.ctp-day-label {
|
||||||
|
font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
|
||||||
|
color: var(--t-3);
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.ctp-day-btn { height: 26px; padding: 0 10px; font-size: 11px; border-radius: 6px; }
|
||||||
|
|
||||||
|
/* ─── Quick-bar status variants — LED + label + row tint per status:
|
||||||
|
SCHEDULED (neutral) · CALL TO PLAY (accent, pulsing) ·
|
||||||
|
STARTING SOON (amber, glowing) · READY (green, steady) ─── */
|
||||||
|
.ctp-ticker[data-status="scheduled"] {
|
||||||
|
background: var(--bg-2);
|
||||||
|
border-color: var(--bd-2);
|
||||||
|
}
|
||||||
|
.ctp-ticker[data-status="scheduled"]:hover { background: var(--bg-3); }
|
||||||
|
.ctp-ticker[data-status="soon"] {
|
||||||
|
background: color-mix(in srgb, var(--warn) 10%, var(--bg-2));
|
||||||
|
border-color: color-mix(in srgb, var(--warn) 50%, var(--bd-2));
|
||||||
|
animation: ctp-soon-glow 2.4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.ctp-ticker[data-status="soon"]:hover { background: color-mix(in srgb, var(--warn) 16%, var(--bg-2)); }
|
||||||
|
@keyframes ctp-soon-glow {
|
||||||
|
0%, 100% { box-shadow: 0 0 0 0 transparent; }
|
||||||
|
50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 28%, transparent); }
|
||||||
|
}
|
||||||
|
.ctp-ticker[data-status="ready"] {
|
||||||
|
background: color-mix(in srgb, var(--ok) 11%, var(--bg-2));
|
||||||
|
border-color: color-mix(in srgb, var(--ok) 55%, var(--bd-2));
|
||||||
|
box-shadow: 0 0 14px -2px color-mix(in srgb, var(--ok) 35%, transparent);
|
||||||
|
}
|
||||||
|
.ctp-ticker[data-status="ready"]:hover { background: color-mix(in srgb, var(--ok) 17%, var(--bg-2)); }
|
||||||
|
.ctp-ticker-dot[data-status="scheduled"] { background: var(--t-3); animation: none; box-shadow: none; }
|
||||||
|
.ctp-ticker-dot[data-status="soon"] { background: var(--warn); animation: ctp-tickerpulse-warn 1.6s ease-out infinite; }
|
||||||
|
.ctp-ticker-dot[data-status="ready"] { background: var(--ok); animation: none; box-shadow: 0 0 6px var(--ok); }
|
||||||
|
@keyframes ctp-tickerpulse-warn {
|
||||||
|
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--warn) 55%, transparent); }
|
||||||
|
70% { box-shadow: 0 0 0 6px transparent; }
|
||||||
|
100% { box-shadow: 0 0 0 0 transparent; }
|
||||||
|
}
|
||||||
|
.ctp-ticker-label[data-status="scheduled"] { color: var(--t-2); }
|
||||||
|
.ctp-ticker-label[data-status="soon"] { color: var(--warn); }
|
||||||
|
.ctp-ticker-label[data-status="ready"] { color: var(--ok); }
|
||||||
|
.ctp-ticker[data-status="soon"] .ctp-ticker-cta { color: var(--warn); }
|
||||||
|
.ctp-ticker[data-status="ready"] .ctp-ticker-cta { color: var(--ok); }
|
||||||
|
|
||||||
|
/* ─── Quick-bar inline chat preview ─── */
|
||||||
|
.ctp-ticker-chat {
|
||||||
|
min-width: 0;
|
||||||
|
display: inline-flex; align-items: center; gap: 5px;
|
||||||
|
color: var(--t-3);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.ctp-ticker-chat svg { flex-shrink: 0; opacity: 0.7; }
|
||||||
|
.ctp-ticker-chat b { flex-shrink: 0; font-weight: 700; }
|
||||||
|
.ctp-ticker-chat-text {
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
color: var(--t-2);
|
||||||
|
}
|
||||||
|
.ctp-card.is-checkin { border-color: color-mix(in srgb, var(--accent) 70%, var(--bd-2)); }
|
||||||
|
.ctp-checkin-note {
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: color-mix(in srgb, var(--accent) 13%, transparent);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 12.5px; font-weight: 600;
|
||||||
|
color: var(--t-1);
|
||||||
|
}
|
||||||
|
.ctp-checkin-note svg { color: var(--accent); flex-shrink: 0; }
|
||||||
|
.ctp-card-timer.is-sched {
|
||||||
|
display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
|
||||||
|
}
|
||||||
|
.ctp-card-clock { font-size: 18px; line-height: 1; }
|
||||||
|
.ctp-card-until { font-size: 10.5px; font-weight: 600; color: var(--t-3); letter-spacing: 0.01em; }
|
||||||
|
.ctp-avatar.is-in .ctp-avatar-dot { opacity: 0.8; }
|
||||||
|
.ctp-avatar-in {
|
||||||
|
position: absolute; bottom: -6px; left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
font-size: 8.5px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em;
|
||||||
|
color: var(--t-2);
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Ticker mini ready-bubbles ─── */
|
||||||
|
.ctp-ticker-bubbles {
|
||||||
|
display: inline-flex; align-items: center;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
.ctp-mini {
|
||||||
|
position: relative;
|
||||||
|
width: 22px; height: 22px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
margin-left: -6px;
|
||||||
|
border: 2px solid var(--bg-2);
|
||||||
|
color: white;
|
||||||
|
font-size: 8px; font-weight: 800; letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
.ctp-mini:first-child { margin-left: 0; }
|
||||||
|
.ctp-mini[data-state="ready"] { box-shadow: 0 0 0 1.5px var(--ok); }
|
||||||
|
.ctp-mini[data-state="pending"] { opacity: 0.75; }
|
||||||
|
.ctp-mini[data-state="in"] { opacity: 0.85; }
|
||||||
|
.ctp-mini-check {
|
||||||
|
position: absolute; bottom: -3px; right: -4px;
|
||||||
|
width: 11px; height: 11px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--ok);
|
||||||
|
color: #06240f;
|
||||||
|
border: 1.5px solid var(--bg-2);
|
||||||
|
}
|
||||||
|
.ctp-mini-check svg { width: 7px; height: 7px; }
|
||||||
|
.ctp-mini-tag {
|
||||||
|
position: absolute; bottom: -6px; right: -7px;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 8px; font-weight: 700;
|
||||||
|
line-height: 11px;
|
||||||
|
padding: 0 3px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-2);
|
||||||
|
color: var(--t-2);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ctp-mini-more { background: var(--bg-4); color: var(--t-2); font-size: 8.5px; }
|
||||||
|
|
||||||
|
/* ─── Per-call chat ─── */
|
||||||
|
.ctp-chat {
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
border-top: 1px solid var(--bd-1);
|
||||||
|
margin-top: 2px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
.ctp-chat-toggle {
|
||||||
|
display: flex; align-items: center; gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 4px 2px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: var(--t-3);
|
||||||
|
font: inherit; font-size: 11.5px; font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color .15s;
|
||||||
|
}
|
||||||
|
.ctp-chat-toggle:hover { color: var(--t-1); }
|
||||||
|
.ctp-chat-toggle svg { flex-shrink: 0; }
|
||||||
|
.ctp-chat-count { color: var(--t-4); font-weight: 600; }
|
||||||
|
.ctp-chat-unread {
|
||||||
|
min-width: 16px; height: 16px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
padding: 0 4px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
font-size: 9.5px; font-weight: 800;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.ctp-chat-preview {
|
||||||
|
flex: 1; min-width: 0;
|
||||||
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--t-3);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.ctp-chat-preview b { font-weight: 700; }
|
||||||
|
.ctp-chat-chevron { margin-left: auto; flex-shrink: 0; transition: transform .15s; }
|
||||||
|
.ctp-chat.is-open .ctp-chat-chevron { transform: rotate(180deg); }
|
||||||
|
.ctp-chat-list {
|
||||||
|
display: flex; flex-direction: column; gap: 6px;
|
||||||
|
max-height: 168px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: rgba(0,0,0,0.22);
|
||||||
|
border: 1px solid var(--bd-1);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.ctp-chat-msg { font-size: 12px; line-height: 1.45; color: var(--t-2); overflow-wrap: anywhere; }
|
||||||
|
.ctp-chat-msg b { font-weight: 700; }
|
||||||
|
.ctp-chat-text { color: var(--t-1); }
|
||||||
|
.ctp-chat-time { margin-left: 6px; font-size: 10px; color: var(--t-4); font-variant-numeric: tabular-nums; }
|
||||||
|
.ctp-chat-empty { font-size: 11.5px; color: var(--t-4); text-align: center; padding: 6px 0; }
|
||||||
|
.ctp-chat-form { display: flex; gap: 6px; }
|
||||||
|
.ctp-chat-input {
|
||||||
|
flex: 1; height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
background: var(--bg-3);
|
||||||
|
border: 1px solid var(--bd-1);
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-1);
|
||||||
|
font: inherit; font-size: 12px;
|
||||||
|
}
|
||||||
|
.ctp-chat-input::placeholder { color: var(--t-4); }
|
||||||
|
.ctp-chat-input:focus { outline: none; border-color: color-mix(in srgb, var(--accent) 60%, var(--bd-2)); }
|
||||||
|
.ctp-chat-send {
|
||||||
|
width: 32px; height: 32px;
|
||||||
|
display: grid; place-items: center;
|
||||||
|
background: color-mix(in srgb, var(--accent) 24%, var(--bg-3));
|
||||||
|
border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--bd-2));
|
||||||
|
border-radius: 7px;
|
||||||
|
color: var(--t-1);
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background .15s;
|
||||||
|
}
|
||||||
|
.ctp-chat-send:hover { background: color-mix(in srgb, var(--accent) 40%, var(--bg-3)); }
|
||||||
|
|||||||
Reference in New Issue
Block a user