diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..8683001 Binary files /dev/null and b/.DS_Store differ diff --git a/Cargo.lock b/Cargo.lock index 8312374..ab991d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,7 +390,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.1.4", + "rustix", "slab", "windows-sys 0.61.2", ] @@ -432,7 +432,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix 1.1.4", + "rustix", ] [[package]] @@ -458,7 +458,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.1.4", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.61.2", @@ -643,7 +643,7 @@ dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.8.9", "object", "rustc-demangle", "windows-link 0.2.1", @@ -881,9 +881,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" dependencies = [ "bytes", "cfg_aliases", @@ -973,7 +973,7 @@ checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ "bitflags 2.13.1", "polling", - "rustix 1.1.4", + "rustix", "slab", "tracing", ] @@ -985,7 +985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" dependencies = [ "calloop", - "rustix 1.1.4", + "rustix", "wayland-backend", "wayland-client", ] @@ -1029,12 +1029,6 @@ dependencies = [ "shlex 2.0.1", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cexpr" version = "0.6.0" @@ -1078,12 +1072,12 @@ dependencies = [ [[package]] name = "chacha20" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "rand_core 0.10.1", ] @@ -1244,7 +1238,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "gpui_util", "indexmap", @@ -1518,9 +1512,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" dependencies = [ "libc", ] @@ -1710,7 +1704,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "proc-macro2", "quote", @@ -2047,7 +2041,7 @@ dependencies = [ "bit_field", "half", "lebe", - "miniz_oxide", + "miniz_oxide 0.8.9", "num-complex", "pulp", "rayon-core", @@ -2145,12 +2139,13 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.9.1", + "zlib-rs", ] [[package]] @@ -2464,7 +2459,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "rustix 1.1.4", + "rustix", "windows-link 0.2.1", ] @@ -2908,7 +2903,7 @@ dependencies = [ "itoa", "libc", "memmap2", - "rustix 1.1.4", + "rustix", "smallvec", "thiserror 2.0.20", ] @@ -3074,7 +3069,7 @@ dependencies = [ "gix-command", "gix-config-value", "parking_lot", - "rustix 1.1.4", + "rustix", "thiserror 2.0.20", ] @@ -3489,7 +3484,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.2.2" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "accesskit", "anyhow", @@ -3560,7 +3555,7 @@ dependencies = [ [[package]] name = "gpui-base" version = "0.5.2" -source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" +source = "git+https://github.com/longbridge/gpui-component#b4393e22d3a04a880bc187dfdabca9b1eeee270a" dependencies = [ "aho-corasick", "anyhow", @@ -3592,7 +3587,7 @@ dependencies = [ [[package]] name = "gpui-component" version = "0.5.2" -source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" +source = "git+https://github.com/longbridge/gpui-component#b4393e22d3a04a880bc187dfdabca9b1eeee270a" dependencies = [ "anyhow", "chrono", @@ -3672,7 +3667,7 @@ dependencies = [ [[package]] name = "gpui-component-assets" version = "0.5.1" -source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" +source = "git+https://github.com/longbridge/gpui-component#b4393e22d3a04a880bc187dfdabca9b1eeee270a" dependencies = [ "anyhow", "gpui", @@ -3680,13 +3675,13 @@ dependencies = [ "rust-embed", "wasm-bindgen", "wasm-bindgen-futures", - "zed-reqwest", + "zed-reqwest 0.12.15-zed (git+https://github.com/zed-industries/reqwest.git?rev=c15662463bda39148ba154100dd44d3fba5873a4)", ] [[package]] name = "gpui-component-macros" version = "0.5.1" -source = "git+https://github.com/longbridge/gpui-component#ff3eb1128ac1058f1bb88e777744ce1237aa3b79" +source = "git+https://github.com/longbridge/gpui-component#b4393e22d3a04a880bc187dfdabca9b1eeee270a" dependencies = [ "proc-macro2", "quote", @@ -3696,7 +3691,7 @@ dependencies = [ [[package]] name = "gpui_apple" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "block", @@ -3719,7 +3714,7 @@ dependencies = [ [[package]] name = "gpui_linux" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "accesskit", "accesskit_unix", @@ -3765,7 +3760,7 @@ dependencies = [ [[package]] name = "gpui_macos" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "accesskit", "accesskit_macos", @@ -3811,7 +3806,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3822,7 +3817,7 @@ dependencies = [ [[package]] name = "gpui_platform" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "console_error_panic_hook", "gpui", @@ -3835,7 +3830,7 @@ dependencies = [ [[package]] name = "gpui_shared_string" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "schemars", "serde", @@ -3845,7 +3840,7 @@ dependencies = [ [[package]] name = "gpui_util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "log", @@ -3855,7 +3850,7 @@ dependencies = [ [[package]] name = "gpui_web" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "console_error_panic_hook", @@ -3879,7 +3874,7 @@ dependencies = [ [[package]] name = "gpui_wgpu" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "bytemuck", @@ -3905,7 +3900,7 @@ dependencies = [ [[package]] name = "gpui_windows" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "accesskit", "accesskit_windows", @@ -4159,15 +4154,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "html5ever" version = "0.27.0" @@ -4218,7 +4204,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "async-compression", @@ -4238,10 +4224,12 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ + "log", "rustls", - "rustls-platform-verifier 0.5.3", + "rustls-platform-verifier", + "webpki-roots 1.0.9", ] [[package]] @@ -4261,9 +4249,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" dependencies = [ "atomic-waker", "bytes", @@ -4510,15 +4498,15 @@ checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" [[package]] name = "imgref" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" +checksum = "6e44b0a4eaa4c82f441d50a963f2d5f05a787240aeee097597033e72accfd22f" [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -4730,22 +4718,6 @@ dependencies = [ "jiff-tzdb", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - [[package]] name = "jni" version = "0.22.4" @@ -4952,9 +4924,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" +checksum = "d7955dfc218a8afb29dfeffd540e3a6e96baeb94fe7138228dd7cc6937fbbf96" dependencies = [ "libc", ] @@ -4977,12 +4949,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e57c38c1e860fd37c604281cdfb1dd2216977fd76a50f85ba2f388ef3219616" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -5042,9 +5008,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.18.2" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2f2f9b4ba7e6b24d95e7e899329d35be83bcded72c8540cdd5368932d1d90a" +checksum = "0d317b4b9eb398e6acce275758ec6125535505e7a146fb1a9b8bda2451b0ff4c" [[package]] name = "lru-slab" @@ -5214,7 +5180,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "bindgen", @@ -5296,6 +5262,16 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.2" @@ -6252,7 +6228,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "collections", "serde", @@ -6413,7 +6389,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -6426,7 +6402,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.8.9", ] [[package]] @@ -6439,7 +6415,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -6793,7 +6769,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ - "chacha20 0.10.1", + "chacha20 0.10.2", "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -7040,7 +7016,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "derive_refineable", ] @@ -7107,7 +7083,7 @@ dependencies = [ "quinn", "rustls", "rustls-pki-types", - "rustls-platform-verifier 0.7.0", + "rustls-platform-verifier", "sync_wrapper", "tokio", "tokio-rustls", @@ -7123,7 +7099,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "bytes", @@ -7134,7 +7110,7 @@ dependencies = [ "log", "regex", "tokio", - "zed-reqwest", + "zed-reqwest 0.12.15-zed (git+https://github.com/zed-industries/reqwest.git?rev=33bc764aa15ff7b200bf7c93bd96e24878d53e14)", ] [[package]] @@ -7329,19 +7305,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.13.1", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.1.4" @@ -7351,7 +7314,7 @@ dependencies = [ "bitflags 2.13.1", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -7402,27 +7365,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-platform-verifier" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni 0.21.1", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs 0.26.11", - "windows-sys 0.59.0", -] - [[package]] name = "rustls-platform-verifier" version = "0.7.0" @@ -7431,7 +7373,7 @@ checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni 0.22.4", + "jni", "log", "once_cell", "rustls", @@ -7440,7 +7382,7 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs 1.0.9", + "webpki-root-certs", "windows-sys 0.61.2", ] @@ -7535,7 +7477,7 @@ dependencies = [ [[package]] name = "scheduler" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "async-task", "backtrace", @@ -7865,7 +7807,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", - "cpufeatures 0.3.0", + "cpufeatures 0.3.1", "digest 0.11.3", ] @@ -8200,18 +8142,18 @@ dependencies = [ [[package]] name = "strum" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -8228,7 +8170,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "heapless 0.9.3", "log", @@ -8456,6 +8398,17 @@ dependencies = [ "system-configuration-sys", ] +[[package]] +name = "system-configuration" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501336eb7ba9e417300a6a0fa985721065467aa83a6dcf0422a8e43e4c0328fa" +dependencies = [ + "bitflags 2.13.1", + "core-foundation 0.10.1", + "system-configuration-sys", +] + [[package]] name = "system-configuration-sys" version = "0.6.0" @@ -8510,7 +8463,7 @@ dependencies = [ "fastrand", "getrandom 0.4.3", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -9601,7 +9554,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "perf", "quote", @@ -9617,9 +9570,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -9843,7 +9796,7 @@ checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.4", + "rustix", "scoped-tls", "smallvec", "wayland-sys", @@ -9856,7 +9809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ "bitflags 2.13.1", - "rustix 1.1.4", + "rustix", "wayland-backend", "wayland-scanner", ] @@ -9867,7 +9820,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ - "rustix 1.1.4", + "rustix", "wayland-client", "xcursor", ] @@ -9959,7 +9912,7 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "jni 0.22.4", + "jni", "log", "ndk-context", "objc2 0.6.4", @@ -9969,15 +9922,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "webpki-root-certs" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" -dependencies = [ - "webpki-root-certs 1.0.9", -] - [[package]] name = "webpki-root-certs" version = "1.0.9" @@ -10191,14 +10135,11 @@ dependencies = [ [[package]] name = "which" -version = "6.0.3" +version = "8.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +checksum = "bae2f2b2b816647a1cab1acc91f5bd20812d53cb344382635ec2181940c8034f" dependencies = [ - "either", - "home", - "rustix 0.38.44", - "winsafe", + "libc", ] [[package]] @@ -10574,15 +10515,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -10610,21 +10542,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -10685,12 +10602,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -10703,12 +10614,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -10721,12 +10626,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -10751,12 +10650,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -10769,12 +10662,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -10787,12 +10674,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -10805,12 +10686,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -10851,12 +10726,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - [[package]] name = "wio" version = "0.2.2" @@ -10935,7 +10804,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "rustix 1.1.4", + "rustix", "x11rb-protocol", "xcursor", ] @@ -11089,7 +10958,7 @@ dependencies = [ "hex", "libc", "ordered-stream", - "rustix 1.1.4", + "rustix", "serde", "serde_repr", "tracing", @@ -11197,6 +11066,55 @@ dependencies = [ "yeslogic-fontconfig-sys", ] +[[package]] +name = "zed-reqwest" +version = "0.12.15-zed" +source = "git+https://github.com/zed-industries/reqwest.git?rev=33bc764aa15ff7b200bf7c93bd96e24878d53e14#33bc764aa15ff7b200bf7c93bd96e24878d53e14" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "mime_guess", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration 0.8.0", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-util", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry 0.4.0", +] + [[package]] name = "zed-reqwest" version = "0.12.15-zed" @@ -11231,7 +11149,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", + "system-configuration 0.6.1", "tokio", "tokio-rustls", "tokio-socks", @@ -11401,7 +11319,7 @@ checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" [[package]] name = "zlog" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "anyhow", "chrono", @@ -11418,7 +11336,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "ztracing" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" dependencies = [ "tracing", "tracing-subscriber", @@ -11429,7 +11347,7 @@ dependencies = [ [[package]] name = "ztracing_macro" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#fecc3273ed32643c2ea1b04a74c8780e2c9ffaf8" +source = "git+https://github.com/zed-industries/zed#399258feeaf90ad8a3a208c99221ee87b6452f38" [[package]] name = "zune-core" diff --git a/crates/.DS_Store b/crates/.DS_Store new file mode 100644 index 0000000..4f0ecab Binary files /dev/null and b/crates/.DS_Store differ diff --git a/crates/assets/.DS_Store b/crates/assets/.DS_Store new file mode 100644 index 0000000..4f0ecab Binary files /dev/null and b/crates/assets/.DS_Store differ diff --git a/crates/assets/assets/.DS_Store b/crates/assets/assets/.DS_Store new file mode 100644 index 0000000..5db0a1f Binary files /dev/null and b/crates/assets/assets/.DS_Store differ diff --git a/crates/assets/assets/backgrounds/.DS_Store b/crates/assets/assets/backgrounds/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/crates/assets/assets/backgrounds/.DS_Store differ diff --git a/crates/assets/assets/backgrounds/banner1.jpg b/crates/assets/assets/backgrounds/banner1.jpg new file mode 100644 index 0000000..89995e3 Binary files /dev/null and b/crates/assets/assets/backgrounds/banner1.jpg differ diff --git a/crates/assets/assets/backgrounds/banner2.jpg b/crates/assets/assets/backgrounds/banner2.jpg new file mode 100644 index 0000000..2654893 Binary files /dev/null and b/crates/assets/assets/backgrounds/banner2.jpg differ diff --git a/crates/assets/assets/backgrounds/banner3.jpg b/crates/assets/assets/backgrounds/banner3.jpg new file mode 100644 index 0000000..381235e Binary files /dev/null and b/crates/assets/assets/backgrounds/banner3.jpg differ diff --git a/crates/assets/assets/backgrounds/headline.png b/crates/assets/assets/backgrounds/headline.png new file mode 100644 index 0000000..6fe5467 Binary files /dev/null and b/crates/assets/assets/backgrounds/headline.png differ diff --git a/crates/assets/assets/icons/grid.svg b/crates/assets/assets/icons/grid.svg new file mode 100644 index 0000000..ecf1466 --- /dev/null +++ b/crates/assets/assets/icons/grid.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/assets/assets/icons/recent.svg b/crates/assets/assets/icons/recent.svg new file mode 100644 index 0000000..e7c76e6 --- /dev/null +++ b/crates/assets/assets/icons/recent.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/assets/assets/icons/trending.svg b/crates/assets/assets/icons/trending.svg new file mode 100644 index 0000000..314cd1f --- /dev/null +++ b/crates/assets/assets/icons/trending.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/assets/src/lib.rs b/crates/assets/src/lib.rs index e40c0ca..7e6fc8a 100644 --- a/crates/assets/src/lib.rs +++ b/crates/assets/src/lib.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use gpui::{App, AssetSource, Result, SharedString}; +use gpui::{AssetSource, Result, SharedString}; use gpui_component::IconNamed; use rust_embed::RustEmbed; @@ -7,6 +7,8 @@ use rust_embed::RustEmbed; #[folder = "assets"] #[include = "icons/**/*.svg"] #[include = "themes/**/*.json"] +#[include = "backgrounds/**/*.jpg"] +#[include = "backgrounds/**/*.png"] #[exclude = "*.DS_Store"] pub struct Assets; @@ -31,17 +33,12 @@ impl AssetSource for Assets { } impl Assets { - /// Returns the embedded theme files as `(file name, JSON content)` pairs, - /// e.g. `("signed.json", ...)`. The content is a `ThemeSet` that can be - /// loaded into the [`ThemeRegistry`](gpui_component::ThemeRegistry). pub fn themes(&self) -> Vec<(String, String)> { Self::iter() .filter(|path| path.starts_with("themes/")) .filter_map(|path| { let data = Self::get(path.as_ref())?; let name = path.strip_prefix("themes/").unwrap_or(path.as_ref()); - // Debug builds read files from disk (owned), release builds - // embed them in the binary (borrowed). let content = match data.data { std::borrow::Cow::Borrowed(bytes) => { std::str::from_utf8(bytes).ok()?.to_owned() @@ -52,22 +49,6 @@ impl Assets { }) .collect() } - - pub fn load_fonts(&self, cx: &App) -> anyhow::Result<()> { - let font_paths = self.list("fonts")?; - let mut embedded_fonts = Vec::new(); - for font_path in font_paths { - if font_path.ends_with(".ttf") { - let font_bytes = cx - .asset_source() - .load(&font_path)? - .expect("Assets should never return None"); - embedded_fonts.push(font_bytes); - } - } - - cx.text_system().add_fonts(embedded_fonts) - } } pub enum CustomIconName { @@ -91,6 +72,9 @@ pub enum CustomIconName { Tag, Markdown, Share, + Trending, + Recent, + Grid, } impl IconNamed for CustomIconName { @@ -116,6 +100,9 @@ impl IconNamed for CustomIconName { CustomIconName::Tag => "icons/tag.svg", CustomIconName::Markdown => "icons/markdown.svg", CustomIconName::Share => "icons/share.svg", + CustomIconName::Trending => "icons/trending.svg", + CustomIconName::Recent => "icons/recent.svg", + CustomIconName::Grid => "icons/grid.svg", } .into() } diff --git a/crates/signed_git/src/lib.rs b/crates/signed_git/src/lib.rs index 886eb95..e7bef21 100644 --- a/crates/signed_git/src/lib.rs +++ b/crates/signed_git/src/lib.rs @@ -807,7 +807,7 @@ fn tree_diff( /// Parse a `git format-patch` output (a single patch or a patch series) /// into the same [`CommitDiff`] structure used for commit diffs. /// -/// The mbox envelope (From/Subject/… headers, commit body and diffstat) +/// The mbox envelope (From/Subject/... headers, commit body and diffstat) /// is skipped; every `diff --git` section becomes one [`FileDiff`]. Paths /// are taken from the section headers, with git's C-style quoting undone. /// Sections without hunks (pure renames, mode changes, binary files) are @@ -895,7 +895,7 @@ fn name_from_address(from: &str) -> String { } } -/// Strip the `[PATCH]`, `[PATCH 1/2]`, `[RFC PATCH]` … prefix from a patch +/// Strip the `[PATCH]`, `[PATCH 1/2]`, `[RFC PATCH]` ... prefix from a patch /// `Subject:` header. fn strip_patch_prefix(subject: &str) -> String { let trimmed = subject.trim(); @@ -1060,7 +1060,7 @@ fn parse_hunk(lines: &[&str], start: usize) -> Result<(DiffHunk, usize)> { } /// The kind of a hunk body line, from its first character; lines that don't -/// belong to the hunk (headers, `\ No newline…`, the next section) yield +/// belong to the hunk (headers, `\ No newline...`, the next section) yield /// `None`. fn line_prefix_kind(line: &str) -> Option { match line.as_bytes().first()? { diff --git a/crates/signed_state/src/backend.rs b/crates/signed_state/src/backend.rs index 7fee0ef..9036190 100644 --- a/crates/signed_state/src/backend.rs +++ b/crates/signed_state/src/backend.rs @@ -29,11 +29,7 @@ pub const BOOTSTRAP_RELAYS: [&str; 4] = [ ]; /// Relays used for indexing user's relay list (NIP-65). -pub const INDEXER_RELAYS: [&str; 3] = [ - "wss://indexer.coracle.social", - "wss://purplepag.es", - "wss://user.kindpag.es", -]; +pub const INDEXER_RELAYS: [&str; 2] = ["wss://indexer.coracle.social", "wss://user.kindpag.es"]; #[derive(Debug, Clone)] pub enum BackendEvent { diff --git a/crates/signed_state/src/lib.rs b/crates/signed_state/src/lib.rs index 4d1ec70..2e39ab0 100644 --- a/crates/signed_state/src/lib.rs +++ b/crates/signed_state/src/lib.rs @@ -12,7 +12,7 @@ use gpui::{App, AppContext, Entity}; pub use nostr_sdk::prelude::Timestamp; pub use profile::{Profile, ProfileStore}; pub use repo::RepoStore; -pub use repo_list::RepoListStore; +pub use repo_list::{RepoActivityCounts, RepoListStore}; use signed_nostr::new_backend; pub use utils::shorten_pubkey; @@ -37,6 +37,11 @@ pub fn init(db_path: impl AsRef, cx: &mut App) -> Entity { ProfileStore::set_global(cx.new(ProfileStore::new), cx); + // Start the explore list from the local database before the first + // window opens; relay syncs continue in the background, so the list + // never waits for them. + RepoListStore::set_global(cx.new(|cx| RepoListStore::new(None, cx)), cx); + // The clone cache is only meaningful on native platforms; the wasm // build registers an empty store so `GitStore::global` still works. GitStore::set_global(PathBuf::new(), cx); @@ -54,6 +59,11 @@ pub fn init(cx: &mut App) -> Entity { ProfileStore::set_global(cx.new(ProfileStore::new), cx); + // Start the explore list from the local database before the first + // window opens; relay syncs continue in the background, so the list + // never waits for them. + RepoListStore::set_global(cx.new(|cx| RepoListStore::new(None, cx)), cx); + GitStore::set_global(PathBuf::new(), cx); entity diff --git a/crates/signed_state/src/repo_list.rs b/crates/signed_state/src/repo_list.rs index 5c93224..e547c95 100644 --- a/crates/signed_state/src/repo_list.rs +++ b/crates/signed_state/src/repo_list.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use std::time::Duration; use anyhow::Error; -use gpui::{AppContext, Context, Subscription, Task}; +use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task}; use nostr_sdk::prelude::*; use signed_core::{Announcement, Deletions, RepoAddr, filters, repo_addr}; @@ -16,13 +16,46 @@ const REFRESH_DEBOUNCE: Duration = Duration::from_millis(300); /// How far back activity events count toward a repository's last activity. const ACTIVITY_WINDOW: Duration = Duration::from_secs(90 * 86_400); +struct GlobalRepoListStore(Entity); + +impl Global for GlobalRepoListStore {} + +/// Counts of NIP-34 activity events per repository, used to rank the +/// explore list by popularity. Each patch event is a pushed commit (or a +/// small commit series), which is the closest cross-repository proxy for +/// commit count available from event data alone. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct RepoActivityCounts { + /// Root `30611` issue events addressed to the repository. + pub issues: u32, + /// Root `3063` pull request events addressed to the repository + /// (updates to a PR are not new PRs and don't count). + pub pull_requests: u32, + /// `1617` patch events addressed to the repository. + pub commits: u32, +} + +impl RepoActivityCounts { + /// Total issues + pull requests + commits; the popularity ranking key. + pub fn score(self) -> u32 { + self.issues + self.pull_requests + self.commits + } +} + /// Store listing repository announcements (global discovery or per-author). +/// +/// The all-repos store (`author: None`) is created at startup by +/// [`crate::init`] and installed as a global, so the explore panel renders +/// what's in the local database without waiting for relays. pub struct RepoListStore { /// Shared so views can clone the list per frame without a deep copy. pub announcements: Arc>, /// Latest known activity timestamp per repository /// (announcements, state updates, patches, PRs, issues, statuses). pub last_activity: Arc>, + /// Issues + pull requests + commits per repository, for the Popular + /// ranking of the explore list. + pub counts: Arc>, author: Option, refreshing: bool, refresh_dirty: bool, @@ -33,6 +66,16 @@ pub struct RepoListStore { } impl RepoListStore { + /// Retrieve the global explore store (all announcements, created at + /// startup by [`crate::init`]). + pub fn global(cx: &App) -> Entity { + cx.global::().0.clone() + } + + pub(crate) fn set_global(entity: Entity, cx: &mut App) { + cx.set_global(GlobalRepoListStore(entity)); + } + /// Create a store. If `author` is `None`, all announcements are listed. pub fn new(author: Option, cx: &mut Context) -> Self { let backend = Backend::global(cx); @@ -70,6 +113,7 @@ impl RepoListStore { let mut store = Self { announcements: Arc::new(Vec::new()), last_activity: Arc::new(HashMap::new()), + counts: Arc::new(HashMap::new()), author, refreshing: false, refresh_dirty: false, @@ -79,7 +123,9 @@ impl RepoListStore { }; store.subscribe_remote(cx); - store.refresh(cx); + // Query the local database right away; the list never waits for the + // relay syncs started above to finish. + store.refresh_initial(cx); store } @@ -107,6 +153,18 @@ impl RepoListStore { }); } + /// One-shot initial load: query the local database immediately (no + /// debounce), so stored announcements appear as soon as the app opens. + /// Only called from [`Self::new`], before any refresh can be pending. + fn refresh_initial(&mut self, cx: &mut Context) { + debug_assert!(!self.debouncing); + if self.refreshing { + self.refresh_dirty = true; + return; + } + self.run_refresh(cx); + } + /// Re-query the local database. Latest announcement per repository wins. /// /// Debounced: a short delay collapses bursts of requests (e.g. sync @@ -222,11 +280,38 @@ impl RepoListStore { } } - Ok::<_, Error>((announcements, last_activity)) + // Popularity counts per repository (issues, pull requests and + // patches). Unbounded, unlike the windowed activity query + // above, so totals are exact. + let mut counts: HashMap = HashMap::new(); + let count_filter = + Filter::new().kinds([Kind::GitIssue, Kind::GitPullRequest, Kind::GitPatch]); + for event in client.database().query(count_filter).await? { + if deletions.is_deleted(&event) { + continue; + } + for addr in event.tags.coordinates() { + // Skip events for repos we don't list, so the map can't + // grow beyond the number of announcements. + if addr.kind != Kind::GitRepoAnnouncement || !last_activity.contains_key(&addr) + { + continue; + } + let entry = counts.entry(addr).or_default(); + match event.kind { + Kind::GitIssue => entry.issues += 1, + Kind::GitPullRequest => entry.pull_requests += 1, + Kind::GitPatch => entry.commits += 1, + _ => {} + } + } + } + + Ok::<_, Error>((announcements, last_activity, counts)) }); self.tasks.push(cx.spawn(async move |this, cx| { - let (announcements, last_activity) = match work.await { + let (announcements, last_activity, counts) = match work.await { Ok(results) => results, // Database errors are transient; keep the last list. Err(_) => { @@ -239,6 +324,7 @@ impl RepoListStore { let again = this.update(cx, |this, cx| { this.announcements = Arc::new(announcements); this.last_activity = Arc::new(last_activity); + this.counts = Arc::new(counts); cx.notify(); this.refreshing = false; diff --git a/crates/workspace/src/views/repo_detail/browser.rs b/crates/workspace/src/views/repo_detail/browser.rs index 41af8c6..868f91d 100644 --- a/crates/workspace/src/views/repo_detail/browser.rs +++ b/crates/workspace/src/views/repo_detail/browser.rs @@ -134,7 +134,7 @@ impl RepoDetailView { div() .text_sm() .text_color(cx.theme().muted_foreground) - .child("Cloning repository…"), + .child("Cloning repository..."), ) .into_any_element() } else if let Some(error) = error { diff --git a/crates/workspace/src/views/repo_detail/issue_detail.rs b/crates/workspace/src/views/repo_detail/issue_detail.rs index 7eb0977..25ec880 100644 --- a/crates/workspace/src/views/repo_detail/issue_detail.rs +++ b/crates/workspace/src/views/repo_detail/issue_detail.rs @@ -37,7 +37,7 @@ impl IssueDetailView { cx: &mut Context, ) -> Self { let comment_input = - cx.new(|cx| TextareaState::new(window, cx).placeholder("Leave a comment…")); + cx.new(|cx| TextareaState::new(window, cx).placeholder("Leave a comment...")); Self { focus_handle: cx.focus_handle(), diff --git a/crates/workspace/src/views/repo_detail/issues.rs b/crates/workspace/src/views/repo_detail/issues.rs index fda3ec6..8e7d8c9 100644 --- a/crates/workspace/src/views/repo_detail/issues.rs +++ b/crates/workspace/src/views/repo_detail/issues.rs @@ -347,7 +347,7 @@ impl IssuesView { /// through [`RepoStore::open_issue`] when confirmed. pub(super) fn open_new_issue_dialog(store: Entity, window: &mut Window, cx: &mut App) { let subject = cx.new(|cx| InputState::new(window, cx).placeholder("Issue title")); - let content = cx.new(|cx| TextareaState::new(window, cx).placeholder("Describe the issue…")); + let content = cx.new(|cx| TextareaState::new(window, cx).placeholder("Describe the issue...")); window.open_dialog(cx, move |dialog, _window, _cx| { let subject = subject.clone(); diff --git a/crates/workspace/src/views/repo_detail/pull_request_detail.rs b/crates/workspace/src/views/repo_detail/pull_request_detail.rs index 31560fb..0cb57f2 100644 --- a/crates/workspace/src/views/repo_detail/pull_request_detail.rs +++ b/crates/workspace/src/views/repo_detail/pull_request_detail.rs @@ -94,7 +94,7 @@ impl PullRequestDetailView { ) -> Self { let tree_state = cx.new(|cx| TreeState::new(cx)); let comment_input = - cx.new(|cx| TextareaState::new(window, cx).placeholder("Leave a comment…")); + cx.new(|cx| TextareaState::new(window, cx).placeholder("Leave a comment...")); // Same display name as the repo detail panel's title. let repo_name = store diff --git a/crates/workspace/src/views/repo_detail/pull_requests.rs b/crates/workspace/src/views/repo_detail/pull_requests.rs index 02c29d4..acff79b 100644 --- a/crates/workspace/src/views/repo_detail/pull_requests.rs +++ b/crates/workspace/src/views/repo_detail/pull_requests.rs @@ -442,9 +442,9 @@ pub(super) fn open_new_pull_request_dialog( ) { let subject = cx.new(|cx| InputState::new(window, cx).placeholder("Pull request title")); let description = - cx.new(|cx| TextareaState::new(window, cx).placeholder("Describe the change…")); - let patch = - cx.new(|cx| TextareaState::new(window, cx).placeholder("Paste `git format-patch` output…")); + cx.new(|cx| TextareaState::new(window, cx).placeholder("Describe the change...")); + let patch = cx + .new(|cx| TextareaState::new(window, cx).placeholder("Paste `git format-patch` output...")); window.open_dialog(cx, move |dialog, _window, _cx| { let subject = subject.clone(); diff --git a/crates/workspace/src/views/repo_list.rs b/crates/workspace/src/views/repo_list.rs index 6ce8437..5e3dc44 100644 --- a/crates/workspace/src/views/repo_list.rs +++ b/crates/workspace/src/views/repo_list.rs @@ -1,15 +1,19 @@ use std::rc::Rc; +use assets::CustomIconName; use dock::{BasePanel, DockArea, DockPlacement, Panel, PanelEvent, panel_handle}; use gpui::prelude::*; use gpui::{ AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render, SharedString, Size, Subscription, WeakEntity, Window, div, px, size, }; +use gpui_base::Button as BaseButton; use gpui_component::avatar::Avatar; +use gpui_component::input::{Input, InputEvent, InputState}; use gpui_component::scroll::Scrollbar; use gpui_component::{ - ActiveTheme, Sizable, StyledExt, VirtualListScrollHandle, h_flex, v_flex, v_virtual_list, + ActiveTheme, Icon, IconName, Sizable, StyledExt, VirtualListScrollHandle, h_flex, v_flex, + v_virtual_list, }; use signed_core::Announcement; use signed_state::{ProfileStore, RepoListStore, Timestamp}; @@ -18,42 +22,155 @@ use utils::relative_time; use super::RepoDetailView; use crate::image_cache::{MAX_IMAGES, image_cache}; -const CARD_HEIGHT: f32 = 160.; +const COLUMNS: usize = 2; +const CARD_HEIGHT: f32 = 40. + 64. + 48. + 2. + 6.; -/// Browse all announced repositories (works anonymously). +/// How many of the newest repositories the "Recent" sort shows. +const RECENT_COUNT: usize = 10; + +/// Sort of the explore list, chosen via the header's filter buttons. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +enum RepoFilter { + /// Every repository, newest first (the store's default order). + All, + #[default] + /// Repositories ranked by total issues + pull requests + commits. + Popular, + /// The [`RECENT_COUNT`] newest repositories. + Recent, +} + +impl RepoFilter { + /// Indices into the store's `announcements` included by this filter, in + /// display order, narrowed to repositories whose name (or id) contains + /// `query`; an empty query matches everything. + fn visible(self, store: &RepoListStore, query: &str) -> Vec { + let announcements = &store.announcements; + let mut indices: Vec = (0..announcements.len()).collect(); + + // Narrow by the search query first, so "Recent" limits the matches + // and "Popular" ranks them. + let query = query.trim().to_lowercase(); + if !query.is_empty() { + indices.retain(|&ix| { + let announcement = &announcements[ix]; + let name = announcement.name.as_deref().unwrap_or(&announcement.id); + name.to_lowercase().contains(&query) + }); + } + + match self { + Self::All => {} + Self::Recent => indices.truncate(RECENT_COUNT), + Self::Popular => { + let counts = &store.counts; + let scores: Vec = announcements + .iter() + .map(|a| counts.get(&a.addr()).map_or(0, |c| c.score())) + .collect(); + indices.sort_by(|&a, &b| scores[b].cmp(&scores[a])); + } + } + + indices + } + + fn icon_name(self) -> CustomIconName { + match self { + Self::All => CustomIconName::Grid, + Self::Recent => CustomIconName::Recent, + Self::Popular => CustomIconName::Trending, + } + } +} + +/// Browse all announced repositories. pub struct RepoListView { store: Entity, dock_area: WeakEntity, focus_handle: FocusHandle, scroll_handle: VirtualListScrollHandle, + /// Sort selected in the header filter buttons. + filter: RepoFilter, + /// Per-row heights of the virtual list. item_sizes: Rc>>, + /// Number of rows [`Self::item_sizes`] was built for (the filtered repo count). + repo_len: usize, + /// Indices into the store's `announcements` matching [`Self::filter`], + /// in display order; rebuilt when the store changes, the filter is + /// switched, or the search text changes. The virtual list renders this + /// slice. + visible: Vec, + /// Search box filtering repositories by name. + search: Entity, + /// Rebuilds the visible slice as the search text changes. + _search_subscription: Subscription, _subscription: Subscription, } impl RepoListView { pub fn new( dock_area: WeakEntity, - _window: &mut Window, + window: &mut Window, cx: &mut Context, ) -> Self { - let store = cx.new(|cx| RepoListStore::new(None, cx)); + let store = RepoListStore::global(cx); - let subscription = cx.observe(&store, |this, store, cx| { - let count = store.read(cx).announcements.len(); - - if this.item_sizes.len() != count { - this.item_sizes = Rc::new(vec![size(px(0.), px(CARD_HEIGHT)); count]); + // Live search over repository names + let search = cx.new(|cx| InputState::new(window, cx).placeholder("Search...")); + let search_subscription = cx.subscribe(&search, |this, _search, event, cx| { + if matches!(event, InputEvent::Change) { + this.rebuild_rows(cx); } }); - Self { + // Keep the visible slice and row sizes in sync with the store, + // so newly announced repositories appear without waiting for a click. + let subscription = cx.observe(&store, |this, _store, cx| { + this.rebuild_rows(cx); + }); + + let mut this = Self { store, dock_area, focus_handle: cx.focus_handle(), scroll_handle: VirtualListScrollHandle::new(), - item_sizes: Rc::new(vec![]), + filter: RepoFilter::default(), + item_sizes: Rc::new(Vec::new()), + repo_len: 0, + visible: Vec::new(), + search, + _search_subscription: search_subscription, _subscription: subscription, + }; + + // Seed the rows right away; the store may already hold announcements + // (it loaded before the panel opened), and the first render must not + // depend on a later store update. + this.rebuild_rows(cx); + + this + } + + /// Rebuild [`Self::visible`] and [`Self::item_sizes`] from the current + /// store contents, [`Self::filter`] and the search query. Called when + /// the view is created, when the store changes, when the filter is + /// switched, and on every search keystroke, so the list is ready before + /// the next render. + fn rebuild_rows(&mut self, cx: &mut Context) { + let filter = self.filter; + let query = self.search.read(cx).value(); + let store = self.store.read(cx); + self.visible = filter.visible(store, &query); + + // Each virtual list row holds `COLUMNS` repo cards. + let rows = self.visible.len().div_ceil(COLUMNS); + if self.repo_len != rows { + self.repo_len = rows; + self.item_sizes = Rc::new(vec![size(px(0.), px(CARD_HEIGHT)); rows]); } + + cx.notify(); } fn open_repo( @@ -106,14 +223,17 @@ impl RepoListView { v_flex() .id(ix) - .px_4() - .w_full() - .border_b(px(1.)) + .flex_1() + .h_full() + .min_w_0() + .px_3() + .rounded(cx.theme().radius) + .border_1() .border_color(cx.theme().border) .hover(|this| this.bg(cx.theme().list_hover)) .child( h_flex() - .h_12() + .h_10() .text_sm() .font_semibold() .whitespace_nowrap() @@ -123,9 +243,11 @@ impl RepoListView { .child( div() .h_16() - .text_sm() + .min_w_0() + .text_xs() .text_color(cx.theme().muted_foreground) .line_clamp(2) + .text_ellipsis() .child(description), ) .child( @@ -171,6 +293,78 @@ impl RepoListView { })) .into_any_element() } + + fn render_header(&self, count: usize, cx: &mut Context) -> AnyElement { + h_flex() + .px_4() + .py_2() + .w_full() + .gap_3() + .child( + h_flex() + .gap_1() + .text_xs() + .child(div().font_semibold().child("Repositories")) + .child( + div() + .w_10() + .min_w_0() + .truncate() + .text_ellipsis() + .text_color(cx.theme().muted_foreground) + .child(SharedString::from(format!("({count})"))), + ), + ) + .child( + Input::new(&self.search) + .cleanable(true) + .w(px(180.)) + .text_sm() + .border_color(cx.theme().muted) + .bg(cx.theme().muted) + .prefix(Icon::new(IconName::Search).small()), + ) + .child(div().flex_1()) + .child( + h_flex() + .gap_1() + .child(self.filter_button(RepoFilter::All, "All", cx)) + .child(self.filter_button(RepoFilter::Popular, "Popular", cx)) + .child(self.filter_button(RepoFilter::Recent, "Recent", cx)), + ) + .into_any_element() + } + + /// One segmented filter button of the header, styled like the issues + /// list's status filter buttons. + fn filter_button( + &self, + filter: RepoFilter, + label: &'static str, + cx: &mut Context, + ) -> AnyElement { + let active = self.filter == filter; + + BaseButton::new(label) + .flex() + .items_center() + .h_7() + .px_2() + .gap_1() + .child(Icon::new(filter.icon_name())) + .child(div().text_sm().child(label)) + .text_color(cx.theme().button_foreground) + .rounded(cx.theme().radius) + .hover(|this| this.bg(cx.theme().button_hover)) + .active(|this| this.bg(cx.theme().button_active)) + .selected(active) + .when(active, |this| this.bg(cx.theme().button_active)) + .on_click(cx.listener(move |this, _event, _window, cx| { + this.filter = filter; + this.rebuild_rows(cx); + })) + .into_any_element() + } } impl BasePanel for RepoListView { @@ -195,39 +389,28 @@ impl Focusable for RepoListView { impl Render for RepoListView { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let announcements = self.store.read(cx).announcements.clone(); - let last_activity = self.store.read(cx).last_activity.clone(); - let has_announcements = !announcements.is_empty(); - let count = announcements.len(); + let store = self.store.read(cx); + let announcements = store.announcements.clone(); + let last_activity = store.last_activity.clone(); + let count = self.visible.len(); + let has_repos = count > 0; v_flex() .relative() .image_cache(image_cache("repos", MAX_IMAGES)) .size_full() - .child( - h_flex() - .px_4() - .py_2() - .items_center() - .child(div().text_sm().font_semibold().child("Repositories")) - .child( - div() - .text_xs() - .text_color(cx.theme().muted_foreground) - .child(SharedString::from(format!(" ({count})"))), - ), - ) - .when(!has_announcements, |this| { + .child(self.render_header(count, cx)) + .when(!has_repos, |this| { this.child( v_flex().size_full().items_center().justify_center().child( div() .text_sm() .text_color(cx.theme().muted_foreground) - .child("No repositories found. Waiting for relays..."), + .child("No repositories found yet."), ), ) }) - .when(has_announcements, |this| { + .when(has_repos, |this| { let view = cx.entity().clone(); let sizes = self.item_sizes.clone(); @@ -235,10 +418,31 @@ impl Render for RepoListView { v_virtual_list(view, "repos", sizes, move |this, range, _window, cx| { let mut items = vec![]; - for ix in range { - let announcement: &Announcement = &announcements[ix]; - let activity = last_activity.get(&announcement.addr()).copied(); - items.push(this.render_card(ix, announcement, activity, cx)); + for row in range { + let mut row_cards = vec![]; + + for col in 0..COLUMNS { + let Some(&ix) = this.visible.get(row * COLUMNS + col) else { + break; + }; + let Some(announcement) = announcements.get(ix) else { + break; + }; + let activity = last_activity.get(&announcement.addr()).copied(); + row_cards.push(this.render_card(ix, announcement, activity, cx)); + } + + items.push( + h_flex() + .id(row) + .w_full() + .h_full() + .gap_3() + .px_4() + .pt_4() + .children(row_cards) + .into_any_element(), + ); } items diff --git a/crates/workspace/src/views/sidebar/create_repo_dialog.rs b/crates/workspace/src/views/sidebar/create_repo_dialog.rs index 8710219..001c688 100644 --- a/crates/workspace/src/views/sidebar/create_repo_dialog.rs +++ b/crates/workspace/src/views/sidebar/create_repo_dialog.rs @@ -214,7 +214,7 @@ pub fn open(dock_area: WeakEntity, window: &mut Window, cx: &mut App) div() .text_xs() .text_color(cx.theme().muted_foreground) - .child("Loading your grasp servers…"), + .child("Loading your grasp servers..."), ) }, ) diff --git a/crates/workspace/src/views/sidebar/mod.rs b/crates/workspace/src/views/sidebar/mod.rs index 9b7d01a..dad9bac 100644 --- a/crates/workspace/src/views/sidebar/mod.rs +++ b/crates/workspace/src/views/sidebar/mod.rs @@ -1,4 +1,5 @@ use std::ops::Range; +use std::time::{SystemTime, UNIX_EPOCH}; use assets::CustomIconName; use dock::{ @@ -7,9 +8,11 @@ use dock::{ }; use gpui::prelude::*; use gpui::{ - AnyElement, App, ClickEvent, Context, ElementId, Entity, EventEmitter, FocusHandle, Focusable, - Render, SharedString, StyleRefinement, Subscription, WeakEntity, Window, div, px, uniform_list, + AnyElement, App, ClickEvent, Context, Div, ElementId, Entity, EventEmitter, FocusHandle, + Focusable, ObjectFit, Render, SharedString, StyleRefinement, Subscription, WeakEntity, Window, + div, img, px, uniform_list, }; +use gpui_base::Button as BaseButton; use gpui_component::avatar::Avatar; use gpui_component::button::{Button, ButtonVariants}; use gpui_component::input::InputState; @@ -40,6 +43,9 @@ pub struct SidebarPanel { /// Observes the current user's repo store so the list re-renders. my_repos_subscription: Option, logged_in: bool, + /// Banner artwork shown behind the sign-in screen, + /// picked at random from the bundled `backgrounds/` assets. + banner: SharedString, _subscription: Subscription, } @@ -56,6 +62,7 @@ impl SidebarPanel { } BackendEvent::SignerRequired => { this.logged_in = false; + this.banner = pick_banner(); this.my_repos = None; this.my_repos_subscription = None; } @@ -71,6 +78,7 @@ impl SidebarPanel { my_repos: None, my_repos_subscription: None, logged_in, + banner: pick_banner(), _subscription: subscription, }; @@ -287,6 +295,87 @@ impl SidebarPanel { cx, ) } + + /// Sign-in placeholder shown while logged out: the banner artwork fills the + /// panel behind a scrim that ends in a solid black band, keeping the CTA + /// buttons readable on a clean dark surface in both themes. + fn render_sign_in(&self, window: &mut Window, cx: &mut Context) -> Div { + v_flex() + .size_full() + .relative() + .bg(cx.theme().sidebar) + .text_color(cx.theme().sidebar_foreground) + .child(title_bar_drag_handlers( + div() + .id("onboarding-drag") + .absolute() + .h_12() + .w_full() + .top_0() + .left_0(), + window, + cx, + )) + .child( + div().absolute().inset_0().child( + img(self.banner.clone()) + .size_full() + .object_fit(ObjectFit::Cover), + ), + ) + .child( + v_flex() + .size_full() + .justify_end() + .p_4() + .mb_4() + .gap_4() + .child(img("backgrounds/headline.png").max_w_48()) + .child( + v_flex() + .gap_1() + .w_full() + .child( + BaseButton::new("onboarding") + .h_flex() + .h_8() + .px_2() + .bg(cx.theme().primary) + .hover(|this| this.bg(cx.theme().primary_hover)) + .active(|this| this.bg(cx.theme().primary_active)) + .text_color(cx.theme().primary_foreground) + .child(div().text_sm().font_semibold().child("Join now")) + .on_click(cx.listener(|this, _ev, window, cx| { + this.open_onboarding(window, cx) + })), + ) + .child( + BaseButton::new("onboarding") + .h_flex() + .h_8() + .px_2() + .text_color(gpui::white()) + .bg(gpui::white().opacity(0.1)) + .hover(|this| this.bg(gpui::white().opacity(0.2))) + .active(|this| this.bg(gpui::white().opacity(0.4))) + .child(div().text_sm().child("Import identity")) + .on_click(cx.listener(|this, _ev, window, cx| { + this.open_import(window, cx) + })), + ), + ), + ) + } +} + +fn pick_banner() -> SharedString { + let num = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system clock before unix epoch") + .subsec_nanos() + % 3 + + 1; + format!("backgrounds/banner{num}.jpg").into() } impl BasePanel for SidebarPanel { @@ -316,36 +405,7 @@ impl Focusable for SidebarPanel { impl Render for SidebarPanel { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { if !self.logged_in { - return v_flex() - .p_4() - .size_full() - .items_center() - .justify_center() - .gap_2() - .child( - div() - .text_sm() - .text_color(cx.theme().muted_foreground) - .child("Sign in to continue"), - ) - .child( - Button::new("onboarding") - .label("Join now") - .primary() - .w_full() - .on_click( - cx.listener(|this, _ev, window, cx| this.open_onboarding(window, cx)), - ), - ) - .child( - Button::new("import-identity") - .label("Import identity") - .secondary() - .w_full() - .on_click( - cx.listener(|this, _ev, window, cx| this.open_import(window, cx)), - ), - ); + return self.render_sign_in(window, cx); } let backend = Backend::global(cx); diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 9ee7edf..930230c 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -14,9 +14,6 @@ fn main() { .with_assets(Assets) .with_http_client(Arc::new(reqwest_client::ReqwestClient::new())) .run(move |cx| { - // Set app identity - cx.set_app_identity("su.reya.signed", "Signed"); - // Initialize components gpui_component::init(cx); @@ -63,6 +60,9 @@ fn main() { std::fs::create_dir_all(paths::repos_dir()).ok(); signed_state::GitStore::set_global(paths::repos_dir().clone(), cx); + // Set app identity + cx.set_app_identity("su.reya.signed", "Signed"); + // Set up the window options let bounds = Bounds::centered(None, size(px(1120.0), px(750.0)), cx);