diff --git a/Cargo.lock b/Cargo.lock index 18dfe2c..e660a9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,7 +163,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.1", + "rand 0.9.2", "serde", "serde_repr", "url", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" dependencies = [ "async-lock", "blocking", @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", @@ -248,8 +248,7 @@ dependencies = [ "polling", "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -276,9 +275,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ "async-channel", "async-io", @@ -290,7 +289,6 @@ dependencies = [ "event-listener", "futures-lite 2.6.0", "rustix 1.0.8", - "tracing", ] [[package]] @@ -306,9 +304,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -319,7 +317,7 @@ dependencies = [ "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -449,9 +447,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08b5d4e069cbc868041a64bd68dc8cb39a0d79585cd6c5a24caa8c2d622121be" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" dependencies = [ "aws-lc-sys", "zeroize", @@ -644,7 +642,7 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] name = "blade-graphics" version = "0.6.0" -source = "git+https://github.com/kvark/blade?rev=416375211bb0b5826b3584dccdb6a43369e499ad#416375211bb0b5826b3584dccdb6a43369e499ad" +source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5" dependencies = [ "ash", "ash-window", @@ -677,7 +675,7 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.3.0" -source = "git+https://github.com/kvark/blade?rev=416375211bb0b5826b3584dccdb6a43369e499ad#416375211bb0b5826b3584dccdb6a43369e499ad" +source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5" dependencies = [ "proc-macro2", "quote", @@ -687,7 +685,7 @@ dependencies = [ [[package]] name = "blade-util" version = "0.2.0" -source = "git+https://github.com/kvark/blade?rev=416375211bb0b5826b3584dccdb6a43369e499ad#416375211bb0b5826b3584dccdb6a43369e499ad" +source = "git+https://github.com/kvark/blade?rev=e0ec4e720957edd51b945b64dd85605ea54bcfe5#e0ec4e720957edd51b945b64dd85605ea54bcfe5" dependencies = [ "blade-graphics", "bytemuck", @@ -774,9 +772,9 @@ dependencies = [ [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" dependencies = [ "proc-macro2", "quote", @@ -856,9 +854,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.29" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -1075,7 +1073,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1476,7 +1474,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "proc-macro2", "quote", @@ -2328,7 +2326,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2394,7 +2392,7 @@ dependencies = [ "slotmap", "smallvec", "smol", - "strum 0.27.1", + "strum 0.27.2", "sum_tree", "taffy", "thiserror 2.0.12", @@ -2421,7 +2419,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2433,7 +2431,7 @@ dependencies = [ [[package]] name = "gpui_tokio" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "gpui", "tokio", @@ -2443,9 +2441,9 @@ dependencies = [ [[package]] name = "grid" -version = "0.13.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" +checksum = "71b01d27060ad58be4663b9e4ac9e2d4806918e8876af8912afbddd1a91d5eaa" [[package]] name = "h2" @@ -2655,13 +2653,14 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "anyhow", "bytes", "derive_more", "futures", "http", + "http-body", "log", "serde", "serde_json", @@ -2672,7 +2671,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "rustls", "rustls-platform-verifier", @@ -2740,9 +2739,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64", "bytes", @@ -2756,7 +2755,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -3048,9 +3047,9 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ "bitflags 2.9.1", "cfg-if", @@ -3194,11 +3193,12 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ "arrayvec", + "euclid", "smallvec", ] @@ -3272,9 +3272,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" dependencies = [ "bitflags 2.9.1", "libc", @@ -3449,7 +3449,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "anyhow", "bindgen 0.71.1", @@ -3672,7 +3672,7 @@ dependencies = [ [[package]] name = "nostr" version = "0.42.1" -source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b" +source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0" dependencies = [ "aes", "base64", @@ -3695,7 +3695,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b" +source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0" dependencies = [ "async-utility", "nostr", @@ -3707,7 +3707,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b" +source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0" dependencies = [ "flatbuffers", "lru", @@ -3718,7 +3718,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b" +source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0" dependencies = [ "async-utility", "heed", @@ -3731,7 +3731,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b" +source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0" dependencies = [ "async-utility", "async-wsocket", @@ -3747,7 +3747,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.42.0" -source = "git+https://github.com/rust-nostr/nostr#ef342a983007ad46fc681d90afd9d8458f632b7b" +source = "git+https://github.com/rust-nostr/nostr#b2d26225a156765d2de9456dd38cc7a40f7426f0" dependencies = [ "async-utility", "nostr", @@ -4062,7 +4062,7 @@ dependencies = [ "num", "num-bigint-dig", "pbkdf2", - "rand 0.9.1", + "rand 0.9.2", "serde", "sha2", "subtle", @@ -4364,17 +4364,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", "rustix 1.0.8", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4539,7 +4538,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2", + "socket2 0.5.10", "thiserror 2.0.12", "tokio", "tracing", @@ -4555,7 +4554,7 @@ dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash 2.1.1", "rustls", @@ -4576,7 +4575,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -4609,9 +4608,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4761,9 +4760,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" dependencies = [ "bitflags 2.9.1", ] @@ -4802,7 +4801,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "derive_refineable", "workspace-hack", @@ -4953,7 +4952,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "anyhow", "bytes", @@ -5308,7 +5307,7 @@ dependencies = [ [[package]] name = "scap" version = "0.0.8" -source = "git+https://github.com/zed-industries/scap?rev=270538dc780f5240723233ff901e1054641ed318#270538dc780f5240723233ff901e1054641ed318" +source = "git+https://github.com/zed-industries/scap?rev=808aa5c45b41e8f44729d02e38fd00a2fe2722e7#808aa5c45b41e8f44729d02e38fd00a2fe2722e7" dependencies = [ "anyhow", "cocoa 0.25.0", @@ -5479,7 +5478,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "anyhow", "serde", @@ -5534,9 +5533,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" dependencies = [ "indexmap", "itoa", @@ -5772,6 +5771,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -5822,11 +5831,11 @@ dependencies = [ [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.1", + "strum_macros 0.27.2", ] [[package]] @@ -5844,14 +5853,13 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "rustversion", "syn 2.0.104", ] @@ -5864,7 +5872,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "arrayvec", "log", @@ -6087,13 +6095,12 @@ dependencies = [ [[package]] name = "taffy" -version = "0.4.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec17858c2d465b2f734b798b920818a974faf0babb15d7fef81818a4b2d16f1" +checksum = "7aaef0ac998e6527d6d0d5582f7e43953bb17221ac75bb8eb2fcc2db3396db1c" dependencies = [ "arrayvec", "grid", - "num-traits", "serde", "slotmap", ] @@ -6297,7 +6304,7 @@ dependencies = [ "mio", "pin-project-lite", "slab", - "socket2", + "socket2 0.5.10", "tokio-macros", "windows-sys 0.52.0", ] @@ -6605,7 +6612,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.1", + "rand 0.9.2", "rustls", "rustls-pki-types", "sha1", @@ -6838,7 +6845,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#ebad5ca50e11fc9d3fbbab397888d0944a825bb7" +source = "git+https://github.com/zed-industries/zed#500ceaabcde9d78b3a1a0d14674523e497dbc6e4" dependencies = [ "anyhow", "async-fs", @@ -7231,14 +7238,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.1", + "webpki-root-certs 1.0.2", ] [[package]] name = "webpki-root-certs" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86138b15b2b7d561bc4469e77027b8dd005a43dc502e9031d1f5afc8ce1f280e" +checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" dependencies = [ "rustls-pki-types", ] @@ -7249,14 +7256,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.1", + "webpki-roots 1.0.2", ] [[package]] name = "webpki-roots" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] diff --git a/assets/icons/open-url.svg b/assets/icons/open-url.svg new file mode 100644 index 0000000..837e1e6 --- /dev/null +++ b/assets/icons/open-url.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/common/src/display.rs b/crates/common/src/display.rs index 630a627..86fd5fd 100644 --- a/crates/common/src/display.rs +++ b/crates/common/src/display.rs @@ -41,8 +41,17 @@ impl DisplayProfile for Profile { } } - let Ok(pubkey) = self.public_key().to_bech32(); - - format!("{}:{}", &pubkey[0..4], &pubkey[pubkey.len() - 4..]).into() + shorten_pubkey(self.public_key(), 4) } } + +pub fn shorten_pubkey(public_key: PublicKey, len: usize) -> SharedString { + let Ok(pubkey) = public_key.to_bech32(); + + format!( + "{}:{}", + &pubkey[0..(len + 1)], + &pubkey[pubkey.len() - len..] + ) + .into() +} diff --git a/crates/coop/src/chatspace.rs b/crates/coop/src/chatspace.rs index 9c9f61b..67eeaa4 100644 --- a/crates/coop/src/chatspace.rs +++ b/crates/coop/src/chatspace.rs @@ -25,6 +25,7 @@ use ui::modal::ModalButtonProps; use ui::{ContextModal, IconName, Root, Sizable, StyledExt, TitleBar}; use crate::views::chat::{self, Chat}; +use crate::views::screening::Screening; use crate::views::user_profile::UserProfile; use crate::views::{ login, new_account, onboarding, preferences, sidebar, startup, user_profile, welcome, @@ -73,7 +74,7 @@ pub struct ChatSpace { dock: Entity, toolbar: bool, #[allow(unused)] - subscriptions: SmallVec<[Subscription; 5]>, + subscriptions: SmallVec<[Subscription; 6]>, } impl ChatSpace { @@ -172,13 +173,20 @@ impl ChatSpace { } })); - // Automatically run on_load function from UserProfile + // Automatically run on_load function when UserProfile is created subscriptions.push(cx.observe_new::(|this, window, cx| { if let Some(window) = window { this.on_load(window, cx); } })); + // Automatically run on_load function when Screening is created + subscriptions.push(cx.observe_new::(|this, window, cx| { + if let Some(window) = window { + this.on_load(window, cx); + } + })); + // Subscribe to open chat room requests subscriptions.push(cx.subscribe_in( ®istry, diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 5834460..963b877 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -405,10 +405,11 @@ async fn handle_nostr_notifications( .kind(Kind::InboxRelays) .limit(1); - if client.subscribe(filter, Some(opts)).await.is_ok() { + if let Ok(output) = client.subscribe(filter, Some(opts)).await { log::info!( - "Subscribed to get DM relays: {}", - event.pubkey.to_bech32().unwrap() + "Subscribed to get DM relays: {} - Relays: {:?}", + event.pubkey.to_bech32().unwrap(), + output.success ) } } diff --git a/crates/coop/src/views/mod.rs b/crates/coop/src/views/mod.rs index bd55380..cd7eede 100644 --- a/crates/coop/src/views/mod.rs +++ b/crates/coop/src/views/mod.rs @@ -6,6 +6,7 @@ pub mod new_account; pub mod onboarding; pub mod preferences; pub mod relays; +pub mod screening; pub mod sidebar; pub mod startup; pub mod user_profile; diff --git a/crates/coop/src/views/preferences.rs b/crates/coop/src/views/preferences.rs index 1055e66..dac3191 100644 --- a/crates/coop/src/views/preferences.rs +++ b/crates/coop/src/views/preferences.rs @@ -51,7 +51,7 @@ impl Preferences { fn open_edit_profile(&self, window: &mut Window, cx: &mut Context) { let edit_profile = edit_profile::init(window, cx); - let title = SharedString::new(t!("preferences.modal_profile_title")); + let title = SharedString::new(t!("profile.title")); window.open_modal(cx, move |modal, _window, _cx| { modal diff --git a/crates/coop/src/views/screening.rs b/crates/coop/src/views/screening.rs new file mode 100644 index 0000000..16e379d --- /dev/null +++ b/crates/coop/src/views/screening.rs @@ -0,0 +1,288 @@ +use common::display::{shorten_pubkey, DisplayProfile}; +use common::nip05::nip05_verify; +use global::nostr_client; +use gpui::prelude::FluentBuilder; +use gpui::{ + div, relative, rems, App, AppContext, Context, Entity, IntoElement, ParentElement, Render, + SharedString, Styled, Task, Window, +}; +use gpui_tokio::Tokio; +use i18n::t; +use identity::Identity; +use nostr_sdk::prelude::*; +use registry::Registry; +use settings::AppSettings; +use theme::ActiveTheme; +use ui::avatar::Avatar; +use ui::button::{Button, ButtonRounded, ButtonVariants}; +use ui::{h_flex, v_flex, ContextModal, Icon, IconName, Sizable, StyledExt}; + +pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity { + Screening::new(public_key, window, cx) +} + +pub struct Screening { + public_key: PublicKey, + followed: bool, + connections: usize, + verified: bool, +} + +impl Screening { + pub fn new(public_key: PublicKey, _window: &mut Window, cx: &mut App) -> Entity { + cx.new(|_| Self { + public_key, + followed: false, + connections: 0, + verified: false, + }) + } + + pub fn on_load(&mut self, window: &mut Window, cx: &mut Context) { + // Skip if user isn't logged in + let Some(identity) = Identity::read_global(cx).public_key() else { + return; + }; + + let public_key = self.public_key; + + let check_trust_score: Task<(bool, usize)> = cx.background_spawn(async move { + let client = nostr_client(); + + let follow = Filter::new() + .kind(Kind::ContactList) + .author(identity) + .pubkey(public_key) + .limit(1); + + let connection = Filter::new() + .kind(Kind::ContactList) + .pubkey(public_key) + .limit(1); + + let is_follow = client.database().count(follow).await.unwrap_or(0) >= 1; + let connects = client.database().count(connection).await.unwrap_or(0); + + (is_follow, connects) + }); + + let verify_nip05 = if let Some(address) = self.address(cx) { + Some(Tokio::spawn(cx, async move { + nip05_verify(public_key, &address).await.unwrap_or(false) + })) + } else { + None + }; + + cx.spawn_in(window, async move |this, cx| { + let (followed, connections) = check_trust_score.await; + + this.update(cx, |this, cx| { + this.followed = followed; + this.connections = connections; + cx.notify(); + }) + .ok(); + + // Update the NIP05 verification status if user has NIP05 address + if let Some(task) = verify_nip05 { + if let Ok(verified) = task.await { + this.update(cx, |this, cx| { + this.verified = verified; + cx.notify(); + }) + .ok(); + } + } + }) + .detach(); + } + + fn profile(&self, cx: &Context) -> Profile { + let registry = Registry::read_global(cx); + registry.get_person(&self.public_key, cx) + } + + fn address(&self, cx: &Context) -> Option { + self.profile(cx).metadata().nip05 + } + + fn open_njump(&mut self, _window: &mut Window, cx: &mut App) { + let Ok(bech32) = self.public_key.to_bech32(); + cx.open_url(&format!("https://njump.me/{bech32}")); + } + + fn report(&mut self, window: &mut Window, cx: &mut Context) { + let public_key = self.public_key; + let task: Task> = cx.background_spawn(async move { + let client = nostr_client(); + let builder = EventBuilder::report( + vec![Tag::public_key_report(public_key, Report::Impersonation)], + "scam/impersonation", + ); + let _ = client.send_event_builder(builder).await?; + + Ok(()) + }); + + cx.spawn_in(window, async move |_, cx| { + if task.await.is_ok() { + cx.update(|window, cx| { + window.close_modal(cx); + window.push_notification(t!("screening.report_msg"), cx); + }) + .ok(); + } + }) + .detach(); + } +} + +impl Render for Screening { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + let proxy = AppSettings::get_global(cx).settings.proxy_user_avatars; + let profile = self.profile(cx); + let shorten_pubkey = shorten_pubkey(profile.public_key(), 8); + + v_flex() + .w_full() + .px_4() + .pt_8() + .pb_4() + .gap_4() + .child( + v_flex() + .gap_3() + .items_center() + .justify_center() + .text_center() + .child(Avatar::new(profile.avatar_url(proxy)).size(rems(4.))) + .child( + div() + .font_semibold() + .line_height(relative(1.25)) + .child(profile.display_name()), + ), + ) + .child( + h_flex() + .gap_1() + .child( + div() + .p_1() + .flex_1() + .h_7() + .flex() + .items_center() + .justify_center() + .rounded_full() + .bg(cx.theme().elevated_surface_background) + .text_sm() + .truncate() + .text_ellipsis() + .text_center() + .line_height(relative(1.)) + .child(shorten_pubkey), + ) + .child( + Button::new("njump") + .label(t!("profile.njump")) + .secondary() + .small() + .rounded(ButtonRounded::Full) + .on_click(cx.listener(move |this, _e, window, cx| { + this.open_njump(window, cx); + })), + ) + .child( + Button::new("report") + .tooltip(t!("screening.report")) + .icon(IconName::Info) + .danger_alt() + .small() + .rounded(ButtonRounded::Full) + .on_click(cx.listener(move |this, _e, window, cx| { + this.report(window, cx); + })), + ), + ) + .child( + v_flex() + .gap_2() + .when_some(self.address(cx), |this, address| { + this.child(h_flex().gap_2().map(|this| { + if self.verified { + this.text_sm() + .child( + Icon::new(IconName::CheckCircleFill) + .small() + .flex_shrink_0() + .text_color(cx.theme().icon_accent), + ) + .child(div().flex_1().child(SharedString::new(t!( + "screening.verified", + address = address + )))) + } else { + this.text_sm() + .child( + Icon::new(IconName::CheckCircleFill) + .small() + .text_color(cx.theme().icon_muted), + ) + .child(div().flex_1().child(SharedString::new(t!( + "screening.not_verified", + address = address + )))) + } + })) + }) + .child(h_flex().gap_2().map(|this| { + if !self.followed { + this.text_sm() + .child( + Icon::new(IconName::CheckCircleFill) + .small() + .text_color(cx.theme().icon_muted), + ) + .child(SharedString::new(t!("screening.not_contact"))) + } else { + this.text_sm() + .child( + Icon::new(IconName::CheckCircleFill) + .small() + .text_color(cx.theme().icon_accent), + ) + .child(SharedString::new(t!("screening.contact"))) + } + })) + .child( + h_flex() + .gap_2() + .text_sm() + .child( + Icon::new(IconName::CheckCircleFill) + .small() + .flex_shrink_0() + .text_color({ + if self.connections > 0 { + cx.theme().icon_accent + } else { + cx.theme().icon_muted + } + }), + ) + .map(|this| { + if self.connections > 0 { + this.child(SharedString::new(t!( + "screening.total_connections", + u = self.connections + ))) + } else { + this.child(SharedString::new(t!("screening.no_connections"))) + } + }), + ), + ) + } +} diff --git a/crates/coop/src/views/sidebar/element.rs b/crates/coop/src/views/sidebar/element.rs deleted file mode 100644 index 540fea0..0000000 --- a/crates/coop/src/views/sidebar/element.rs +++ /dev/null @@ -1,122 +0,0 @@ -use std::rc::Rc; - -use gpui::prelude::FluentBuilder; -use gpui::{ - div, img, rems, App, ClickEvent, Div, InteractiveElement, IntoElement, ParentElement as _, - RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, -}; -use settings::AppSettings; -use theme::ActiveTheme; -use ui::avatar::Avatar; -use ui::StyledExt; - -#[derive(IntoElement)] -pub struct DisplayRoom { - ix: usize, - base: Div, - img: Option, - label: Option, - description: Option, - #[allow(clippy::type_complexity)] - handler: Rc, -} - -impl DisplayRoom { - pub fn new(ix: usize) -> Self { - Self { - ix, - base: div().h_9().w_full().px_1p5(), - img: None, - label: None, - description: None, - handler: Rc::new(|_, _, _| {}), - } - } - - pub fn label(mut self, label: impl Into) -> Self { - self.label = Some(label.into()); - self - } - - pub fn description(mut self, description: impl Into) -> Self { - self.description = Some(description.into()); - self - } - - pub fn img(mut self, img: impl Into) -> Self { - self.img = Some(img.into()); - self - } - - pub fn on_click( - mut self, - handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.handler = Rc::new(handler); - self - } -} - -impl RenderOnce for DisplayRoom { - fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { - let handler = self.handler.clone(); - let hide_avatar = AppSettings::get_global(cx).settings.hide_user_avatars; - - self.base - .id(self.ix) - .flex() - .items_center() - .gap_2() - .text_sm() - .rounded(cx.theme().radius) - .when(!hide_avatar, |this| { - this.child( - div() - .flex_shrink_0() - .size_6() - .rounded_full() - .overflow_hidden() - .map(|this| { - if let Some(path) = self.img { - this.child(Avatar::new(path).size(rems(1.5))) - } else { - this.child( - img("brand/avatar.png") - .rounded_full() - .size_6() - .into_any_element(), - ) - } - }), - ) - }) - .child( - div() - .flex_1() - .flex() - .items_center() - .justify_between() - .when_some(self.label, |this, label| { - this.child( - div() - .flex_1() - .line_clamp(1) - .text_ellipsis() - .font_medium() - .child(label), - ) - }) - .when_some(self.description, |this, description| { - this.child( - div() - .flex_shrink_0() - .text_xs() - .text_color(cx.theme().text_placeholder) - .child(description), - ) - }), - ) - .hover(|this| this.bg(cx.theme().elevated_surface_background)) - .on_click(move |ev, window, cx| handler(ev, window, cx)) - } -} diff --git a/crates/coop/src/views/sidebar/list_item.rs b/crates/coop/src/views/sidebar/list_item.rs new file mode 100644 index 0000000..83acde5 --- /dev/null +++ b/crates/coop/src/views/sidebar/list_item.rs @@ -0,0 +1,174 @@ +use std::rc::Rc; + +use gpui::prelude::FluentBuilder; +use gpui::{ + div, img, rems, App, ClickEvent, Div, InteractiveElement, IntoElement, + ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, +}; +use i18n::t; +use nostr_sdk::prelude::*; +use registry::room::RoomKind; +use settings::AppSettings; +use theme::ActiveTheme; +use ui::actions::OpenProfile; +use ui::avatar::Avatar; +use ui::context_menu::ContextMenuExt; +use ui::modal::ModalButtonProps; +use ui::{h_flex, ContextModal, StyledExt}; + +use crate::views::screening; + +#[derive(IntoElement)] +pub struct RoomListItem { + ix: usize, + base: Div, + public_key: PublicKey, + name: Option, + avatar: Option, + created_at: Option, + kind: Option, + #[allow(clippy::type_complexity)] + handler: Rc, +} + +impl RoomListItem { + pub fn new(ix: usize, public_key: PublicKey) -> Self { + Self { + ix, + public_key, + base: h_flex().h_9().w_full().px_1p5(), + name: None, + avatar: None, + created_at: None, + kind: None, + handler: Rc::new(|_, _, _| {}), + } + } + + pub fn name(mut self, name: impl Into) -> Self { + self.name = Some(name.into()); + self + } + + pub fn created_at(mut self, created_at: impl Into) -> Self { + self.created_at = Some(created_at.into()); + self + } + + pub fn avatar(mut self, avatar: impl Into) -> Self { + self.avatar = Some(avatar.into()); + self + } + + pub fn kind(mut self, kind: RoomKind) -> Self { + self.kind = Some(kind); + self + } + + pub fn on_click( + mut self, + handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + ) -> Self { + self.handler = Rc::new(handler); + self + } +} + +impl RenderOnce for RoomListItem { + fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { + let public_key = self.public_key; + let kind = self.kind; + let handler = self.handler.clone(); + let hide_avatar = AppSettings::get_global(cx).settings.hide_user_avatars; + let screening = AppSettings::get_global(cx).settings.screening; + + self.base + .id(self.ix) + .gap_2() + .text_sm() + .rounded(cx.theme().radius) + .when(!hide_avatar, |this| { + this.child( + div() + .flex_shrink_0() + .size_6() + .rounded_full() + .overflow_hidden() + .map(|this| { + if let Some(img) = self.avatar { + this.child(Avatar::new(img).size(rems(1.5))) + } else { + this.child( + img("brand/avatar.png") + .rounded_full() + .size_6() + .into_any_element(), + ) + } + }), + ) + }) + .child( + div() + .flex_1() + .flex() + .items_center() + .justify_between() + .when_some(self.name, |this, name| { + this.child( + div() + .flex_1() + .line_clamp(1) + .text_ellipsis() + .truncate() + .font_medium() + .child(name), + ) + }) + .when_some(self.created_at, |this, ago| { + this.child( + div() + .flex_shrink_0() + .text_xs() + .text_color(cx.theme().text_placeholder) + .child(ago), + ) + }), + ) + .context_menu(move |this, _window, _cx| { + // TODO: add share chat room + this.menu(t!("profile.view"), Box::new(OpenProfile(public_key))) + }) + .hover(|this| this.bg(cx.theme().elevated_surface_background)) + .on_click(move |event, window, cx| { + let handler = handler.clone(); + + if let Some(kind) = kind { + if kind != RoomKind::Ongoing && screening { + let screening = screening::init(public_key, window, cx); + + window.open_modal(cx, move |this, _window, _cx| { + let handler_clone = handler.clone(); + + this.confirm() + .child(screening.clone()) + .button_props( + ModalButtonProps::default() + .cancel_text(t!("screening.ignore")) + .ok_text(t!("screening.response")), + ) + .on_ok(move |event, window, cx| { + handler_clone(event, window, cx); + // true to close the modal + true + }) + }); + } else { + handler(event, window, cx) + } + } else { + handler(event, window, cx) + } + }) + } +} diff --git a/crates/coop/src/views/sidebar/mod.rs b/crates/coop/src/views/sidebar/mod.rs index b2c996a..352a081 100644 --- a/crates/coop/src/views/sidebar/mod.rs +++ b/crates/coop/src/views/sidebar/mod.rs @@ -5,8 +5,6 @@ use std::time::Duration; use anyhow::{anyhow, Error}; use common::debounced_delay::DebouncedDelay; use common::display::DisplayProfile; -use common::nip05::nip05_verify; -use element::DisplayRoom; use global::constants::{BOOTSTRAP_RELAYS, DEFAULT_MODAL_WIDTH, SEARCH_RELAYS}; use global::nostr_client; use gpui::prelude::FluentBuilder; @@ -20,6 +18,7 @@ use gpui_tokio::Tokio; use i18n::t; use identity::Identity; use itertools::Itertools; +use list_item::RoomListItem; use nostr_sdk::prelude::*; use registry::room::{Room, RoomKind}; use registry::{Registry, RoomEmitter}; @@ -37,7 +36,7 @@ use ui::{ContextModal, IconName, Selectable, Sizable, StyledExt}; use crate::views::compose; -mod element; +mod list_item; const FIND_DELAY: u64 = 600; const FIND_LIMIT: usize = 10; @@ -58,7 +57,6 @@ pub struct Sidebar { // Rooms indicator: Entity>, active_filter: Entity, - trusted_only: bool, // GPUI focus_handle: FocusHandle, image_cache: Entity, @@ -129,7 +127,6 @@ impl Sidebar { image_cache: RetainAllImageCache::new(cx), find_debouncer: DebouncedDelay::new(), finding: false, - trusted_only: false, cancel_handle, indicator, active_filter, @@ -153,10 +150,16 @@ impl Sidebar { } async fn create_temp_room(identity: PublicKey, public_key: PublicKey) -> Result { + let client = nostr_client(); let keys = Keys::generate(); let builder = EventBuilder::private_msg_rumor(public_key, ""); let event = builder.build(identity).sign(&keys).await?; - let room = Room::new(&event).kind(RoomKind::Ongoing); + + // Request to get user's metadata + Self::request_metadata(client, public_key).await?; + + // Create a temporary room + let room = Room::new(&event).rearrange_by(identity); Ok(room) } @@ -165,7 +168,6 @@ impl Sidebar { let client = nostr_client(); let timeout = Duration::from_secs(2); let mut rooms: BTreeSet = BTreeSet::new(); - let mut processed: BTreeSet = BTreeSet::new(); let filter = Filter::new() .kind(Kind::Metadata) @@ -177,24 +179,13 @@ impl Sidebar { .await { // Process to verify the search results - for event in events.into_iter() { - if processed.contains(&event.pubkey) { + for event in events.into_iter().unique_by(|event| event.pubkey) { + // Skip if author is match current user + if event.pubkey == identity { continue; } - processed.insert(event.pubkey); - - let metadata = Metadata::from_json(event.content).unwrap_or_default(); - - // Skip if NIP-05 is not found - let Some(target) = metadata.nip05.as_ref() else { - continue; - }; - - // Skip if NIP-05 is not valid or failed to verify - if !nip05_verify(event.pubkey, target).await.unwrap_or(false) { - continue; - }; + // Return a temporary room if let Ok(room) = Self::create_temp_room(identity, event.pubkey).await { rooms.insert(room); } @@ -299,14 +290,8 @@ impl Sidebar { let address = query.to_owned(); let task = Tokio::spawn(cx, async move { - let client = nostr_client(); - if let Ok(profile) = common::nip05::nip05_profile(&address).await { - let public_key = profile.public_key; - // Request for user metadata - Self::request_metadata(client, public_key).await.ok(); - // Return a temporary room - Self::create_temp_room(identity, public_key).await + Self::create_temp_room(identity, profile.public_key).await } else { Err(anyhow!(t!("sidebar.addr_error"))) } @@ -362,11 +347,6 @@ impl Sidebar { }; let task: Task> = cx.background_spawn(async move { - let client = nostr_client(); - - // Request metadata for this user - Self::request_metadata(client, public_key).await?; - // Create a gift wrap event to represent as room Self::create_temp_room(identity, public_key).await }); @@ -544,11 +524,6 @@ impl Sidebar { }); } - fn set_trusted_only(&mut self, cx: &mut Context) { - self.trusted_only = !self.trusted_only; - cx.notify(); - } - fn open_room(&mut self, id: u64, window: &mut Window, cx: &mut Context) { let room = if let Some(room) = Registry::read_global(cx).room(&id, cx) { room @@ -695,20 +670,19 @@ impl Sidebar { for ix in range { if let Some(room) = rooms.get(ix) { let this = room.read(cx); - let id = this.id; - let ago = this.ago(); - let label = this.display_name(cx); - let img = this.display_image(proxy, cx); - - let handler = cx.listener(move |this, _, window, cx| { - this.open_room(id, window, cx); + let handler = cx.listener({ + let id = this.id; + move |this, _, window, cx| { + this.open_room(id, window, cx); + } }); items.push( - DisplayRoom::new(ix) - .img(img) - .label(label) - .description(ago) + RoomListItem::new(ix, this.members[0]) + .avatar(this.display_image(proxy, cx)) + .name(this.display_name(cx)) + .created_at(this.ago()) + .kind(this.kind) .on_click(handler), ) } @@ -761,7 +735,7 @@ impl Render for Sidebar { if self.active_filter.read(cx) == &RoomKind::Ongoing { registry.ongoing_rooms(cx) } else { - registry.request_rooms(self.trusted_only, cx) + registry.request_rooms(cx) } }; @@ -872,28 +846,10 @@ impl Render for Sidebar { .rounded(ButtonRounded::Full) .selected(!self.filter(&RoomKind::Ongoing, cx)) .on_click(cx.listener(|this, _, _, cx| { - this.set_filter(RoomKind::Unknown, cx); + this.set_filter(RoomKind::default(), cx); })), ), - ) - .when(!self.filter(&RoomKind::Ongoing, cx), |this| { - this.child( - Button::new("trusted") - .tooltip(t!("sidebar.trusted_contacts_tooltip")) - .map(|this| { - if self.trusted_only { - this.icon(IconName::FilterFill) - } else { - this.icon(IconName::Filter) - } - }) - .small() - .transparent() - .on_click(cx.listener(|this, _, _, cx| { - this.set_trusted_only(cx); - })), - ) - }), + ), ) .when(registry.loading, |this| { this.child( @@ -918,7 +874,7 @@ impl Render for Sidebar { ) .when(registry.loading, |this| { this.child( - div().absolute().bottom_4().px_4().child( + div().absolute().bottom_4().px_4().w_full().child( div() .p_1() .w_full() diff --git a/crates/coop/src/views/user_profile.rs b/crates/coop/src/views/user_profile.rs index 17c844b..55dd801 100644 --- a/crates/coop/src/views/user_profile.rs +++ b/crates/coop/src/views/user_profile.rs @@ -188,10 +188,13 @@ impl Render for UserProfile { .when(!self.followed, |this| { this.child( div() + .flex_none() + .w_32() .p_1() .rounded_full() - .bg(cx.theme().surface_background) + .bg(cx.theme().elevated_surface_background) .text_xs() + .font_semibold() .child(SharedString::new(t!("profile.unknown"))), ) }), @@ -211,7 +214,7 @@ impl Render for UserProfile { .gap_1() .child( div() - .p_1p5() + .p_2() .h_9() .rounded_md() .bg(cx.theme().elevated_surface_background) @@ -246,15 +249,18 @@ impl Render for UserProfile { .text_color(cx.theme().text_muted) .child(SharedString::new(t!("profile.label_bio"))), ) - .when_some(profile.metadata().about, |this, bio| { - this.child( - div() - .p_1p5() - .rounded_md() - .bg(cx.theme().elevated_surface_background) - .child(bio), - ) - }), + .child( + div() + .p_2() + .rounded_md() + .bg(cx.theme().elevated_surface_background) + .child( + profile + .metadata() + .about + .unwrap_or(t!("profile.no_bio").to_string()), + ), + ), ) .child( Button::new("open-njump") diff --git a/crates/global/src/lib.rs b/crates/global/src/lib.rs index 5ec050d..7f3a1e6 100644 --- a/crates/global/src/lib.rs +++ b/crates/global/src/lib.rs @@ -58,7 +58,7 @@ pub fn nostr_client() -> &'static Client { .automatic_authentication(true) // Sleep after idle for 5 seconds .sleep_when_idle(SleepWhenIdle::Enabled { - timeout: Duration::from_secs(5), + timeout: Duration::from_secs(10), }); ClientBuilder::default().database(lmdb).opts(opts).build() diff --git a/crates/registry/src/lib.rs b/crates/registry/src/lib.rs index 3b2470d..00d7c3e 100644 --- a/crates/registry/src/lib.rs +++ b/crates/registry/src/lib.rs @@ -188,16 +188,10 @@ impl Registry { } /// Get all request rooms. - pub fn request_rooms(&self, trusted_only: bool, cx: &App) -> Vec> { + pub fn request_rooms(&self, cx: &App) -> Vec> { self.rooms .iter() - .filter(|room| { - if trusted_only { - room.read(cx).kind == RoomKind::Trusted - } else { - room.read(cx).kind != RoomKind::Ongoing - } - }) + .filter(|room| room.read(cx).kind != RoomKind::Ongoing) .cloned() .collect() } @@ -274,7 +268,6 @@ impl Registry { let events = send_events.merge(recv_events); let mut rooms: BTreeSet = BTreeSet::new(); - let mut trusted_keys: BTreeSet = BTreeSet::new(); // Process each event and group by room hash for event in events @@ -291,20 +284,7 @@ impl Registry { let mut public_keys = event.tags.public_keys().copied().collect_vec(); public_keys.push(event.pubkey); - let mut is_trust = trusted_keys.contains(&event.pubkey); - - if !is_trust { - // Check if room's author is seen in any contact list - let filter = Filter::new().kind(Kind::ContactList).pubkey(event.pubkey); - // If room's author is seen at least once, mark as trusted - is_trust = client.database().count(filter).await.unwrap_or(0) >= 1; - - if is_trust { - trusted_keys.insert(event.pubkey); - } - } - - // Check if current_user has sent a message to this room at least once + // Check if the current user has sent at least one message to this room let filter = Filter::new() .kind(Kind::PrivateDirectMessage) .author(public_key) @@ -313,20 +293,13 @@ impl Registry { // If current user has sent a message at least once, mark as ongoing let is_ongoing = client.database().count(filter).await.unwrap_or(1) >= 1; + // Create a new room + let room = Room::new(&event).rearrange_by(public_key); + if is_ongoing { - rooms.insert( - Room::new(&event) - .kind(RoomKind::Ongoing) - .rearrange_by(public_key), - ); - } else if is_trust { - rooms.insert( - Room::new(&event) - .kind(RoomKind::Trusted) - .rearrange_by(public_key), - ); + rooms.insert(room.kind(RoomKind::Ongoing)); } else { - rooms.insert(Room::new(&event).rearrange_by(public_key)); + rooms.insert(room); } } @@ -425,7 +398,7 @@ impl Registry { self.sort(cx); } else { let room = Room::new(&event) - .kind(RoomKind::Unknown) + .kind(RoomKind::default()) .rearrange_by(identity); // Push the new room to the front of the list @@ -433,7 +406,7 @@ impl Registry { // Notify the UI about the new room cx.defer_in(window, move |_this, _window, cx| { - cx.emit(RoomEmitter::Request(RoomKind::Unknown)); + cx.emit(RoomEmitter::Request(RoomKind::default())); }); } } diff --git a/crates/registry/src/room.rs b/crates/registry/src/room.rs index 49da5e1..541c984 100644 --- a/crates/registry/src/room.rs +++ b/crates/registry/src/room.rs @@ -30,9 +30,8 @@ pub struct SendError { #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum RoomKind { Ongoing, - Trusted, #[default] - Unknown, + Request, } #[derive(Debug)] @@ -104,7 +103,7 @@ impl Room { subject, picture, members, - kind: RoomKind::Unknown, + kind: RoomKind::default(), } } diff --git a/crates/settings/src/lib.rs b/crates/settings/src/lib.rs index 6887fb2..c2deeb1 100644 --- a/crates/settings/src/lib.rs +++ b/crates/settings/src/lib.rs @@ -25,6 +25,7 @@ pub struct Settings { pub proxy_user_avatars: bool, pub hide_user_avatars: bool, pub backup_messages: bool, + pub screening: bool, pub auto_login: bool, } @@ -62,10 +63,11 @@ impl AppSettings { fn new(cx: &mut Context) -> Self { let settings = Settings { - media_server: Url::parse("https://nostrmedia.com").expect("it's fine"), + media_server: Url::parse("https://nostrmedia.com").unwrap(), proxy_user_avatars: true, hide_user_avatars: false, backup_messages: true, + screening: true, auto_login: false, }; diff --git a/crates/theme/src/colors.rs b/crates/theme/src/colors.rs index 1fe1609..23408ad 100644 --- a/crates/theme/src/colors.rs +++ b/crates/theme/src/colors.rs @@ -1,2205 +1,2213 @@ -#![allow(dead_code)] - -use gpui::{hsla, Hsla, Rgba}; - -use crate::scale::{ColorScale, ColorScaleSet, ColorScales}; - -pub(crate) fn neutral() -> ColorScaleSet { - gray() -} - -pub(crate) fn brand() -> ColorScaleSet { - yellow() -} - -/// Make a [gpui::Hsla] color. -/// -/// - h: 0..360.0 -/// - s: 0.0..100.0 -/// - l: 0.0..100.0 -pub fn hsl(h: f32, s: f32, l: f32) -> Hsla { - hsla(h / 360., s / 100.0, l / 100.0, 1.0) -} - -type StaticColorScale = [&'static str; 12]; - -struct StaticColorScaleSet { - scale: &'static str, - light: StaticColorScale, - light_alpha: StaticColorScale, - dark: StaticColorScale, - dark_alpha: StaticColorScale, -} - -impl TryFrom for ColorScaleSet { - type Error = anyhow::Error; - - fn try_from(value: StaticColorScaleSet) -> Result { - fn to_color_scale(scale: StaticColorScale) -> Result { - scale - .into_iter() - .map(|color| Rgba::try_from(color).map(Hsla::from)) - .collect::, _>>() - .map(ColorScale::from_iter) - } - - Ok(Self::new( - value.scale, - to_color_scale(value.light)?, - to_color_scale(value.light_alpha)?, - to_color_scale(value.dark)?, - to_color_scale(value.dark_alpha)?, - )) - } -} - -/// Color scales used to build the default themes. -pub fn default_color_scales() -> ColorScales { - ColorScales { - gray: gray(), - mauve: mauve(), - slate: slate(), - sage: sage(), - olive: olive(), - sand: sand(), - gold: gold(), - bronze: bronze(), - brown: brown(), - yellow: yellow(), - amber: amber(), - orange: orange(), - tomato: tomato(), - red: red(), - ruby: ruby(), - crimson: crimson(), - pink: pink(), - plum: plum(), - purple: purple(), - violet: violet(), - iris: iris(), - indigo: indigo(), - blue: blue(), - cyan: cyan(), - teal: teal(), - jade: jade(), - green: green(), - grass: grass(), - lime: lime(), - mint: mint(), - sky: sky(), - black: black(), - white: white(), - } -} - -pub(crate) fn gray() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Gray", - light: [ - "#fcfcfcff", - "#f9f9f9ff", - "#f0f0f0ff", - "#e8e8e8ff", - "#e0e0e0ff", - "#d9d9d9ff", - "#cececeff", - "#bbbbbbff", - "#8d8d8dff", - "#838383ff", - "#646464ff", - "#202020ff", - ], - light_alpha: [ - "#00000003", - "#00000006", - "#0000000f", - "#00000017", - "#0000001f", - "#00000026", - "#00000031", - "#00000044", - "#00000072", - "#0000007c", - "#0000009b", - "#000000df", - ], - dark: [ - "#111111ff", - "#191919ff", - "#222222ff", - "#2a2a2aff", - "#313131ff", - "#3a3a3aff", - "#484848ff", - "#606060ff", - "#6e6e6eff", - "#7b7b7bff", - "#b4b4b4ff", - "#eeeeeeff", - ], - dark_alpha: [ - "#00000000", - "#ffffff09", - "#ffffff12", - "#ffffff1b", - "#ffffff22", - "#ffffff2c", - "#ffffff3b", - "#ffffff55", - "#ffffff64", - "#ffffff72", - "#ffffffaf", - "#ffffffed", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn mauve() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Mauve", - light: [ - "#fdfcfdff", - "#faf9fbff", - "#f2eff3ff", - "#eae7ecff", - "#e3dfe6ff", - "#dbd8e0ff", - "#d0cdd7ff", - "#bcbac7ff", - "#8e8c99ff", - "#84828eff", - "#65636dff", - "#211f26ff", - ], - light_alpha: [ - "#55005503", - "#2b005506", - "#30004010", - "#20003618", - "#20003820", - "#14003527", - "#10003332", - "#08003145", - "#05001d73", - "#0500197d", - "#0400119c", - "#020008e0", - ], - dark: [ - "#121113ff", - "#1a191bff", - "#232225ff", - "#2b292dff", - "#323035ff", - "#3c393fff", - "#49474eff", - "#625f69ff", - "#6f6d78ff", - "#7c7a85ff", - "#b5b2bcff", - "#eeeef0ff", - ], - dark_alpha: [ - "#00000000", - "#f5f4f609", - "#ebeaf814", - "#eee5f81d", - "#efe6fe25", - "#f1e6fd30", - "#eee9ff40", - "#eee7ff5d", - "#eae6fd6e", - "#ece9fd7c", - "#f5f1ffb7", - "#fdfdffef", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn slate() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Slate", - light: [ - "#fcfcfdff", - "#f9f9fbff", - "#f0f0f3ff", - "#e8e8ecff", - "#e0e1e6ff", - "#d9d9e0ff", - "#cdced6ff", - "#b9bbc6ff", - "#8b8d98ff", - "#80838dff", - "#60646cff", - "#1c2024ff", - ], - light_alpha: [ - "#00005503", - "#00005506", - "#0000330f", - "#00002d17", - "#0009321f", - "#00002f26", - "#00062e32", - "#00083046", - "#00051d74", - "#00071b7f", - "#0007149f", - "#000509e3", - ], - dark: [ - "#111113ff", - "#18191bff", - "#212225ff", - "#272a2dff", - "#2e3135ff", - "#363a3fff", - "#43484eff", - "#5a6169ff", - "#696e77ff", - "#777b84ff", - "#b0b4baff", - "#edeef0ff", - ], - dark_alpha: [ - "#00000000", - "#d8f4f609", - "#ddeaf814", - "#d3edf81d", - "#d9edfe25", - "#d6ebfd30", - "#d9edff40", - "#d9edff5d", - "#dfebfd6d", - "#e5edfd7b", - "#f1f7feb5", - "#fcfdffef", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn sage() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Sage", - light: [ - "#fbfdfcff", - "#f7f9f8ff", - "#eef1f0ff", - "#e6e9e8ff", - "#dfe2e0ff", - "#d7dad9ff", - "#cbcfcdff", - "#b8bcbaff", - "#868e8bff", - "#7c8481ff", - "#5f6563ff", - "#1a211eff", - ], - light_alpha: [ - "#00804004", - "#00402008", - "#002d1e11", - "#001f1519", - "#00180820", - "#00140d28", - "#00140a34", - "#000f0847", - "#00110b79", - "#00100a83", - "#000a07a0", - "#000805e5", - ], - dark: [ - "#101211ff", - "#171918ff", - "#202221ff", - "#272a29ff", - "#2e3130ff", - "#373b39ff", - "#444947ff", - "#5b625fff", - "#63706bff", - "#717d79ff", - "#adb5b2ff", - "#eceeedff", - ], - dark_alpha: [ - "#00000000", - "#f0f2f108", - "#f3f5f412", - "#f2fefd1a", - "#f1fbfa22", - "#edfbf42d", - "#edfcf73c", - "#ebfdf657", - "#dffdf266", - "#e5fdf674", - "#f4fefbb0", - "#fdfffeed", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn olive() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Olive", - light: [ - "#fcfdfcff", - "#f8faf8ff", - "#eff1efff", - "#e7e9e7ff", - "#dfe2dfff", - "#d7dad7ff", - "#cccfccff", - "#b9bcb8ff", - "#898e87ff", - "#7f847dff", - "#60655fff", - "#1d211cff", - ], - light_alpha: [ - "#00550003", - "#00490007", - "#00200010", - "#00160018", - "#00180020", - "#00140028", - "#000f0033", - "#040f0047", - "#050f0078", - "#040e0082", - "#020a00a0", - "#010600e3", - ], - dark: [ - "#111210ff", - "#181917ff", - "#212220ff", - "#282a27ff", - "#2f312eff", - "#383a36ff", - "#454843ff", - "#5c625bff", - "#687066ff", - "#767d74ff", - "#afb5adff", - "#eceeecff", - ], - dark_alpha: [ - "#00000000", - "#f1f2f008", - "#f4f5f312", - "#f3fef21a", - "#f2fbf122", - "#f4faed2c", - "#f2fced3b", - "#edfdeb57", - "#ebfde766", - "#f0fdec74", - "#f6fef4b0", - "#fdfffded", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn sand() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Sand", - light: [ - "#fdfdfcff", - "#f9f9f8ff", - "#f1f0efff", - "#e9e8e6ff", - "#e2e1deff", - "#dad9d6ff", - "#cfcecaff", - "#bcbbb5ff", - "#8d8d86ff", - "#82827cff", - "#63635eff", - "#21201cff", - ], - light_alpha: [ - "#55550003", - "#25250007", - "#20100010", - "#1f150019", - "#1f180021", - "#19130029", - "#19140035", - "#1915014a", - "#0f0f0079", - "#0c0c0083", - "#080800a1", - "#060500e3", - ], - dark: [ - "#111110ff", - "#191918ff", - "#222221ff", - "#2a2a28ff", - "#31312eff", - "#3b3a37ff", - "#494844ff", - "#62605bff", - "#6f6d66ff", - "#7c7b74ff", - "#b5b3adff", - "#eeeeecff", - ], - dark_alpha: [ - "#00000000", - "#f4f4f309", - "#f6f6f513", - "#fefef31b", - "#fbfbeb23", - "#fffaed2d", - "#fffbed3c", - "#fff9eb57", - "#fffae965", - "#fffdee73", - "#fffcf4b0", - "#fffffded", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn gold() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Gold", - light: [ - "#fdfdfcff", - "#faf9f2ff", - "#f2f0e7ff", - "#eae6dbff", - "#e1dccfff", - "#d8d0bfff", - "#cbc0aaff", - "#b9a88dff", - "#978365ff", - "#8c7a5eff", - "#71624bff", - "#3b352bff", - ], - light_alpha: [ - "#55550003", - "#9d8a000d", - "#75600018", - "#6b4e0024", - "#60460030", - "#64440040", - "#63420055", - "#633d0072", - "#5332009a", - "#492d00a1", - "#362100b4", - "#130c00d4", - ], - dark: [ - "#121211ff", - "#1b1a17ff", - "#24231fff", - "#2d2b26ff", - "#38352eff", - "#444039ff", - "#544f46ff", - "#696256ff", - "#978365ff", - "#a39073ff", - "#cbb99fff", - "#e8e2d9ff", - ], - dark_alpha: [ - "#91911102", - "#f9e29d0b", - "#f8ecbb15", - "#ffeec41e", - "#feecc22a", - "#feebcb37", - "#ffedcd48", - "#fdeaca5f", - "#ffdba690", - "#fedfb09d", - "#fee7c6c8", - "#fef7ede7", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn bronze() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Bronze", - light: [ - "#fdfcfcff", - "#fdf7f5ff", - "#f6edeaff", - "#efe4dfff", - "#e7d9d3ff", - "#dfcdc5ff", - "#d3bcb3ff", - "#c2a499ff", - "#a18072ff", - "#957468ff", - "#7d5e54ff", - "#43302bff", - ], - light_alpha: [ - "#55000003", - "#cc33000a", - "#92250015", - "#80280020", - "#7423002c", - "#7324003a", - "#6c1f004c", - "#671c0066", - "#551a008d", - "#4c150097", - "#3d0f00ab", - "#1d0600d4", - ], - dark: [ - "#141110ff", - "#1c1917ff", - "#262220ff", - "#302a27ff", - "#3b3330ff", - "#493e3aff", - "#5a4c47ff", - "#6f5f58ff", - "#a18072ff", - "#ae8c7eff", - "#d4b3a5ff", - "#ede0d9ff", - ], - dark_alpha: [ - "#d1110004", - "#fbbc910c", - "#faceb817", - "#facdb622", - "#ffd2c12d", - "#ffd1c03c", - "#fdd0c04f", - "#ffd6c565", - "#fec7b09b", - "#fecab5a9", - "#ffd7c6d1", - "#fff1e9ec", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn brown() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Brown", - light: [ - "#fefdfcff", - "#fcf9f6ff", - "#f6eee7ff", - "#f0e4d9ff", - "#ebdacaff", - "#e4cdb7ff", - "#dcbc9fff", - "#cea37eff", - "#ad7f58ff", - "#a07553ff", - "#815e46ff", - "#3e332eff", - ], - light_alpha: [ - "#aa550003", - "#aa550009", - "#a04b0018", - "#9b4a0026", - "#9f4d0035", - "#a04e0048", - "#a34e0060", - "#9f4a0081", - "#823c00a7", - "#723300ac", - "#522100b9", - "#140600d1", - ], - dark: [ - "#12110fff", - "#1c1816ff", - "#28211dff", - "#322922ff", - "#3e3128ff", - "#4d3c2fff", - "#614a39ff", - "#7c5f46ff", - "#ad7f58ff", - "#b88c67ff", - "#dbb594ff", - "#f2e1caff", - ], - dark_alpha: [ - "#91110002", - "#fba67c0c", - "#fcb58c19", - "#fbbb8a24", - "#fcb88931", - "#fdba8741", - "#ffbb8856", - "#ffbe8773", - "#feb87da8", - "#ffc18cb3", - "#fed1aad9", - "#feecd4f2", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn yellow() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Yellow", - light: [ - "#fdfdf9ff", - "#fefce9ff", - "#fffab8ff", - "#fff394ff", - "#ffe770ff", - "#f3d768ff", - "#e4c767ff", - "#d5ae39ff", - "#ffe629ff", - "#ffdc00ff", - "#9e6c00ff", - "#473b1fff", - ], - light_alpha: [ - "#aaaa0006", - "#f4dd0016", - "#ffee0047", - "#ffe3016b", - "#ffd5008f", - "#ebbc0097", - "#d2a10098", - "#c99700c6", - "#ffe100d6", - "#ffdc00ff", - "#9e6c00ff", - "#2e2000e0", - ], - dark: [ - "#14120bff", - "#1b180fff", - "#2d2305ff", - "#362b00ff", - "#433500ff", - "#524202ff", - "#665417ff", - "#836a21ff", - "#ffe629ff", - "#ffff57ff", - "#f5e147ff", - "#f6eeb4ff", - ], - dark_alpha: [ - "#d1510004", - "#f9b4000b", - "#ffaa001e", - "#fdb70028", - "#febb0036", - "#fec40046", - "#fdcb225c", - "#fdca327b", - "#ffe629ff", - "#ffff57ff", - "#fee949f5", - "#fef6baf6", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn amber() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Amber", - light: [ - "#fefdfbff", - "#fefbe9ff", - "#fff7c2ff", - "#ffee9cff", - "#fbe577ff", - "#f3d673ff", - "#e9c162ff", - "#e2a336ff", - "#ffc53dff", - "#ffba18ff", - "#ab6400ff", - "#4f3422ff", - ], - light_alpha: [ - "#c0800004", - "#f4d10016", - "#ffde003d", - "#ffd40063", - "#f8cf0088", - "#eab5008c", - "#dc9b009d", - "#da8a00c9", - "#ffb300c2", - "#ffb300e7", - "#ab6400ff", - "#341500dd", - ], - dark: [ - "#16120cff", - "#1d180fff", - "#302008ff", - "#3f2700ff", - "#4d3000ff", - "#5c3d05ff", - "#714f19ff", - "#8f6424ff", - "#ffc53dff", - "#ffd60aff", - "#ffca16ff", - "#ffe7b3ff", - ], - dark_alpha: [ - "#e63c0006", - "#fd9b000d", - "#fa820022", - "#fc820032", - "#fd8b0041", - "#fd9b0051", - "#ffab2567", - "#ffae3587", - "#ffc53dff", - "#ffd60aff", - "#ffca16ff", - "#ffe7b3ff", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn orange() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Orange", - light: [ - "#fefcfbff", - "#fff7edff", - "#ffefd6ff", - "#ffdfb5ff", - "#ffd19aff", - "#ffc182ff", - "#f5ae73ff", - "#ec9455ff", - "#f76b15ff", - "#ef5f00ff", - "#cc4e00ff", - "#582d1dff", - ], - light_alpha: [ - "#c0400004", - "#ff8e0012", - "#ff9c0029", - "#ff91014a", - "#ff8b0065", - "#ff81007d", - "#ed6c008c", - "#e35f00aa", - "#f65e00ea", - "#ef5f00ff", - "#cc4e00ff", - "#431200e2", - ], - dark: [ - "#17120eff", - "#1e160fff", - "#331e0bff", - "#462100ff", - "#562800ff", - "#66350cff", - "#7e451dff", - "#a35829ff", - "#f76b15ff", - "#ff801fff", - "#ffa057ff", - "#ffe0c2ff", - ], - dark_alpha: [ - "#ec360007", - "#fe6d000e", - "#fb6a0025", - "#ff590039", - "#ff61004a", - "#fd75045c", - "#ff832c75", - "#fe84389d", - "#fe6d15f7", - "#ff801fff", - "#ffa057ff", - "#ffe0c2ff", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn tomato() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Tomato", - light: [ - "#fffcfcff", - "#fff8f7ff", - "#feebe7ff", - "#ffdcd3ff", - "#ffcdc2ff", - "#fdbdafff", - "#f5a898ff", - "#ec8e7bff", - "#e54d2eff", - "#dd4425ff", - "#d13415ff", - "#5c271fff", - ], - light_alpha: [ - "#ff000003", - "#ff200008", - "#f52b0018", - "#ff35002c", - "#ff2e003d", - "#f92d0050", - "#e7280067", - "#db250084", - "#df2600d1", - "#d72400da", - "#cd2200ea", - "#460900e0", - ], - dark: [ - "#181111ff", - "#1f1513ff", - "#391714ff", - "#4e1511ff", - "#5e1c16ff", - "#6e2920ff", - "#853a2dff", - "#ac4d39ff", - "#e54d2eff", - "#ec6142ff", - "#ff977dff", - "#fbd3cbff", - ], - dark_alpha: [ - "#f1121208", - "#ff55330f", - "#ff35232b", - "#fd201142", - "#fe332153", - "#ff4f3864", - "#fd644a7d", - "#fe6d4ea7", - "#fe5431e4", - "#ff6847eb", - "#ff977dff", - "#ffd6cefb", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn red() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Red", - light: [ - "#fffcfcff", - "#fff7f7ff", - "#feebecff", - "#ffdbdcff", - "#ffcdceff", - "#fdbdbeff", - "#f4a9aaff", - "#eb8e90ff", - "#e5484dff", - "#dc3e42ff", - "#ce2c31ff", - "#641723ff", - ], - light_alpha: [ - "#ff000003", - "#ff000008", - "#f3000d14", - "#ff000824", - "#ff000632", - "#f8000442", - "#df000356", - "#d2000571", - "#db0007b7", - "#d10005c1", - "#c40006d3", - "#55000de8", - ], - dark: [ - "#191111ff", - "#201314ff", - "#3b1219ff", - "#500f1cff", - "#611623ff", - "#72232dff", - "#8c333aff", - "#b54548ff", - "#e5484dff", - "#ec5d5eff", - "#ff9592ff", - "#ffd1d9ff", - ], - dark_alpha: [ - "#f4121209", - "#f22f3e11", - "#ff173f2d", - "#fe0a3b44", - "#ff204756", - "#ff3e5668", - "#ff536184", - "#ff5d61b0", - "#fe4e54e4", - "#ff6465eb", - "#ff9592ff", - "#ffd1d9ff", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn ruby() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Ruby", - light: [ - "#fffcfdff", - "#fff7f8ff", - "#feeaedff", - "#ffdce1ff", - "#ffced6ff", - "#f8bfc8ff", - "#efacb8ff", - "#e592a3ff", - "#e54666ff", - "#dc3b5dff", - "#ca244dff", - "#64172bff", - ], - light_alpha: [ - "#ff005503", - "#ff002008", - "#f3002515", - "#ff002523", - "#ff002a31", - "#e4002440", - "#ce002553", - "#c300286d", - "#db002cb9", - "#d2002cc4", - "#c10030db", - "#550016e8", - ], - dark: [ - "#191113ff", - "#1e1517ff", - "#3a141eff", - "#4e1325ff", - "#5e1a2eff", - "#6f2539ff", - "#883447ff", - "#b3445aff", - "#e54666ff", - "#ec5a72ff", - "#ff949dff", - "#fed2e1ff", - ], - dark_alpha: [ - "#f4124a09", - "#fe5a7f0e", - "#ff235d2c", - "#fd195e42", - "#fe2d6b53", - "#ff447665", - "#ff577d80", - "#ff5c7cae", - "#fe4c70e4", - "#ff617beb", - "#ff949dff", - "#ffd3e2fe", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn crimson() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Crimson", - light: [ - "#fffcfdff", - "#fef7f9ff", - "#ffe9f0ff", - "#fedce7ff", - "#faceddff", - "#f3bed1ff", - "#eaacc3ff", - "#e093b2ff", - "#e93d82ff", - "#df3478ff", - "#cb1d63ff", - "#621639ff", - ], - light_alpha: [ - "#ff005503", - "#e0004008", - "#ff005216", - "#f8005123", - "#e5004f31", - "#d0004b41", - "#bf004753", - "#b6004a6c", - "#e2005bc2", - "#d70056cb", - "#c4004fe2", - "#530026e9", - ], - dark: [ - "#191114ff", - "#201318ff", - "#381525ff", - "#4d122fff", - "#5c1839ff", - "#6d2545ff", - "#873356ff", - "#b0436eff", - "#e93d82ff", - "#ee518aff", - "#ff92adff", - "#fdd3e8ff", - ], - dark_alpha: [ - "#f4126709", - "#f22f7a11", - "#fe2a8b2a", - "#fd158741", - "#fd278f51", - "#fe459763", - "#fd559b7f", - "#fe5b9bab", - "#fe418de8", - "#ff5693ed", - "#ff92adff", - "#ffd5eafd", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn pink() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Pink", - light: [ - "#fffcfeff", - "#fef7fbff", - "#fee9f5ff", - "#fbdcefff", - "#f6cee7ff", - "#efbfddff", - "#e7acd0ff", - "#dd93c2ff", - "#d6409fff", - "#cf3897ff", - "#c2298aff", - "#651249ff", - ], - light_alpha: [ - "#ff00aa03", - "#e0008008", - "#f4008c16", - "#e2008b23", - "#d1008331", - "#c0007840", - "#b6006f53", - "#af006f6c", - "#c8007fbf", - "#c2007ac7", - "#b60074d6", - "#59003bed", - ], - dark: [ - "#191117ff", - "#21121dff", - "#37172fff", - "#4b143dff", - "#591c47ff", - "#692955ff", - "#833869ff", - "#a84885ff", - "#d6409fff", - "#de51a8ff", - "#ff8dccff", - "#fdd1eaff", - ], - dark_alpha: [ - "#f412bc09", - "#f420bb12", - "#fe37cc29", - "#fc1ec43f", - "#fd35c24e", - "#fd51c75f", - "#fd62c87b", - "#ff68c8a2", - "#fe49bcd4", - "#ff5cc0dc", - "#ff8dccff", - "#ffd3ecfd", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn plum() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Plum", - light: [ - "#fefcffff", - "#fdf7fdff", - "#fbebfbff", - "#f7def8ff", - "#f2d1f3ff", - "#e9c2ecff", - "#deade3ff", - "#cf91d8ff", - "#ab4abaff", - "#a144afff", - "#953ea3ff", - "#53195dff", - ], - light_alpha: [ - "#aa00ff03", - "#c000c008", - "#cc00cc14", - "#c200c921", - "#b700bd2e", - "#a400b03d", - "#9900a852", - "#9000a56e", - "#89009eb5", - "#7f0092bb", - "#730086c1", - "#40004be6", - ], - dark: [ - "#181118ff", - "#201320ff", - "#351a35ff", - "#451d47ff", - "#512454ff", - "#5e3061ff", - "#734079ff", - "#92549cff", - "#ab4abaff", - "#b658c4ff", - "#e796f3ff", - "#f4d4f4ff", - ], - dark_alpha: [ - "#f112f108", - "#f22ff211", - "#fd4cfd27", - "#f646ff3a", - "#f455ff48", - "#f66dff56", - "#f07cfd70", - "#ee84ff95", - "#e961feb6", - "#ed70ffc0", - "#f19cfef3", - "#feddfef4", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn purple() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Purple", - light: [ - "#fefcfeff", - "#fbf7feff", - "#f7edfeff", - "#f2e2fcff", - "#ead5f9ff", - "#e0c4f4ff", - "#d1afecff", - "#be93e4ff", - "#8e4ec6ff", - "#8347b9ff", - "#8145b5ff", - "#402060ff", - ], - light_alpha: [ - "#aa00aa03", - "#8000e008", - "#8e00f112", - "#8d00e51d", - "#8000db2a", - "#7a01d03b", - "#6d00c350", - "#6600c06c", - "#5c00adb1", - "#53009eb8", - "#52009aba", - "#250049df", - ], - dark: [ - "#18111bff", - "#1e1523ff", - "#301c3bff", - "#3d224eff", - "#48295cff", - "#54346bff", - "#664282ff", - "#8457aaff", - "#8e4ec6ff", - "#9a5cd0ff", - "#d19dffff", - "#ecd9faff", - ], - dark_alpha: [ - "#b412f90b", - "#b744f714", - "#c150ff2d", - "#bb53fd42", - "#be5cfd51", - "#c16dfd61", - "#c378fd7a", - "#c47effa4", - "#b661ffc2", - "#bc6fffcd", - "#d19dffff", - "#f1ddfffa", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn violet() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Violet", - light: [ - "#fdfcfeff", - "#faf8ffff", - "#f4f0feff", - "#ebe4ffff", - "#e1d9ffff", - "#d4cafeff", - "#c2b5f5ff", - "#aa99ecff", - "#6e56cfff", - "#654dc4ff", - "#6550b9ff", - "#2f265fff", - ], - light_alpha: [ - "#5500aa03", - "#4900ff07", - "#4400ee0f", - "#4300ff1b", - "#3600ff26", - "#3100fb35", - "#2d01dd4a", - "#2b00d066", - "#2400b7a9", - "#2300abb2", - "#1f0099af", - "#0b0043d9", - ], - dark: [ - "#14121fff", - "#1b1525ff", - "#291f43ff", - "#33255bff", - "#3c2e69ff", - "#473876ff", - "#56468bff", - "#6958adff", - "#6e56cfff", - "#7d66d9ff", - "#baa7ffff", - "#e2ddfeff", - ], - dark_alpha: [ - "#4422ff0f", - "#853ff916", - "#8354fe36", - "#7d51fd50", - "#845ffd5f", - "#8f6cfd6d", - "#9879ff83", - "#977dfea8", - "#8668ffcc", - "#9176fed7", - "#baa7ffff", - "#e3defffe", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn iris() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Iris", - light: [ - "#fdfdffff", - "#f8f8ffff", - "#f0f1feff", - "#e6e7ffff", - "#dadcffff", - "#cbcdffff", - "#b8baf8ff", - "#9b9ef0ff", - "#5b5bd6ff", - "#5151cdff", - "#5753c6ff", - "#272962ff", - ], - light_alpha: [ - "#0000ff02", - "#0000ff07", - "#0011ee0f", - "#000bff19", - "#000eff25", - "#000aff34", - "#0008e647", - "#0008d964", - "#0000c0a4", - "#0000b6ae", - "#0600abac", - "#000246d8", - ], - dark: [ - "#13131eff", - "#171625ff", - "#202248ff", - "#262a65ff", - "#303374ff", - "#3d3e82ff", - "#4a4a95ff", - "#5958b1ff", - "#5b5bd6ff", - "#6e6adeff", - "#b1a9ffff", - "#e0dffeff", - ], - dark_alpha: [ - "#3636fe0e", - "#564bf916", - "#525bff3b", - "#4d58ff5a", - "#5b62fd6b", - "#6d6ffd7a", - "#7777fe8e", - "#7b7afeac", - "#6a6afed4", - "#7d79ffdc", - "#b1a9ffff", - "#e1e0fffe", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn indigo() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Indigo", - light: [ - "#fdfdfeff", - "#f7f9ffff", - "#edf2feff", - "#e1e9ffff", - "#d2deffff", - "#c1d0ffff", - "#abbdf9ff", - "#8da4efff", - "#3e63ddff", - "#3358d4ff", - "#3a5bc7ff", - "#1f2d5cff", - ], - light_alpha: [ - "#00008002", - "#0040ff08", - "#0047f112", - "#0044ff1e", - "#0044ff2d", - "#003eff3e", - "#0037ed54", - "#0034dc72", - "#0031d2c1", - "#002ec9cc", - "#002bb7c5", - "#001046e0", - ], - dark: [ - "#11131fff", - "#141726ff", - "#182449ff", - "#1d2e62ff", - "#253974ff", - "#304384ff", - "#3a4f97ff", - "#435db1ff", - "#3e63ddff", - "#5472e4ff", - "#9eb1ffff", - "#d6e1ffff", - ], - dark_alpha: [ - "#1133ff0f", - "#3354fa17", - "#2f62ff3c", - "#3566ff57", - "#4171fd6b", - "#5178fd7c", - "#5a7fff90", - "#5b81feac", - "#4671ffdb", - "#5c7efee3", - "#9eb1ffff", - "#d6e1ffff", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn blue() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Blue", - light: [ - "#fbfdffff", - "#f4faffff", - "#e6f4feff", - "#d5efffff", - "#c2e5ffff", - "#acd8fcff", - "#8ec8f6ff", - "#5eb1efff", - "#0090ffff", - "#0588f0ff", - "#0d74ceff", - "#113264ff", - ], - light_alpha: [ - "#0080ff04", - "#008cff0b", - "#008ff519", - "#009eff2a", - "#0093ff3d", - "#0088f653", - "#0083eb71", - "#0084e6a1", - "#0090ffff", - "#0086f0fa", - "#006dcbf2", - "#002359ee", - ], - dark: [ - "#0d1520ff", - "#111927ff", - "#0d2847ff", - "#003362ff", - "#004074ff", - "#104d87ff", - "#205d9eff", - "#2870bdff", - "#0090ffff", - "#3b9effff", - "#70b8ffff", - "#c2e6ffff", - ], - dark_alpha: [ - "#004df211", - "#1166fb18", - "#0077ff3a", - "#0075ff57", - "#0081fd6b", - "#0f89fd7f", - "#2a91fe98", - "#3094feb9", - "#0090ffff", - "#3b9effff", - "#70b8ffff", - "#c2e6ffff", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn cyan() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Cyan", - light: [ - "#fafdfeff", - "#f2fafbff", - "#def7f9ff", - "#caf1f6ff", - "#b5e9f0ff", - "#9ddde7ff", - "#7dcedcff", - "#3db9cfff", - "#00a2c7ff", - "#0797b9ff", - "#107d98ff", - "#0d3c48ff", - ], - light_alpha: [ - "#0099cc05", - "#009db10d", - "#00c2d121", - "#00bcd435", - "#01b4cc4a", - "#00a7c162", - "#009fbb82", - "#00a3c0c2", - "#00a2c7ff", - "#0094b7f8", - "#007491ef", - "#00323ef2", - ], - dark: [ - "#0b161aff", - "#101b20ff", - "#082c36ff", - "#003848ff", - "#004558ff", - "#045468ff", - "#12677eff", - "#11809cff", - "#00a2c7ff", - "#23afd0ff", - "#4ccce6ff", - "#b6ecf7ff", - ], - dark_alpha: [ - "#0091f70a", - "#02a7f211", - "#00befd28", - "#00baff3b", - "#00befd4d", - "#00c7fd5e", - "#14cdff75", - "#11cfff95", - "#00cfffc3", - "#28d6ffcd", - "#52e1fee5", - "#bbf3fef7", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn teal() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Teal", - light: [ - "#fafefdff", - "#f3fbf9ff", - "#e0f8f3ff", - "#ccf3eaff", - "#b8eae0ff", - "#a1ded2ff", - "#83cdc1ff", - "#53b9abff", - "#12a594ff", - "#0d9b8aff", - "#008573ff", - "#0d3d38ff", - ], - light_alpha: [ - "#00cc9905", - "#00aa800c", - "#00c69d1f", - "#00c39633", - "#00b49047", - "#00a6855e", - "#0099807c", - "#009783ac", - "#009e8ced", - "#009684f2", - "#008573ff", - "#00332df2", - ], - dark: [ - "#0d1514ff", - "#111c1bff", - "#0d2d2aff", - "#023b37ff", - "#084843ff", - "#145750ff", - "#1c6961ff", - "#207e73ff", - "#12a594ff", - "#0eb39eff", - "#0bd8b6ff", - "#adf0ddff", - ], - dark_alpha: [ - "#00deab05", - "#12fbe60c", - "#00ffe61e", - "#00ffe92d", - "#00ffea3b", - "#1cffe84b", - "#2efde85f", - "#32ffe775", - "#13ffe49f", - "#0dffe0ae", - "#0afed5d6", - "#b8ffebef", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn jade() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Jade", - light: [ - "#fbfefdff", - "#f4fbf7ff", - "#e6f7edff", - "#d6f1e3ff", - "#c3e9d7ff", - "#acdec8ff", - "#8bceb6ff", - "#56ba9fff", - "#29a383ff", - "#26997bff", - "#208368ff", - "#1d3b31ff", - ], - light_alpha: [ - "#00c08004", - "#00a3460b", - "#00ae4819", - "#00a85129", - "#00a2553c", - "#009a5753", - "#00945f74", - "#00976ea9", - "#00916bd6", - "#008764d9", - "#007152df", - "#002217e2", - ], - dark: [ - "#0d1512ff", - "#121c18ff", - "#0f2e22ff", - "#0b3b2cff", - "#114837ff", - "#1b5745ff", - "#246854ff", - "#2a7e68ff", - "#29a383ff", - "#27b08bff", - "#1fd8a4ff", - "#adf0d4ff", - ], - dark_alpha: [ - "#00de4505", - "#27fba60c", - "#02f99920", - "#00ffaa2d", - "#11ffb63b", - "#34ffc24b", - "#45fdc75e", - "#48ffcf75", - "#38feca9d", - "#31fec7ab", - "#21fec0d6", - "#b8ffe1ef", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn green() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Green", - light: [ - "#fbfefcff", - "#f4fbf6ff", - "#e6f6ebff", - "#d6f1dfff", - "#c4e8d1ff", - "#adddc0ff", - "#8eceaaff", - "#5bb98bff", - "#30a46cff", - "#2b9a66ff", - "#218358ff", - "#193b2dff", - ], - light_alpha: [ - "#00c04004", - "#00a32f0b", - "#00a43319", - "#00a83829", - "#019c393b", - "#00963c52", - "#00914071", - "#00924ba4", - "#008f4acf", - "#008647d4", - "#00713fde", - "#002616e6", - ], - dark: [ - "#0e1512ff", - "#121b17ff", - "#132d21ff", - "#113b29ff", - "#174933ff", - "#20573eff", - "#28684aff", - "#2f7c57ff", - "#30a46cff", - "#33b074ff", - "#3dd68cff", - "#b1f1cbff", - ], - dark_alpha: [ - "#00de4505", - "#29f99d0b", - "#22ff991e", - "#11ff992d", - "#2bffa23c", - "#44ffaa4b", - "#50fdac5e", - "#54ffad73", - "#44ffa49e", - "#43fea4ab", - "#46fea5d4", - "#bbffd7f0", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn grass() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Grass", - light: [ - "#fbfefbff", - "#f5fbf5ff", - "#e9f6e9ff", - "#daf1dbff", - "#c9e8caff", - "#b2ddb5ff", - "#94ce9aff", - "#65ba74ff", - "#46a758ff", - "#3e9b4fff", - "#2a7e3bff", - "#203c25ff", - ], - light_alpha: [ - "#00c00004", - "#0099000a", - "#00970016", - "#009f0725", - "#00930536", - "#008f0a4d", - "#018b0f6b", - "#008d199a", - "#008619b9", - "#007b17c1", - "#006514d5", - "#002006df", - ], - dark: [ - "#0e1511ff", - "#141a15ff", - "#1b2a1eff", - "#1d3a24ff", - "#25482dff", - "#2d5736ff", - "#366740ff", - "#3e7949ff", - "#46a758ff", - "#53b365ff", - "#71d083ff", - "#c2f0c2ff", - ], - dark_alpha: [ - "#00de1205", - "#5ef7780a", - "#70fe8c1b", - "#57ff802c", - "#68ff8b3b", - "#71ff8f4b", - "#77fd925d", - "#77fd9070", - "#65ff82a1", - "#72ff8dae", - "#89ff9fcd", - "#ceffceef", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn lime() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Lime", - light: [ - "#fcfdfaff", - "#f8faf3ff", - "#eef6d6ff", - "#e2f0bdff", - "#d3e7a6ff", - "#c2da91ff", - "#abc978ff", - "#8db654ff", - "#bdee63ff", - "#b0e64cff", - "#5c7c2fff", - "#37401cff", - ], - light_alpha: [ - "#66990005", - "#6b95000c", - "#96c80029", - "#8fc60042", - "#81bb0059", - "#72aa006e", - "#61990087", - "#559200ab", - "#93e4009c", - "#8fdc00b3", - "#375f00d0", - "#1e2900e3", - ], - dark: [ - "#11130cff", - "#151a10ff", - "#1f2917ff", - "#29371dff", - "#334423ff", - "#3d522aff", - "#496231ff", - "#577538ff", - "#bdee63ff", - "#d4ff70ff", - "#bde56cff", - "#e3f7baff", - ], - dark_alpha: [ - "#11bb0003", - "#78f7000a", - "#9bfd4c1a", - "#a7fe5c29", - "#affe6537", - "#b2fe6d46", - "#b6ff6f57", - "#b6fd6d6c", - "#caff69ed", - "#d4ff70ff", - "#d1fe77e4", - "#e9febff7", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn mint() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Mint", - light: [ - "#f9fefdff", - "#f2fbf9ff", - "#ddf9f2ff", - "#c8f4e9ff", - "#b3ecdeff", - "#9ce0d0ff", - "#7ecfbdff", - "#4cbba5ff", - "#86ead4ff", - "#7de0cbff", - "#027864ff", - "#16433cff", - ], - light_alpha: [ - "#00d5aa06", - "#00b18a0d", - "#00d29e22", - "#00cc9937", - "#00c0914c", - "#00b08663", - "#00a17d81", - "#009e7fb3", - "#00d3a579", - "#00c39982", - "#007763fd", - "#00312ae9", - ], - dark: [ - "#0e1515ff", - "#0f1b1bff", - "#092c2bff", - "#003a38ff", - "#004744ff", - "#105650ff", - "#1e685fff", - "#277f70ff", - "#86ead4ff", - "#a8f5e5ff", - "#58d5baff", - "#c4f5e1ff", - ], - dark_alpha: [ - "#00dede05", - "#00f9f90b", - "#00fff61d", - "#00fff42c", - "#00fff23a", - "#0effeb4a", - "#34fde55e", - "#41ffdf76", - "#92ffe7e9", - "#aefeedf5", - "#67ffded2", - "#cbfee9f5", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn sky() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Sky", - light: [ - "#f9feffff", - "#f1fafdff", - "#e1f6fdff", - "#d1f0faff", - "#bee7f5ff", - "#a9daedff", - "#8dcae3ff", - "#60b3d7ff", - "#7ce2feff", - "#74daf8ff", - "#00749eff", - "#1d3e56ff", - ], - light_alpha: [ - "#00d5ff06", - "#00a4db0e", - "#00b3ee1e", - "#00ace42e", - "#00a1d841", - "#0092ca56", - "#0089c172", - "#0085bf9f", - "#00c7fe83", - "#00bcf38b", - "#00749eff", - "#002540e2", - ], - dark: [ - "#0d141fff", - "#111a27ff", - "#112840ff", - "#113555ff", - "#154467ff", - "#1b537bff", - "#1f6692ff", - "#197caeff", - "#7ce2feff", - "#a8eeffff", - "#75c7f0ff", - "#c2f3ffff", - ], - dark_alpha: [ - "#0044ff0f", - "#1171fb18", - "#1184fc33", - "#128fff49", - "#1c9dfd5d", - "#28a5ff72", - "#2badfe8b", - "#1db2fea9", - "#7ce3fffe", - "#a8eeffff", - "#7cd3ffef", - "#c2f3ffff", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn black() -> ColorScaleSet { - StaticColorScaleSet { - scale: "Black", - light: [ - "#0000000d", - "#0000001a", - "#00000026", - "#00000033", - "#0000004d", - "#00000066", - "#00000080", - "#00000099", - "#000000b3", - "#000000cc", - "#000000e6", - "#000000f2", - ], - light_alpha: [ - "#0000000d", - "#0000001a", - "#00000026", - "#00000033", - "#0000004d", - "#00000066", - "#00000080", - "#00000099", - "#000000b3", - "#000000cc", - "#000000e6", - "#000000f2", - ], - dark: [ - "#0000000d", - "#0000001a", - "#00000026", - "#00000033", - "#0000004d", - "#00000066", - "#00000080", - "#00000099", - "#000000b3", - "#000000cc", - "#000000e6", - "#000000f2", - ], - dark_alpha: [ - "#0000000d", - "#0000001a", - "#00000026", - "#00000033", - "#0000004d", - "#00000066", - "#00000080", - "#00000099", - "#000000b3", - "#000000cc", - "#000000e6", - "#000000f2", - ], - } - .try_into() - .unwrap() -} - -pub(crate) fn white() -> ColorScaleSet { - StaticColorScaleSet { - scale: "White", - light: [ - "#ffffff0d", - "#ffffff1a", - "#ffffff26", - "#ffffff33", - "#ffffff4d", - "#ffffff66", - "#ffffff80", - "#ffffff99", - "#ffffffb3", - "#ffffffcc", - "#ffffffe6", - "#fffffff2", - ], - light_alpha: [ - "#ffffff0d", - "#ffffff1a", - "#ffffff26", - "#ffffff33", - "#ffffff4d", - "#ffffff66", - "#ffffff80", - "#ffffff99", - "#ffffffb3", - "#ffffffcc", - "#ffffffe6", - "#fffffff2", - ], - dark: [ - "#ffffff0d", - "#ffffff1a", - "#ffffff26", - "#ffffff33", - "#ffffff4d", - "#ffffff66", - "#ffffff80", - "#ffffff99", - "#ffffffb3", - "#ffffffcc", - "#ffffffe6", - "#fffffff2", - ], - dark_alpha: [ - "#ffffff0d", - "#ffffff1a", - "#ffffff26", - "#ffffff33", - "#ffffff4d", - "#ffffff66", - "#ffffff80", - "#ffffff99", - "#ffffffb3", - "#ffffffcc", - "#ffffffe6", - "#fffffff2", - ], - } - .try_into() - .unwrap() -} +#![allow(dead_code)] + +use gpui::{hsla, Hsla, Rgba}; + +use crate::scale::{ColorScale, ColorScaleSet, ColorScales}; + +pub(crate) fn neutral() -> ColorScaleSet { + gray() +} + +pub(crate) fn brand() -> ColorScaleSet { + yellow() +} + +pub(crate) fn danger() -> ColorScaleSet { + tomato() +} + +pub(crate) fn warning() -> ColorScaleSet { + orange() +} + +/// Make a [gpui::Hsla] color. +/// +/// - h: 0..360.0 +/// - s: 0.0..100.0 +/// - l: 0.0..100.0 +pub fn hsl(h: f32, s: f32, l: f32) -> Hsla { + hsla(h / 360., s / 100.0, l / 100.0, 1.0) +} + +type StaticColorScale = [&'static str; 12]; + +struct StaticColorScaleSet { + scale: &'static str, + light: StaticColorScale, + light_alpha: StaticColorScale, + dark: StaticColorScale, + dark_alpha: StaticColorScale, +} + +impl TryFrom for ColorScaleSet { + type Error = anyhow::Error; + + fn try_from(value: StaticColorScaleSet) -> Result { + fn to_color_scale(scale: StaticColorScale) -> Result { + scale + .into_iter() + .map(|color| Rgba::try_from(color).map(Hsla::from)) + .collect::, _>>() + .map(ColorScale::from_iter) + } + + Ok(Self::new( + value.scale, + to_color_scale(value.light)?, + to_color_scale(value.light_alpha)?, + to_color_scale(value.dark)?, + to_color_scale(value.dark_alpha)?, + )) + } +} + +/// Color scales used to build the default themes. +pub fn default_color_scales() -> ColorScales { + ColorScales { + gray: gray(), + mauve: mauve(), + slate: slate(), + sage: sage(), + olive: olive(), + sand: sand(), + gold: gold(), + bronze: bronze(), + brown: brown(), + yellow: yellow(), + amber: amber(), + orange: orange(), + tomato: tomato(), + red: red(), + ruby: ruby(), + crimson: crimson(), + pink: pink(), + plum: plum(), + purple: purple(), + violet: violet(), + iris: iris(), + indigo: indigo(), + blue: blue(), + cyan: cyan(), + teal: teal(), + jade: jade(), + green: green(), + grass: grass(), + lime: lime(), + mint: mint(), + sky: sky(), + black: black(), + white: white(), + } +} + +pub(crate) fn gray() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Gray", + light: [ + "#fcfcfcff", + "#f9f9f9ff", + "#f0f0f0ff", + "#e8e8e8ff", + "#e0e0e0ff", + "#d9d9d9ff", + "#cececeff", + "#bbbbbbff", + "#8d8d8dff", + "#838383ff", + "#646464ff", + "#202020ff", + ], + light_alpha: [ + "#00000003", + "#00000006", + "#0000000f", + "#00000017", + "#0000001f", + "#00000026", + "#00000031", + "#00000044", + "#00000072", + "#0000007c", + "#0000009b", + "#000000df", + ], + dark: [ + "#111111ff", + "#191919ff", + "#222222ff", + "#2a2a2aff", + "#313131ff", + "#3a3a3aff", + "#484848ff", + "#606060ff", + "#6e6e6eff", + "#7b7b7bff", + "#b4b4b4ff", + "#eeeeeeff", + ], + dark_alpha: [ + "#00000000", + "#ffffff09", + "#ffffff12", + "#ffffff1b", + "#ffffff22", + "#ffffff2c", + "#ffffff3b", + "#ffffff55", + "#ffffff64", + "#ffffff72", + "#ffffffaf", + "#ffffffed", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn mauve() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Mauve", + light: [ + "#fdfcfdff", + "#faf9fbff", + "#f2eff3ff", + "#eae7ecff", + "#e3dfe6ff", + "#dbd8e0ff", + "#d0cdd7ff", + "#bcbac7ff", + "#8e8c99ff", + "#84828eff", + "#65636dff", + "#211f26ff", + ], + light_alpha: [ + "#55005503", + "#2b005506", + "#30004010", + "#20003618", + "#20003820", + "#14003527", + "#10003332", + "#08003145", + "#05001d73", + "#0500197d", + "#0400119c", + "#020008e0", + ], + dark: [ + "#121113ff", + "#1a191bff", + "#232225ff", + "#2b292dff", + "#323035ff", + "#3c393fff", + "#49474eff", + "#625f69ff", + "#6f6d78ff", + "#7c7a85ff", + "#b5b2bcff", + "#eeeef0ff", + ], + dark_alpha: [ + "#00000000", + "#f5f4f609", + "#ebeaf814", + "#eee5f81d", + "#efe6fe25", + "#f1e6fd30", + "#eee9ff40", + "#eee7ff5d", + "#eae6fd6e", + "#ece9fd7c", + "#f5f1ffb7", + "#fdfdffef", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn slate() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Slate", + light: [ + "#fcfcfdff", + "#f9f9fbff", + "#f0f0f3ff", + "#e8e8ecff", + "#e0e1e6ff", + "#d9d9e0ff", + "#cdced6ff", + "#b9bbc6ff", + "#8b8d98ff", + "#80838dff", + "#60646cff", + "#1c2024ff", + ], + light_alpha: [ + "#00005503", + "#00005506", + "#0000330f", + "#00002d17", + "#0009321f", + "#00002f26", + "#00062e32", + "#00083046", + "#00051d74", + "#00071b7f", + "#0007149f", + "#000509e3", + ], + dark: [ + "#111113ff", + "#18191bff", + "#212225ff", + "#272a2dff", + "#2e3135ff", + "#363a3fff", + "#43484eff", + "#5a6169ff", + "#696e77ff", + "#777b84ff", + "#b0b4baff", + "#edeef0ff", + ], + dark_alpha: [ + "#00000000", + "#d8f4f609", + "#ddeaf814", + "#d3edf81d", + "#d9edfe25", + "#d6ebfd30", + "#d9edff40", + "#d9edff5d", + "#dfebfd6d", + "#e5edfd7b", + "#f1f7feb5", + "#fcfdffef", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn sage() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Sage", + light: [ + "#fbfdfcff", + "#f7f9f8ff", + "#eef1f0ff", + "#e6e9e8ff", + "#dfe2e0ff", + "#d7dad9ff", + "#cbcfcdff", + "#b8bcbaff", + "#868e8bff", + "#7c8481ff", + "#5f6563ff", + "#1a211eff", + ], + light_alpha: [ + "#00804004", + "#00402008", + "#002d1e11", + "#001f1519", + "#00180820", + "#00140d28", + "#00140a34", + "#000f0847", + "#00110b79", + "#00100a83", + "#000a07a0", + "#000805e5", + ], + dark: [ + "#101211ff", + "#171918ff", + "#202221ff", + "#272a29ff", + "#2e3130ff", + "#373b39ff", + "#444947ff", + "#5b625fff", + "#63706bff", + "#717d79ff", + "#adb5b2ff", + "#eceeedff", + ], + dark_alpha: [ + "#00000000", + "#f0f2f108", + "#f3f5f412", + "#f2fefd1a", + "#f1fbfa22", + "#edfbf42d", + "#edfcf73c", + "#ebfdf657", + "#dffdf266", + "#e5fdf674", + "#f4fefbb0", + "#fdfffeed", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn olive() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Olive", + light: [ + "#fcfdfcff", + "#f8faf8ff", + "#eff1efff", + "#e7e9e7ff", + "#dfe2dfff", + "#d7dad7ff", + "#cccfccff", + "#b9bcb8ff", + "#898e87ff", + "#7f847dff", + "#60655fff", + "#1d211cff", + ], + light_alpha: [ + "#00550003", + "#00490007", + "#00200010", + "#00160018", + "#00180020", + "#00140028", + "#000f0033", + "#040f0047", + "#050f0078", + "#040e0082", + "#020a00a0", + "#010600e3", + ], + dark: [ + "#111210ff", + "#181917ff", + "#212220ff", + "#282a27ff", + "#2f312eff", + "#383a36ff", + "#454843ff", + "#5c625bff", + "#687066ff", + "#767d74ff", + "#afb5adff", + "#eceeecff", + ], + dark_alpha: [ + "#00000000", + "#f1f2f008", + "#f4f5f312", + "#f3fef21a", + "#f2fbf122", + "#f4faed2c", + "#f2fced3b", + "#edfdeb57", + "#ebfde766", + "#f0fdec74", + "#f6fef4b0", + "#fdfffded", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn sand() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Sand", + light: [ + "#fdfdfcff", + "#f9f9f8ff", + "#f1f0efff", + "#e9e8e6ff", + "#e2e1deff", + "#dad9d6ff", + "#cfcecaff", + "#bcbbb5ff", + "#8d8d86ff", + "#82827cff", + "#63635eff", + "#21201cff", + ], + light_alpha: [ + "#55550003", + "#25250007", + "#20100010", + "#1f150019", + "#1f180021", + "#19130029", + "#19140035", + "#1915014a", + "#0f0f0079", + "#0c0c0083", + "#080800a1", + "#060500e3", + ], + dark: [ + "#111110ff", + "#191918ff", + "#222221ff", + "#2a2a28ff", + "#31312eff", + "#3b3a37ff", + "#494844ff", + "#62605bff", + "#6f6d66ff", + "#7c7b74ff", + "#b5b3adff", + "#eeeeecff", + ], + dark_alpha: [ + "#00000000", + "#f4f4f309", + "#f6f6f513", + "#fefef31b", + "#fbfbeb23", + "#fffaed2d", + "#fffbed3c", + "#fff9eb57", + "#fffae965", + "#fffdee73", + "#fffcf4b0", + "#fffffded", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn gold() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Gold", + light: [ + "#fdfdfcff", + "#faf9f2ff", + "#f2f0e7ff", + "#eae6dbff", + "#e1dccfff", + "#d8d0bfff", + "#cbc0aaff", + "#b9a88dff", + "#978365ff", + "#8c7a5eff", + "#71624bff", + "#3b352bff", + ], + light_alpha: [ + "#55550003", + "#9d8a000d", + "#75600018", + "#6b4e0024", + "#60460030", + "#64440040", + "#63420055", + "#633d0072", + "#5332009a", + "#492d00a1", + "#362100b4", + "#130c00d4", + ], + dark: [ + "#121211ff", + "#1b1a17ff", + "#24231fff", + "#2d2b26ff", + "#38352eff", + "#444039ff", + "#544f46ff", + "#696256ff", + "#978365ff", + "#a39073ff", + "#cbb99fff", + "#e8e2d9ff", + ], + dark_alpha: [ + "#91911102", + "#f9e29d0b", + "#f8ecbb15", + "#ffeec41e", + "#feecc22a", + "#feebcb37", + "#ffedcd48", + "#fdeaca5f", + "#ffdba690", + "#fedfb09d", + "#fee7c6c8", + "#fef7ede7", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn bronze() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Bronze", + light: [ + "#fdfcfcff", + "#fdf7f5ff", + "#f6edeaff", + "#efe4dfff", + "#e7d9d3ff", + "#dfcdc5ff", + "#d3bcb3ff", + "#c2a499ff", + "#a18072ff", + "#957468ff", + "#7d5e54ff", + "#43302bff", + ], + light_alpha: [ + "#55000003", + "#cc33000a", + "#92250015", + "#80280020", + "#7423002c", + "#7324003a", + "#6c1f004c", + "#671c0066", + "#551a008d", + "#4c150097", + "#3d0f00ab", + "#1d0600d4", + ], + dark: [ + "#141110ff", + "#1c1917ff", + "#262220ff", + "#302a27ff", + "#3b3330ff", + "#493e3aff", + "#5a4c47ff", + "#6f5f58ff", + "#a18072ff", + "#ae8c7eff", + "#d4b3a5ff", + "#ede0d9ff", + ], + dark_alpha: [ + "#d1110004", + "#fbbc910c", + "#faceb817", + "#facdb622", + "#ffd2c12d", + "#ffd1c03c", + "#fdd0c04f", + "#ffd6c565", + "#fec7b09b", + "#fecab5a9", + "#ffd7c6d1", + "#fff1e9ec", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn brown() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Brown", + light: [ + "#fefdfcff", + "#fcf9f6ff", + "#f6eee7ff", + "#f0e4d9ff", + "#ebdacaff", + "#e4cdb7ff", + "#dcbc9fff", + "#cea37eff", + "#ad7f58ff", + "#a07553ff", + "#815e46ff", + "#3e332eff", + ], + light_alpha: [ + "#aa550003", + "#aa550009", + "#a04b0018", + "#9b4a0026", + "#9f4d0035", + "#a04e0048", + "#a34e0060", + "#9f4a0081", + "#823c00a7", + "#723300ac", + "#522100b9", + "#140600d1", + ], + dark: [ + "#12110fff", + "#1c1816ff", + "#28211dff", + "#322922ff", + "#3e3128ff", + "#4d3c2fff", + "#614a39ff", + "#7c5f46ff", + "#ad7f58ff", + "#b88c67ff", + "#dbb594ff", + "#f2e1caff", + ], + dark_alpha: [ + "#91110002", + "#fba67c0c", + "#fcb58c19", + "#fbbb8a24", + "#fcb88931", + "#fdba8741", + "#ffbb8856", + "#ffbe8773", + "#feb87da8", + "#ffc18cb3", + "#fed1aad9", + "#feecd4f2", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn yellow() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Yellow", + light: [ + "#fdfdf9ff", + "#fefce9ff", + "#fffab8ff", + "#fff394ff", + "#ffe770ff", + "#f3d768ff", + "#e4c767ff", + "#d5ae39ff", + "#ffe629ff", + "#ffdc00ff", + "#9e6c00ff", + "#473b1fff", + ], + light_alpha: [ + "#aaaa0006", + "#f4dd0016", + "#ffee0047", + "#ffe3016b", + "#ffd5008f", + "#ebbc0097", + "#d2a10098", + "#c99700c6", + "#ffe100d6", + "#ffdc00ff", + "#9e6c00ff", + "#2e2000e0", + ], + dark: [ + "#14120bff", + "#1b180fff", + "#2d2305ff", + "#362b00ff", + "#433500ff", + "#524202ff", + "#665417ff", + "#836a21ff", + "#ffe629ff", + "#ffff57ff", + "#f5e147ff", + "#f6eeb4ff", + ], + dark_alpha: [ + "#d1510004", + "#f9b4000b", + "#ffaa001e", + "#fdb70028", + "#febb0036", + "#fec40046", + "#fdcb225c", + "#fdca327b", + "#ffe629ff", + "#ffff57ff", + "#fee949f5", + "#fef6baf6", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn amber() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Amber", + light: [ + "#fefdfbff", + "#fefbe9ff", + "#fff7c2ff", + "#ffee9cff", + "#fbe577ff", + "#f3d673ff", + "#e9c162ff", + "#e2a336ff", + "#ffc53dff", + "#ffba18ff", + "#ab6400ff", + "#4f3422ff", + ], + light_alpha: [ + "#c0800004", + "#f4d10016", + "#ffde003d", + "#ffd40063", + "#f8cf0088", + "#eab5008c", + "#dc9b009d", + "#da8a00c9", + "#ffb300c2", + "#ffb300e7", + "#ab6400ff", + "#341500dd", + ], + dark: [ + "#16120cff", + "#1d180fff", + "#302008ff", + "#3f2700ff", + "#4d3000ff", + "#5c3d05ff", + "#714f19ff", + "#8f6424ff", + "#ffc53dff", + "#ffd60aff", + "#ffca16ff", + "#ffe7b3ff", + ], + dark_alpha: [ + "#e63c0006", + "#fd9b000d", + "#fa820022", + "#fc820032", + "#fd8b0041", + "#fd9b0051", + "#ffab2567", + "#ffae3587", + "#ffc53dff", + "#ffd60aff", + "#ffca16ff", + "#ffe7b3ff", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn orange() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Orange", + light: [ + "#fefcfbff", + "#fff7edff", + "#ffefd6ff", + "#ffdfb5ff", + "#ffd19aff", + "#ffc182ff", + "#f5ae73ff", + "#ec9455ff", + "#f76b15ff", + "#ef5f00ff", + "#cc4e00ff", + "#582d1dff", + ], + light_alpha: [ + "#c0400004", + "#ff8e0012", + "#ff9c0029", + "#ff91014a", + "#ff8b0065", + "#ff81007d", + "#ed6c008c", + "#e35f00aa", + "#f65e00ea", + "#ef5f00ff", + "#cc4e00ff", + "#431200e2", + ], + dark: [ + "#17120eff", + "#1e160fff", + "#331e0bff", + "#462100ff", + "#562800ff", + "#66350cff", + "#7e451dff", + "#a35829ff", + "#f76b15ff", + "#ff801fff", + "#ffa057ff", + "#ffe0c2ff", + ], + dark_alpha: [ + "#ec360007", + "#fe6d000e", + "#fb6a0025", + "#ff590039", + "#ff61004a", + "#fd75045c", + "#ff832c75", + "#fe84389d", + "#fe6d15f7", + "#ff801fff", + "#ffa057ff", + "#ffe0c2ff", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn tomato() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Tomato", + light: [ + "#fffcfcff", + "#fff8f7ff", + "#feebe7ff", + "#ffdcd3ff", + "#ffcdc2ff", + "#fdbdafff", + "#f5a898ff", + "#ec8e7bff", + "#e54d2eff", + "#dd4425ff", + "#d13415ff", + "#5c271fff", + ], + light_alpha: [ + "#ff000003", + "#ff200008", + "#f52b0018", + "#ff35002c", + "#ff2e003d", + "#f92d0050", + "#e7280067", + "#db250084", + "#df2600d1", + "#d72400da", + "#cd2200ea", + "#460900e0", + ], + dark: [ + "#181111ff", + "#1f1513ff", + "#391714ff", + "#4e1511ff", + "#5e1c16ff", + "#6e2920ff", + "#853a2dff", + "#ac4d39ff", + "#e54d2eff", + "#ec6142ff", + "#ff977dff", + "#fbd3cbff", + ], + dark_alpha: [ + "#f1121208", + "#ff55330f", + "#ff35232b", + "#fd201142", + "#fe332153", + "#ff4f3864", + "#fd644a7d", + "#fe6d4ea7", + "#fe5431e4", + "#ff6847eb", + "#ff977dff", + "#ffd6cefb", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn red() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Red", + light: [ + "#fffcfcff", + "#fff7f7ff", + "#feebecff", + "#ffdbdcff", + "#ffcdceff", + "#fdbdbeff", + "#f4a9aaff", + "#eb8e90ff", + "#e5484dff", + "#dc3e42ff", + "#ce2c31ff", + "#641723ff", + ], + light_alpha: [ + "#ff000003", + "#ff000008", + "#f3000d14", + "#ff000824", + "#ff000632", + "#f8000442", + "#df000356", + "#d2000571", + "#db0007b7", + "#d10005c1", + "#c40006d3", + "#55000de8", + ], + dark: [ + "#191111ff", + "#201314ff", + "#3b1219ff", + "#500f1cff", + "#611623ff", + "#72232dff", + "#8c333aff", + "#b54548ff", + "#e5484dff", + "#ec5d5eff", + "#ff9592ff", + "#ffd1d9ff", + ], + dark_alpha: [ + "#f4121209", + "#f22f3e11", + "#ff173f2d", + "#fe0a3b44", + "#ff204756", + "#ff3e5668", + "#ff536184", + "#ff5d61b0", + "#fe4e54e4", + "#ff6465eb", + "#ff9592ff", + "#ffd1d9ff", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn ruby() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Ruby", + light: [ + "#fffcfdff", + "#fff7f8ff", + "#feeaedff", + "#ffdce1ff", + "#ffced6ff", + "#f8bfc8ff", + "#efacb8ff", + "#e592a3ff", + "#e54666ff", + "#dc3b5dff", + "#ca244dff", + "#64172bff", + ], + light_alpha: [ + "#ff005503", + "#ff002008", + "#f3002515", + "#ff002523", + "#ff002a31", + "#e4002440", + "#ce002553", + "#c300286d", + "#db002cb9", + "#d2002cc4", + "#c10030db", + "#550016e8", + ], + dark: [ + "#191113ff", + "#1e1517ff", + "#3a141eff", + "#4e1325ff", + "#5e1a2eff", + "#6f2539ff", + "#883447ff", + "#b3445aff", + "#e54666ff", + "#ec5a72ff", + "#ff949dff", + "#fed2e1ff", + ], + dark_alpha: [ + "#f4124a09", + "#fe5a7f0e", + "#ff235d2c", + "#fd195e42", + "#fe2d6b53", + "#ff447665", + "#ff577d80", + "#ff5c7cae", + "#fe4c70e4", + "#ff617beb", + "#ff949dff", + "#ffd3e2fe", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn crimson() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Crimson", + light: [ + "#fffcfdff", + "#fef7f9ff", + "#ffe9f0ff", + "#fedce7ff", + "#faceddff", + "#f3bed1ff", + "#eaacc3ff", + "#e093b2ff", + "#e93d82ff", + "#df3478ff", + "#cb1d63ff", + "#621639ff", + ], + light_alpha: [ + "#ff005503", + "#e0004008", + "#ff005216", + "#f8005123", + "#e5004f31", + "#d0004b41", + "#bf004753", + "#b6004a6c", + "#e2005bc2", + "#d70056cb", + "#c4004fe2", + "#530026e9", + ], + dark: [ + "#191114ff", + "#201318ff", + "#381525ff", + "#4d122fff", + "#5c1839ff", + "#6d2545ff", + "#873356ff", + "#b0436eff", + "#e93d82ff", + "#ee518aff", + "#ff92adff", + "#fdd3e8ff", + ], + dark_alpha: [ + "#f4126709", + "#f22f7a11", + "#fe2a8b2a", + "#fd158741", + "#fd278f51", + "#fe459763", + "#fd559b7f", + "#fe5b9bab", + "#fe418de8", + "#ff5693ed", + "#ff92adff", + "#ffd5eafd", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn pink() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Pink", + light: [ + "#fffcfeff", + "#fef7fbff", + "#fee9f5ff", + "#fbdcefff", + "#f6cee7ff", + "#efbfddff", + "#e7acd0ff", + "#dd93c2ff", + "#d6409fff", + "#cf3897ff", + "#c2298aff", + "#651249ff", + ], + light_alpha: [ + "#ff00aa03", + "#e0008008", + "#f4008c16", + "#e2008b23", + "#d1008331", + "#c0007840", + "#b6006f53", + "#af006f6c", + "#c8007fbf", + "#c2007ac7", + "#b60074d6", + "#59003bed", + ], + dark: [ + "#191117ff", + "#21121dff", + "#37172fff", + "#4b143dff", + "#591c47ff", + "#692955ff", + "#833869ff", + "#a84885ff", + "#d6409fff", + "#de51a8ff", + "#ff8dccff", + "#fdd1eaff", + ], + dark_alpha: [ + "#f412bc09", + "#f420bb12", + "#fe37cc29", + "#fc1ec43f", + "#fd35c24e", + "#fd51c75f", + "#fd62c87b", + "#ff68c8a2", + "#fe49bcd4", + "#ff5cc0dc", + "#ff8dccff", + "#ffd3ecfd", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn plum() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Plum", + light: [ + "#fefcffff", + "#fdf7fdff", + "#fbebfbff", + "#f7def8ff", + "#f2d1f3ff", + "#e9c2ecff", + "#deade3ff", + "#cf91d8ff", + "#ab4abaff", + "#a144afff", + "#953ea3ff", + "#53195dff", + ], + light_alpha: [ + "#aa00ff03", + "#c000c008", + "#cc00cc14", + "#c200c921", + "#b700bd2e", + "#a400b03d", + "#9900a852", + "#9000a56e", + "#89009eb5", + "#7f0092bb", + "#730086c1", + "#40004be6", + ], + dark: [ + "#181118ff", + "#201320ff", + "#351a35ff", + "#451d47ff", + "#512454ff", + "#5e3061ff", + "#734079ff", + "#92549cff", + "#ab4abaff", + "#b658c4ff", + "#e796f3ff", + "#f4d4f4ff", + ], + dark_alpha: [ + "#f112f108", + "#f22ff211", + "#fd4cfd27", + "#f646ff3a", + "#f455ff48", + "#f66dff56", + "#f07cfd70", + "#ee84ff95", + "#e961feb6", + "#ed70ffc0", + "#f19cfef3", + "#feddfef4", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn purple() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Purple", + light: [ + "#fefcfeff", + "#fbf7feff", + "#f7edfeff", + "#f2e2fcff", + "#ead5f9ff", + "#e0c4f4ff", + "#d1afecff", + "#be93e4ff", + "#8e4ec6ff", + "#8347b9ff", + "#8145b5ff", + "#402060ff", + ], + light_alpha: [ + "#aa00aa03", + "#8000e008", + "#8e00f112", + "#8d00e51d", + "#8000db2a", + "#7a01d03b", + "#6d00c350", + "#6600c06c", + "#5c00adb1", + "#53009eb8", + "#52009aba", + "#250049df", + ], + dark: [ + "#18111bff", + "#1e1523ff", + "#301c3bff", + "#3d224eff", + "#48295cff", + "#54346bff", + "#664282ff", + "#8457aaff", + "#8e4ec6ff", + "#9a5cd0ff", + "#d19dffff", + "#ecd9faff", + ], + dark_alpha: [ + "#b412f90b", + "#b744f714", + "#c150ff2d", + "#bb53fd42", + "#be5cfd51", + "#c16dfd61", + "#c378fd7a", + "#c47effa4", + "#b661ffc2", + "#bc6fffcd", + "#d19dffff", + "#f1ddfffa", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn violet() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Violet", + light: [ + "#fdfcfeff", + "#faf8ffff", + "#f4f0feff", + "#ebe4ffff", + "#e1d9ffff", + "#d4cafeff", + "#c2b5f5ff", + "#aa99ecff", + "#6e56cfff", + "#654dc4ff", + "#6550b9ff", + "#2f265fff", + ], + light_alpha: [ + "#5500aa03", + "#4900ff07", + "#4400ee0f", + "#4300ff1b", + "#3600ff26", + "#3100fb35", + "#2d01dd4a", + "#2b00d066", + "#2400b7a9", + "#2300abb2", + "#1f0099af", + "#0b0043d9", + ], + dark: [ + "#14121fff", + "#1b1525ff", + "#291f43ff", + "#33255bff", + "#3c2e69ff", + "#473876ff", + "#56468bff", + "#6958adff", + "#6e56cfff", + "#7d66d9ff", + "#baa7ffff", + "#e2ddfeff", + ], + dark_alpha: [ + "#4422ff0f", + "#853ff916", + "#8354fe36", + "#7d51fd50", + "#845ffd5f", + "#8f6cfd6d", + "#9879ff83", + "#977dfea8", + "#8668ffcc", + "#9176fed7", + "#baa7ffff", + "#e3defffe", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn iris() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Iris", + light: [ + "#fdfdffff", + "#f8f8ffff", + "#f0f1feff", + "#e6e7ffff", + "#dadcffff", + "#cbcdffff", + "#b8baf8ff", + "#9b9ef0ff", + "#5b5bd6ff", + "#5151cdff", + "#5753c6ff", + "#272962ff", + ], + light_alpha: [ + "#0000ff02", + "#0000ff07", + "#0011ee0f", + "#000bff19", + "#000eff25", + "#000aff34", + "#0008e647", + "#0008d964", + "#0000c0a4", + "#0000b6ae", + "#0600abac", + "#000246d8", + ], + dark: [ + "#13131eff", + "#171625ff", + "#202248ff", + "#262a65ff", + "#303374ff", + "#3d3e82ff", + "#4a4a95ff", + "#5958b1ff", + "#5b5bd6ff", + "#6e6adeff", + "#b1a9ffff", + "#e0dffeff", + ], + dark_alpha: [ + "#3636fe0e", + "#564bf916", + "#525bff3b", + "#4d58ff5a", + "#5b62fd6b", + "#6d6ffd7a", + "#7777fe8e", + "#7b7afeac", + "#6a6afed4", + "#7d79ffdc", + "#b1a9ffff", + "#e1e0fffe", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn indigo() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Indigo", + light: [ + "#fdfdfeff", + "#f7f9ffff", + "#edf2feff", + "#e1e9ffff", + "#d2deffff", + "#c1d0ffff", + "#abbdf9ff", + "#8da4efff", + "#3e63ddff", + "#3358d4ff", + "#3a5bc7ff", + "#1f2d5cff", + ], + light_alpha: [ + "#00008002", + "#0040ff08", + "#0047f112", + "#0044ff1e", + "#0044ff2d", + "#003eff3e", + "#0037ed54", + "#0034dc72", + "#0031d2c1", + "#002ec9cc", + "#002bb7c5", + "#001046e0", + ], + dark: [ + "#11131fff", + "#141726ff", + "#182449ff", + "#1d2e62ff", + "#253974ff", + "#304384ff", + "#3a4f97ff", + "#435db1ff", + "#3e63ddff", + "#5472e4ff", + "#9eb1ffff", + "#d6e1ffff", + ], + dark_alpha: [ + "#1133ff0f", + "#3354fa17", + "#2f62ff3c", + "#3566ff57", + "#4171fd6b", + "#5178fd7c", + "#5a7fff90", + "#5b81feac", + "#4671ffdb", + "#5c7efee3", + "#9eb1ffff", + "#d6e1ffff", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn blue() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Blue", + light: [ + "#fbfdffff", + "#f4faffff", + "#e6f4feff", + "#d5efffff", + "#c2e5ffff", + "#acd8fcff", + "#8ec8f6ff", + "#5eb1efff", + "#0090ffff", + "#0588f0ff", + "#0d74ceff", + "#113264ff", + ], + light_alpha: [ + "#0080ff04", + "#008cff0b", + "#008ff519", + "#009eff2a", + "#0093ff3d", + "#0088f653", + "#0083eb71", + "#0084e6a1", + "#0090ffff", + "#0086f0fa", + "#006dcbf2", + "#002359ee", + ], + dark: [ + "#0d1520ff", + "#111927ff", + "#0d2847ff", + "#003362ff", + "#004074ff", + "#104d87ff", + "#205d9eff", + "#2870bdff", + "#0090ffff", + "#3b9effff", + "#70b8ffff", + "#c2e6ffff", + ], + dark_alpha: [ + "#004df211", + "#1166fb18", + "#0077ff3a", + "#0075ff57", + "#0081fd6b", + "#0f89fd7f", + "#2a91fe98", + "#3094feb9", + "#0090ffff", + "#3b9effff", + "#70b8ffff", + "#c2e6ffff", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn cyan() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Cyan", + light: [ + "#fafdfeff", + "#f2fafbff", + "#def7f9ff", + "#caf1f6ff", + "#b5e9f0ff", + "#9ddde7ff", + "#7dcedcff", + "#3db9cfff", + "#00a2c7ff", + "#0797b9ff", + "#107d98ff", + "#0d3c48ff", + ], + light_alpha: [ + "#0099cc05", + "#009db10d", + "#00c2d121", + "#00bcd435", + "#01b4cc4a", + "#00a7c162", + "#009fbb82", + "#00a3c0c2", + "#00a2c7ff", + "#0094b7f8", + "#007491ef", + "#00323ef2", + ], + dark: [ + "#0b161aff", + "#101b20ff", + "#082c36ff", + "#003848ff", + "#004558ff", + "#045468ff", + "#12677eff", + "#11809cff", + "#00a2c7ff", + "#23afd0ff", + "#4ccce6ff", + "#b6ecf7ff", + ], + dark_alpha: [ + "#0091f70a", + "#02a7f211", + "#00befd28", + "#00baff3b", + "#00befd4d", + "#00c7fd5e", + "#14cdff75", + "#11cfff95", + "#00cfffc3", + "#28d6ffcd", + "#52e1fee5", + "#bbf3fef7", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn teal() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Teal", + light: [ + "#fafefdff", + "#f3fbf9ff", + "#e0f8f3ff", + "#ccf3eaff", + "#b8eae0ff", + "#a1ded2ff", + "#83cdc1ff", + "#53b9abff", + "#12a594ff", + "#0d9b8aff", + "#008573ff", + "#0d3d38ff", + ], + light_alpha: [ + "#00cc9905", + "#00aa800c", + "#00c69d1f", + "#00c39633", + "#00b49047", + "#00a6855e", + "#0099807c", + "#009783ac", + "#009e8ced", + "#009684f2", + "#008573ff", + "#00332df2", + ], + dark: [ + "#0d1514ff", + "#111c1bff", + "#0d2d2aff", + "#023b37ff", + "#084843ff", + "#145750ff", + "#1c6961ff", + "#207e73ff", + "#12a594ff", + "#0eb39eff", + "#0bd8b6ff", + "#adf0ddff", + ], + dark_alpha: [ + "#00deab05", + "#12fbe60c", + "#00ffe61e", + "#00ffe92d", + "#00ffea3b", + "#1cffe84b", + "#2efde85f", + "#32ffe775", + "#13ffe49f", + "#0dffe0ae", + "#0afed5d6", + "#b8ffebef", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn jade() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Jade", + light: [ + "#fbfefdff", + "#f4fbf7ff", + "#e6f7edff", + "#d6f1e3ff", + "#c3e9d7ff", + "#acdec8ff", + "#8bceb6ff", + "#56ba9fff", + "#29a383ff", + "#26997bff", + "#208368ff", + "#1d3b31ff", + ], + light_alpha: [ + "#00c08004", + "#00a3460b", + "#00ae4819", + "#00a85129", + "#00a2553c", + "#009a5753", + "#00945f74", + "#00976ea9", + "#00916bd6", + "#008764d9", + "#007152df", + "#002217e2", + ], + dark: [ + "#0d1512ff", + "#121c18ff", + "#0f2e22ff", + "#0b3b2cff", + "#114837ff", + "#1b5745ff", + "#246854ff", + "#2a7e68ff", + "#29a383ff", + "#27b08bff", + "#1fd8a4ff", + "#adf0d4ff", + ], + dark_alpha: [ + "#00de4505", + "#27fba60c", + "#02f99920", + "#00ffaa2d", + "#11ffb63b", + "#34ffc24b", + "#45fdc75e", + "#48ffcf75", + "#38feca9d", + "#31fec7ab", + "#21fec0d6", + "#b8ffe1ef", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn green() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Green", + light: [ + "#fbfefcff", + "#f4fbf6ff", + "#e6f6ebff", + "#d6f1dfff", + "#c4e8d1ff", + "#adddc0ff", + "#8eceaaff", + "#5bb98bff", + "#30a46cff", + "#2b9a66ff", + "#218358ff", + "#193b2dff", + ], + light_alpha: [ + "#00c04004", + "#00a32f0b", + "#00a43319", + "#00a83829", + "#019c393b", + "#00963c52", + "#00914071", + "#00924ba4", + "#008f4acf", + "#008647d4", + "#00713fde", + "#002616e6", + ], + dark: [ + "#0e1512ff", + "#121b17ff", + "#132d21ff", + "#113b29ff", + "#174933ff", + "#20573eff", + "#28684aff", + "#2f7c57ff", + "#30a46cff", + "#33b074ff", + "#3dd68cff", + "#b1f1cbff", + ], + dark_alpha: [ + "#00de4505", + "#29f99d0b", + "#22ff991e", + "#11ff992d", + "#2bffa23c", + "#44ffaa4b", + "#50fdac5e", + "#54ffad73", + "#44ffa49e", + "#43fea4ab", + "#46fea5d4", + "#bbffd7f0", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn grass() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Grass", + light: [ + "#fbfefbff", + "#f5fbf5ff", + "#e9f6e9ff", + "#daf1dbff", + "#c9e8caff", + "#b2ddb5ff", + "#94ce9aff", + "#65ba74ff", + "#46a758ff", + "#3e9b4fff", + "#2a7e3bff", + "#203c25ff", + ], + light_alpha: [ + "#00c00004", + "#0099000a", + "#00970016", + "#009f0725", + "#00930536", + "#008f0a4d", + "#018b0f6b", + "#008d199a", + "#008619b9", + "#007b17c1", + "#006514d5", + "#002006df", + ], + dark: [ + "#0e1511ff", + "#141a15ff", + "#1b2a1eff", + "#1d3a24ff", + "#25482dff", + "#2d5736ff", + "#366740ff", + "#3e7949ff", + "#46a758ff", + "#53b365ff", + "#71d083ff", + "#c2f0c2ff", + ], + dark_alpha: [ + "#00de1205", + "#5ef7780a", + "#70fe8c1b", + "#57ff802c", + "#68ff8b3b", + "#71ff8f4b", + "#77fd925d", + "#77fd9070", + "#65ff82a1", + "#72ff8dae", + "#89ff9fcd", + "#ceffceef", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn lime() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Lime", + light: [ + "#fcfdfaff", + "#f8faf3ff", + "#eef6d6ff", + "#e2f0bdff", + "#d3e7a6ff", + "#c2da91ff", + "#abc978ff", + "#8db654ff", + "#bdee63ff", + "#b0e64cff", + "#5c7c2fff", + "#37401cff", + ], + light_alpha: [ + "#66990005", + "#6b95000c", + "#96c80029", + "#8fc60042", + "#81bb0059", + "#72aa006e", + "#61990087", + "#559200ab", + "#93e4009c", + "#8fdc00b3", + "#375f00d0", + "#1e2900e3", + ], + dark: [ + "#11130cff", + "#151a10ff", + "#1f2917ff", + "#29371dff", + "#334423ff", + "#3d522aff", + "#496231ff", + "#577538ff", + "#bdee63ff", + "#d4ff70ff", + "#bde56cff", + "#e3f7baff", + ], + dark_alpha: [ + "#11bb0003", + "#78f7000a", + "#9bfd4c1a", + "#a7fe5c29", + "#affe6537", + "#b2fe6d46", + "#b6ff6f57", + "#b6fd6d6c", + "#caff69ed", + "#d4ff70ff", + "#d1fe77e4", + "#e9febff7", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn mint() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Mint", + light: [ + "#f9fefdff", + "#f2fbf9ff", + "#ddf9f2ff", + "#c8f4e9ff", + "#b3ecdeff", + "#9ce0d0ff", + "#7ecfbdff", + "#4cbba5ff", + "#86ead4ff", + "#7de0cbff", + "#027864ff", + "#16433cff", + ], + light_alpha: [ + "#00d5aa06", + "#00b18a0d", + "#00d29e22", + "#00cc9937", + "#00c0914c", + "#00b08663", + "#00a17d81", + "#009e7fb3", + "#00d3a579", + "#00c39982", + "#007763fd", + "#00312ae9", + ], + dark: [ + "#0e1515ff", + "#0f1b1bff", + "#092c2bff", + "#003a38ff", + "#004744ff", + "#105650ff", + "#1e685fff", + "#277f70ff", + "#86ead4ff", + "#a8f5e5ff", + "#58d5baff", + "#c4f5e1ff", + ], + dark_alpha: [ + "#00dede05", + "#00f9f90b", + "#00fff61d", + "#00fff42c", + "#00fff23a", + "#0effeb4a", + "#34fde55e", + "#41ffdf76", + "#92ffe7e9", + "#aefeedf5", + "#67ffded2", + "#cbfee9f5", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn sky() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Sky", + light: [ + "#f9feffff", + "#f1fafdff", + "#e1f6fdff", + "#d1f0faff", + "#bee7f5ff", + "#a9daedff", + "#8dcae3ff", + "#60b3d7ff", + "#7ce2feff", + "#74daf8ff", + "#00749eff", + "#1d3e56ff", + ], + light_alpha: [ + "#00d5ff06", + "#00a4db0e", + "#00b3ee1e", + "#00ace42e", + "#00a1d841", + "#0092ca56", + "#0089c172", + "#0085bf9f", + "#00c7fe83", + "#00bcf38b", + "#00749eff", + "#002540e2", + ], + dark: [ + "#0d141fff", + "#111a27ff", + "#112840ff", + "#113555ff", + "#154467ff", + "#1b537bff", + "#1f6692ff", + "#197caeff", + "#7ce2feff", + "#a8eeffff", + "#75c7f0ff", + "#c2f3ffff", + ], + dark_alpha: [ + "#0044ff0f", + "#1171fb18", + "#1184fc33", + "#128fff49", + "#1c9dfd5d", + "#28a5ff72", + "#2badfe8b", + "#1db2fea9", + "#7ce3fffe", + "#a8eeffff", + "#7cd3ffef", + "#c2f3ffff", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn black() -> ColorScaleSet { + StaticColorScaleSet { + scale: "Black", + light: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + light_alpha: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + dark: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + dark_alpha: [ + "#0000000d", + "#0000001a", + "#00000026", + "#00000033", + "#0000004d", + "#00000066", + "#00000080", + "#00000099", + "#000000b3", + "#000000cc", + "#000000e6", + "#000000f2", + ], + } + .try_into() + .unwrap() +} + +pub(crate) fn white() -> ColorScaleSet { + StaticColorScaleSet { + scale: "White", + light: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + light_alpha: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + dark: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + dark_alpha: [ + "#ffffff0d", + "#ffffff1a", + "#ffffff26", + "#ffffff33", + "#ffffff4d", + "#ffffff66", + "#ffffff80", + "#ffffff99", + "#ffffffb3", + "#ffffffcc", + "#ffffffe6", + "#fffffff2", + ], + } + .try_into() + .unwrap() +} diff --git a/crates/theme/src/lib.rs b/crates/theme/src/lib.rs index 8476fb3..7bf8b84 100644 --- a/crates/theme/src/lib.rs +++ b/crates/theme/src/lib.rs @@ -3,6 +3,8 @@ use std::ops::{Deref, DerefMut}; use colors::{brand, hsl, neutral}; use gpui::{px, App, Global, Hsla, Pixels, SharedString, Window, WindowAppearance}; +use crate::colors::{danger, warning}; + mod colors; mod scale; @@ -12,154 +14,91 @@ pub fn init(cx: &mut App) { #[derive(Debug, Clone, Copy, Default)] pub struct ThemeColor { - /// Border color. Used for most borders, is usually a high contrast color. - pub border: Hsla, - /// Border color. Used for deemphasized borders, like a visual divider between two sections - pub border_variant: Hsla, - /// Border color. Used for focused elements, like keyboard focused list item. - pub border_focused: Hsla, - /// Border color. Used for selected elements, like an active search filter or selected checkbox. - pub border_selected: Hsla, - /// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change. - pub border_transparent: Hsla, - /// Border color. Used for disabled elements, like a disabled input or button. - pub border_disabled: Hsla, - /// Background color. Used for elevated surfaces, like a context menu, popup, or dialog. - pub elevated_surface_background: Hsla, - /// Background color. Used for grounded surfaces like a panel or tab. - pub surface_background: Hsla, - /// Background color. Used for the app background and blank panels or windows. + // Surface colors pub background: Hsla, - /// Text color. Used for the foreground of an element. - pub element_foreground: Hsla, - /// Background color. Used for the background of an element that should have a different background than the surface it's on. - /// - /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons... - /// - /// For an element that should have the same background as the surface it's on, use `ghost_element_background`. - pub element_background: Hsla, - /// Background color. Used for the hover state of an element that should have a different background than the surface it's on. - /// - /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen. - pub element_hover: Hsla, - /// Background color. Used for the active state of an element that should have a different background than the surface it's on. - /// - /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed. - pub element_active: Hsla, - /// Background color. Used for the selected state of an element that should have a different background than the surface it's on. - /// - /// Selected states are triggered by the element being selected (or "activated") by the user. - /// - /// This could include a selected checkbox, a toggleable button that is toggled on, etc. - pub element_selected: Hsla, - /// Background Color. Used for the disabled state of a element that should have a different background than the surface it's on. - /// - /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input. - pub element_disabled: Hsla, - /// Text color. Used for the foreground of an secondary element. - pub secondary_foreground: Hsla, - /// Background color. Used for the background of an secondary element that should have a different background than the surface it's on. - /// - /// Secondary elements might include: Buttons, Inputs, Checkboxes, Radio Buttons... - /// - /// For an element that should have the same background as the surface it's on, use `ghost_element_background`. - pub secondary_background: Hsla, - /// Background color. Used for the hover state of an secondary element that should have a different background than the surface it's on. - /// - /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen. - pub secondary_hover: Hsla, - /// Background color. Used for the active state of an secondary element that should have a different background than the surface it's on. - /// - /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed. - pub secondary_active: Hsla, - /// Background color. Used for the selected state of an secondary element that should have a different background than the surface it's on. - /// - /// Selected states are triggered by the element being selected (or "activated") by the user. - /// - /// This could include a selected checkbox, a toggleable button that is toggled on, etc. - pub secondary_selected: Hsla, - /// Background Color. Used for the disabled state of an secondary element that should have a different background than the surface it's on. - /// - /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input. - pub secondary_disabled: Hsla, - /// Background color. Used for the area that shows where a dragged element will be dropped. - pub drop_target_background: Hsla, - /// Used for the background of a ghost element that should have the same background as the surface it's on. - /// - /// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons... - /// - /// For an element that should have a different background than the surface it's on, use `element_background`. - pub ghost_element_background: Hsla, - /// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on. - /// - /// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen. - pub ghost_element_hover: Hsla, - /// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on. - /// - /// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed. - pub ghost_element_active: Hsla, - /// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on. - /// - /// Selected states are triggered by the element being selected (or "activated") by the user. - /// - /// This could include a selected checkbox, a toggleable button that is toggled on, etc. - pub ghost_element_selected: Hsla, - /// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on. - /// - /// Disabled states are shown when a user cannot interact with an element, like a disabled button or input. - pub ghost_element_disabled: Hsla, - /// Text color. Default text color used for most text. - pub text: Hsla, - /// Text color. Color of muted or deemphasized text. It is a subdued version of the standard text color. - pub text_muted: Hsla, - /// Text color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data. - pub text_placeholder: Hsla, - /// Text color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search. - pub text_accent: Hsla, - /// Fill color. Used for the default fill color of an icon. - pub icon: Hsla, - /// Fill color. Used for the muted or deemphasized fill color of an icon. - /// - /// This might be used to show an icon in an inactive pane, or to deemphasize a series of icons to give them less visual weight. - pub icon_muted: Hsla, - /// Fill color. Used for the accent fill color of an icon. - /// - /// This might be used to show when a toggleable icon button is selected. - pub icon_accent: Hsla, - /// The color of the scrollbar thumb. - pub scrollbar_thumb_background: Hsla, - /// The color of the scrollbar thumb when hovered over. - pub scrollbar_thumb_hover_background: Hsla, - /// The border color of the scrollbar thumb. - pub scrollbar_thumb_border: Hsla, - /// The background color of the scrollbar track. - pub scrollbar_track_background: Hsla, - /// The border color of the scrollbar track. - pub scrollbar_track_border: Hsla, - /// Background color. Used for the background of a panel + pub surface_background: Hsla, + pub elevated_surface_background: Hsla, pub panel_background: Hsla, - /// Border color. Used for outline border. - pub ring: Hsla, - /// Background color. Used for inactive tab. - pub tab_inactive_background: Hsla, - /// Background color. Used for hovered tab. - pub tab_hover_background: Hsla, - /// Background color. Used for active tab. - pub tab_active_background: Hsla, - /// Background color. Used for Title Bar. - pub title_bar: Hsla, - /// Border color. Used for Title Bar. - pub title_bar_border: Hsla, - /// Background color. Used for modal's overlay. pub overlay: Hsla, - /// Background color. Used for cursor. - pub cursor: Hsla, - /// Window border color. - /// - /// # Platform specific: - /// - /// This is only works on Linux, other platforms we can't change the window border color. + pub title_bar: Hsla, + pub title_bar_border: Hsla, pub window_border: Hsla, + + // Border colors + pub border: Hsla, + pub border_variant: Hsla, + pub border_focused: Hsla, + pub border_selected: Hsla, + pub border_transparent: Hsla, + pub border_disabled: Hsla, + pub ring: Hsla, + + // Text colors + pub text: Hsla, + pub text_muted: Hsla, + pub text_placeholder: Hsla, + pub text_accent: Hsla, + + // Icon colors + pub icon: Hsla, + pub icon_muted: Hsla, + pub icon_accent: Hsla, + + // Element colors + pub element_foreground: Hsla, + pub element_background: Hsla, + pub element_hover: Hsla, + pub element_active: Hsla, + pub element_selected: Hsla, + pub element_disabled: Hsla, + + // Secondary element colors + pub secondary_foreground: Hsla, + pub secondary_background: Hsla, + pub secondary_hover: Hsla, + pub secondary_active: Hsla, + pub secondary_selected: Hsla, + pub secondary_disabled: Hsla, + + // Danger element colors + pub danger_foreground: Hsla, + pub danger_background: Hsla, + pub danger_hover: Hsla, + pub danger_active: Hsla, + pub danger_selected: Hsla, + pub danger_disabled: Hsla, + + // Warning element colors + pub warning_foreground: Hsla, + pub warning_background: Hsla, + pub warning_hover: Hsla, + pub warning_active: Hsla, + pub warning_selected: Hsla, + pub warning_disabled: Hsla, + + // Ghost element colors + pub ghost_element_background: Hsla, + pub ghost_element_hover: Hsla, + pub ghost_element_active: Hsla, + pub ghost_element_selected: Hsla, + pub ghost_element_disabled: Hsla, + + // Tab colors + pub tab_inactive_background: Hsla, + pub tab_hover_background: Hsla, + pub tab_active_background: Hsla, + + // Scrollbar colors + pub scrollbar_thumb_background: Hsla, + pub scrollbar_thumb_hover_background: Hsla, + pub scrollbar_thumb_border: Hsla, + pub scrollbar_track_background: Hsla, + pub scrollbar_track_border: Hsla, + + // Interactive colors + pub drop_target_background: Hsla, + pub cursor: Hsla, + pub selection: Hsla, } /// The default colors for the theme. @@ -171,55 +110,79 @@ impl ThemeColor { /// Themes that do not specify all colors are refined off of these defaults. pub fn light() -> Self { Self { + background: neutral().light().step_1(), + surface_background: neutral().light().step_2(), + elevated_surface_background: neutral().light().step_3(), + panel_background: gpui::white(), + overlay: neutral().light_alpha().step_3(), + title_bar: gpui::transparent_black(), + title_bar_border: gpui::transparent_black(), + window_border: hsl(240.0, 5.9, 78.0), + border: neutral().light().step_6(), border_variant: neutral().light().step_5(), border_focused: brand().light().step_7(), border_selected: brand().light().step_7(), border_transparent: gpui::transparent_black(), border_disabled: neutral().light().step_3(), - elevated_surface_background: neutral().light().step_3(), - surface_background: neutral().light().step_2(), - background: neutral().light().step_1(), + ring: brand().light().step_8(), + + text: neutral().light().step_12(), + text_muted: neutral().light().step_11(), + text_placeholder: neutral().light().step_10(), + text_accent: brand().light().step_11(), + + icon: neutral().light().step_11(), + icon_muted: neutral().light().step_10(), + icon_accent: brand().light().step_11(), + element_foreground: brand().light().step_12(), element_background: brand().light().step_9(), element_hover: brand().light_alpha().step_10(), element_active: brand().light().step_10(), element_selected: brand().light().step_11(), element_disabled: brand().light_alpha().step_3(), + secondary_foreground: brand().light().step_12(), secondary_background: brand().light().step_3(), secondary_hover: brand().light_alpha().step_4(), secondary_active: brand().light().step_5(), secondary_selected: brand().light().step_5(), secondary_disabled: brand().light_alpha().step_3(), - drop_target_background: brand().light_alpha().step_2(), + + danger_foreground: danger().light().step_1(), + danger_background: danger().light().step_9(), + danger_hover: danger().light_alpha().step_10(), + danger_active: danger().light().step_10(), + danger_selected: danger().light().step_11(), + danger_disabled: danger().light_alpha().step_3(), + + warning_foreground: warning().light().step_12(), + warning_background: warning().light().step_9(), + warning_hover: warning().light_alpha().step_10(), + warning_active: warning().light().step_10(), + warning_selected: warning().light().step_11(), + warning_disabled: warning().light_alpha().step_3(), + ghost_element_background: gpui::transparent_black(), ghost_element_hover: neutral().light_alpha().step_3(), - ghost_element_active: neutral().light_alpha().step_4(), + ghost_element_active: neutral().light_alpha().step_5(), ghost_element_selected: neutral().light_alpha().step_5(), ghost_element_disabled: neutral().light_alpha().step_2(), - text: neutral().light().step_12(), - text_muted: neutral().light().step_11(), - text_placeholder: neutral().light().step_10(), - text_accent: brand().light().step_11(), - icon: neutral().light().step_11(), - icon_muted: neutral().light().step_10(), - icon_accent: brand().light().step_11(), + + tab_inactive_background: neutral().light().step_3(), + tab_hover_background: neutral().light().step_4(), + tab_active_background: neutral().light().step_5(), + scrollbar_thumb_background: neutral().light_alpha().step_3(), scrollbar_thumb_hover_background: neutral().light_alpha().step_4(), scrollbar_thumb_border: gpui::transparent_black(), scrollbar_track_background: gpui::transparent_black(), scrollbar_track_border: neutral().light().step_5(), - panel_background: gpui::white(), - ring: brand().light().step_8(), - tab_active_background: neutral().light().step_5(), - tab_hover_background: neutral().light().step_4(), - tab_inactive_background: neutral().light().step_3(), - title_bar: gpui::transparent_black(), - title_bar_border: gpui::transparent_black(), - overlay: neutral().light_alpha().step_3(), + + drop_target_background: brand().light_alpha().step_2(), cursor: hsl(200., 100., 50.), - window_border: hsl(240.0, 5.9, 78.0), + selection: hsl(200., 100., 50.).alpha(0.25), } } @@ -228,55 +191,79 @@ impl ThemeColor { /// Themes that do not specify all colors are refined off of these defaults. pub fn dark() -> Self { Self { + background: neutral().dark().step_1(), + surface_background: neutral().dark().step_2(), + elevated_surface_background: neutral().dark().step_3(), + panel_background: gpui::black(), + overlay: neutral().dark_alpha().step_3(), + title_bar: gpui::transparent_black(), + title_bar_border: gpui::transparent_black(), + window_border: hsl(240.0, 3.7, 28.0), + border: neutral().dark().step_6(), border_variant: neutral().dark().step_5(), border_focused: brand().dark().step_7(), border_selected: brand().dark().step_7(), border_transparent: gpui::transparent_black(), border_disabled: neutral().dark().step_3(), - elevated_surface_background: neutral().dark().step_3(), - surface_background: neutral().dark().step_2(), - background: neutral().dark().step_1(), + ring: brand().dark().step_8(), + + text: neutral().dark().step_12(), + text_muted: neutral().dark().step_11(), + text_placeholder: neutral().dark().step_10(), + text_accent: brand().dark().step_11(), + + icon: neutral().dark().step_11(), + icon_muted: neutral().dark().step_10(), + icon_accent: brand().dark().step_11(), + element_foreground: brand().dark().step_1(), element_background: brand().dark().step_9(), element_hover: brand().dark_alpha().step_10(), element_active: brand().dark().step_10(), element_selected: brand().dark().step_11(), element_disabled: brand().dark_alpha().step_3(), + secondary_foreground: brand().dark().step_12(), secondary_background: brand().dark().step_3(), secondary_hover: brand().dark_alpha().step_4(), secondary_active: brand().dark().step_5(), secondary_selected: brand().dark().step_5(), secondary_disabled: brand().dark_alpha().step_3(), - drop_target_background: brand().dark_alpha().step_2(), + + danger_foreground: danger().dark().step_1(), + danger_background: danger().dark().step_9(), + danger_hover: danger().dark_alpha().step_10(), + danger_active: danger().dark().step_10(), + danger_selected: danger().dark().step_11(), + danger_disabled: danger().dark_alpha().step_3(), + + warning_foreground: warning().dark().step_12(), + warning_background: warning().dark().step_9(), + warning_hover: warning().dark_alpha().step_10(), + warning_active: warning().dark().step_10(), + warning_selected: warning().dark().step_11(), + warning_disabled: warning().dark_alpha().step_3(), + ghost_element_background: gpui::transparent_black(), ghost_element_hover: neutral().dark_alpha().step_3(), ghost_element_active: neutral().dark_alpha().step_4(), ghost_element_selected: neutral().dark_alpha().step_5(), ghost_element_disabled: neutral().dark_alpha().step_2(), - text: neutral().dark().step_12(), - text_muted: neutral().dark().step_11(), - text_placeholder: neutral().dark().step_10(), - text_accent: brand().dark().step_11(), - icon: neutral().dark().step_11(), - icon_muted: neutral().dark().step_10(), - icon_accent: brand().dark().step_11(), + + tab_inactive_background: neutral().dark().step_3(), + tab_hover_background: neutral().dark().step_4(), + tab_active_background: neutral().dark().step_5(), + scrollbar_thumb_background: neutral().dark_alpha().step_3(), scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(), scrollbar_thumb_border: gpui::transparent_black(), scrollbar_track_background: gpui::transparent_black(), scrollbar_track_border: neutral().dark().step_5(), - panel_background: gpui::black(), - ring: brand().dark().step_8(), - tab_active_background: neutral().dark().step_5(), - tab_hover_background: neutral().dark().step_4(), - tab_inactive_background: neutral().dark().step_3(), - title_bar: gpui::transparent_black(), - title_bar_border: gpui::transparent_black(), - overlay: neutral().dark_alpha().step_3(), + + drop_target_background: brand().dark_alpha().step_2(), cursor: hsl(200., 100., 50.), - window_border: hsl(240.0, 3.7, 28.0), + selection: hsl(200., 100., 50.).alpha(0.25), } } } diff --git a/crates/ui/src/button.rs b/crates/ui/src/button.rs index f2f5955..9b93ec8 100644 --- a/crates/ui/src/button.rs +++ b/crates/ui/src/button.rs @@ -36,6 +36,21 @@ pub trait ButtonVariants: Sized { self.with_variant(ButtonVariant::Secondary) } + /// With the danger style for the Button. + fn danger(self) -> Self { + self.with_variant(ButtonVariant::Danger) + } + + /// With the danger alternate style for the Button. + fn danger_alt(self) -> Self { + self.with_variant(ButtonVariant::DangerAlt) + } + + /// With the warning style for the Button. + fn warning(self) -> Self { + self.with_variant(ButtonVariant::Warning) + } + /// With the ghost style for the Button. fn ghost(self) -> Self { self.with_variant(ButtonVariant::Ghost) @@ -88,6 +103,9 @@ impl ButtonCustomVariant { pub enum ButtonVariant { Primary, Secondary, + Danger, + DangerAlt, + Warning, Ghost, Transparent, Custom(ButtonCustomVariant), @@ -394,11 +412,19 @@ struct ButtonVariantStyle { } impl ButtonVariant { + fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle { + let bg = self.bg_color(window, cx); + let fg = self.text_color(window, cx); + + ButtonVariantStyle { bg, fg } + } + fn bg_color(&self, _window: &Window, cx: &App) -> Hsla { match self { ButtonVariant::Primary => cx.theme().element_background, ButtonVariant::Secondary => cx.theme().elevated_surface_background, - ButtonVariant::Transparent => gpui::transparent_black(), + ButtonVariant::Danger => cx.theme().danger_background, + ButtonVariant::Warning => cx.theme().warning_background, ButtonVariant::Custom(colors) => colors.color, _ => cx.theme().ghost_element_background, } @@ -408,23 +434,22 @@ impl ButtonVariant { match self { ButtonVariant::Primary => cx.theme().element_foreground, ButtonVariant::Secondary => cx.theme().text_muted, + ButtonVariant::Danger => cx.theme().danger_foreground, + ButtonVariant::DangerAlt => cx.theme().danger_background, + ButtonVariant::Warning => cx.theme().warning_foreground, ButtonVariant::Transparent => cx.theme().text_placeholder, ButtonVariant::Ghost => cx.theme().text_muted, ButtonVariant::Custom(colors) => colors.foreground, } } - fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle { - let bg = self.bg_color(window, cx); - let fg = self.text_color(window, cx); - - ButtonVariantStyle { bg, fg } - } - fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle { let bg = match self { ButtonVariant::Primary => cx.theme().element_hover, ButtonVariant::Secondary => cx.theme().secondary_hover, + ButtonVariant::Danger => cx.theme().danger_hover, + ButtonVariant::DangerAlt => gpui::transparent_black(), + ButtonVariant::Warning => cx.theme().warning_hover, ButtonVariant::Ghost => cx.theme().ghost_element_hover, ButtonVariant::Transparent => gpui::transparent_black(), ButtonVariant::Custom(colors) => colors.hover, @@ -444,6 +469,9 @@ impl ButtonVariant { let bg = match self { ButtonVariant::Primary => cx.theme().element_active, ButtonVariant::Secondary => cx.theme().secondary_active, + ButtonVariant::Danger => cx.theme().danger_active, + ButtonVariant::DangerAlt => gpui::transparent_black(), + ButtonVariant::Warning => cx.theme().warning_active, ButtonVariant::Ghost => cx.theme().ghost_element_active, ButtonVariant::Transparent => gpui::transparent_black(), ButtonVariant::Custom(colors) => colors.active, @@ -462,6 +490,9 @@ impl ButtonVariant { let bg = match self { ButtonVariant::Primary => cx.theme().element_selected, ButtonVariant::Secondary => cx.theme().secondary_selected, + ButtonVariant::Danger => cx.theme().danger_selected, + ButtonVariant::DangerAlt => gpui::transparent_black(), + ButtonVariant::Warning => cx.theme().warning_selected, ButtonVariant::Ghost => cx.theme().ghost_element_selected, ButtonVariant::Transparent => gpui::transparent_black(), ButtonVariant::Custom(colors) => colors.active, diff --git a/crates/ui/src/icon.rs b/crates/ui/src/icon.rs index 6923cd8..6bde457 100644 --- a/crates/ui/src/icon.rs +++ b/crates/ui/src/icon.rs @@ -70,6 +70,7 @@ pub enum IconName { ThumbsUp, Upload, UsersThreeFill, + OpenUrl, WindowClose, WindowMaximize, WindowMinimize, @@ -140,6 +141,7 @@ impl IconName { Self::ThumbsUp => "icons/thumbs-up.svg", Self::Upload => "icons/upload.svg", Self::UsersThreeFill => "icons/users-three-fill.svg", + Self::OpenUrl => "icons/open-url.svg", Self::WindowClose => "icons/window-close.svg", Self::WindowMaximize => "icons/window-maximize.svg", Self::WindowMinimize => "icons/window-minimize.svg", diff --git a/crates/ui/src/input/element.rs b/crates/ui/src/input/element.rs index 4e94796..1928d46 100644 --- a/crates/ui/src/input/element.rs +++ b/crates/ui/src/input/element.rs @@ -700,7 +700,7 @@ impl Element for TextElement { // Paint selections if let Some(path) = prepaint.selection_path.take() { - window.paint_path(path, cx.theme().element_disabled); + window.paint_path(path, cx.theme().selection); } // Paint text diff --git a/crates/ui/src/modal.rs b/crates/ui/src/modal.rs index 4bd2457..aa0bc71 100644 --- a/crates/ui/src/modal.rs +++ b/crates/ui/src/modal.rs @@ -1,502 +1,502 @@ -use std::rc::Rc; -use std::time::Duration; - -use gpui::prelude::FluentBuilder; -use gpui::{ - anchored, div, point, px, relative, Animation, AnimationExt as _, AnyElement, App, Bounds, - ClickEvent, Div, FocusHandle, InteractiveElement, IntoElement, KeyBinding, MouseButton, - ParentElement, Pixels, Point, RenderOnce, SharedString, Styled, Window, -}; -use theme::ActiveTheme; - -use crate::actions::{Cancel, Confirm}; -use crate::animation::cubic_bezier; -use crate::button::{Button, ButtonCustomVariant, ButtonVariant, ButtonVariants as _}; -use crate::{h_flex, v_flex, ContextModal, IconName, Root, Sizable, StyledExt}; - -const CONTEXT: &str = "Modal"; - -pub fn init(cx: &mut App) { - cx.bind_keys([ - KeyBinding::new("escape", Cancel, Some(CONTEXT)), - KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)), - ]); -} - -type OnClose = Rc; -type OnOk = Option bool + 'static>>; -type OnCancel = Rc bool + 'static>; -type RenderButtonFn = Box AnyElement>; -type FooterFn = - Box Vec>; - -/// Modal button props. -pub struct ModalButtonProps { - ok_text: Option, - ok_variant: ButtonVariant, - cancel_text: Option, - cancel_variant: ButtonVariant, -} - -impl Default for ModalButtonProps { - fn default() -> Self { - Self { - ok_text: None, - ok_variant: ButtonVariant::Primary, - cancel_text: None, - cancel_variant: ButtonVariant::Ghost, - } - } -} - -impl ModalButtonProps { - /// Sets the text of the OK button. Default is `OK`. - pub fn ok_text(mut self, ok_text: impl Into) -> Self { - self.ok_text = Some(ok_text.into()); - self - } - - /// Sets the variant of the OK button. Default is `ButtonVariant::Primary`. - pub fn ok_variant(mut self, ok_variant: ButtonVariant) -> Self { - self.ok_variant = ok_variant; - self - } - - /// Sets the text of the Cancel button. Default is `Cancel`. - pub fn cancel_text(mut self, cancel_text: impl Into) -> Self { - self.cancel_text = Some(cancel_text.into()); - self - } - - /// Sets the variant of the Cancel button. Default is `ButtonVariant::default()`. - pub fn cancel_variant(mut self, cancel_variant: ButtonVariant) -> Self { - self.cancel_variant = cancel_variant; - self - } -} - -#[derive(IntoElement)] -pub struct Modal { - base: Div, - title: Option, - footer: Option, - content: Div, - width: Pixels, - max_width: Option, - margin_top: Option, - - on_close: OnClose, - on_ok: OnOk, - on_cancel: OnCancel, - button_props: ModalButtonProps, - show_close: bool, - overlay: bool, - overlay_closable: bool, - keyboard: bool, - - /// This will be change when open the modal, the focus handle is create when open the modal. - pub(crate) focus_handle: FocusHandle, - pub(crate) layer_ix: usize, - pub(crate) overlay_visible: bool, -} - -impl Modal { - pub fn new(_window: &mut Window, cx: &mut App) -> Self { - let radius = (cx.theme().radius * 2.).min(px(20.)); - - let base = v_flex() - .bg(cx.theme().background) - .border_1() - .border_color(cx.theme().border) - .rounded(radius) - .shadow_xl() - .min_h_24(); - - Self { - base, - focus_handle: cx.focus_handle(), - title: None, - footer: None, - content: v_flex(), - margin_top: None, - width: px(380.), - max_width: None, - overlay: true, - keyboard: true, - layer_ix: 0, - overlay_visible: false, - on_close: Rc::new(|_, _, _| {}), - on_ok: None, - on_cancel: Rc::new(|_, _, _| true), - button_props: ModalButtonProps::default(), - show_close: true, - overlay_closable: true, - } - } - - /// Sets the title of the modal. - pub fn title(mut self, title: impl IntoElement) -> Self { - self.title = Some(title.into_any_element()); - self - } - - /// Set the footer of the modal. - /// - /// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`. - /// - /// - First `RenderButtonFn` is the render function for the OK button. - /// - Second `RenderButtonFn` is the render function for the CANCEL button. - /// - /// When you set the footer, the footer will be placed default footer buttons. - pub fn footer(mut self, footer: F) -> Self - where - E: IntoElement, - F: Fn(RenderButtonFn, RenderButtonFn, &mut Window, &mut App) -> Vec + 'static, - { - self.footer = Some(Box::new(move |ok, cancel, window, cx| { - footer(ok, cancel, window, cx) - .into_iter() - .map(|e| e.into_any_element()) - .collect() - })); - self - } - - /// Set to use confirm modal, with OK and Cancel buttons. - /// - /// See also [`Self::alert`] - pub fn confirm(self) -> Self { - self.footer(|ok, cancel, window, cx| vec![cancel(window, cx), ok(window, cx)]) - .overlay_closable(false) - .show_close(false) - } - - /// Set to as a alter modal, with OK button. - /// - /// See also [`Self::confirm`] - pub fn alert(self) -> Self { - self.footer(|ok, _, window, cx| vec![ok(window, cx)]) - .overlay_closable(false) - .show_close(false) - } - - /// Set the button props of the modal. - pub fn button_props(mut self, button_props: ModalButtonProps) -> Self { - self.button_props = button_props; - self - } - - /// Sets the callback for when the modal is closed. - /// - /// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback. - pub fn on_close( - mut self, - on_close: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, - ) -> Self { - self.on_close = Rc::new(on_close); - self - } - - /// Sets the callback for when the modal is has been confirmed. - /// - /// The callback should return `true` to close the modal, if return `false` the modal will not be closed. - pub fn on_ok( - mut self, - on_ok: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static, - ) -> Self { - self.on_ok = Some(Rc::new(on_ok)); - self - } - - /// Sets the callback for when the modal is has been canceled. - /// - /// The callback should return `true` to close the modal, if return `false` the modal will not be closed. - pub fn on_cancel( - mut self, - on_cancel: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static, - ) -> Self { - self.on_cancel = Rc::new(on_cancel); - self - } - - /// Sets the false to hide close icon, default: true - pub fn show_close(mut self, show_close: bool) -> Self { - self.show_close = show_close; - self - } - - /// Set the top offset of the modal, defaults to None, will use the 1/10 of the viewport height. - pub fn margin_top(mut self, margin_top: Pixels) -> Self { - self.margin_top = Some(margin_top); - self - } - - /// Sets the width of the modal, defaults to 480px. - pub fn width(mut self, width: Pixels) -> Self { - self.width = width; - self - } - - /// Set the maximum width of the modal, defaults to `None`. - pub fn max_w(mut self, max_width: Pixels) -> Self { - self.max_width = Some(max_width); - self - } - - /// Set the overlay of the modal, defaults to `true`. - pub fn overlay(mut self, overlay: bool) -> Self { - self.overlay = overlay; - self - } - - /// Set the overlay closable of the modal, defaults to `true`. - /// - /// When the overlay is clicked, the modal will be closed. - pub fn overlay_closable(mut self, overlay_closable: bool) -> Self { - self.overlay_closable = overlay_closable; - self - } - - /// Set whether to support keyboard esc to close the modal, defaults to `true`. - pub fn keyboard(mut self, keyboard: bool) -> Self { - self.keyboard = keyboard; - self - } - - pub(crate) fn has_overlay(&self) -> bool { - self.overlay - } -} - -impl ParentElement for Modal { - fn extend(&mut self, elements: impl IntoIterator) { - self.content.extend(elements); - } -} - -impl Styled for Modal { - fn style(&mut self) -> &mut gpui::StyleRefinement { - self.base.style() - } -} - -impl RenderOnce for Modal { - fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement { - let layer_ix = self.layer_ix; - let on_close = self.on_close.clone(); - let on_ok = self.on_ok.clone(); - let on_cancel = self.on_cancel.clone(); - - let render_ok: RenderButtonFn = Box::new({ - let on_ok = on_ok.clone(); - let on_close = on_close.clone(); - let ok_variant = self.button_props.ok_variant; - let ok_text = self.button_props.ok_text.unwrap_or_else(|| "OK".into()); - - move |_, _| { - Button::new("ok") - .label(ok_text) - .with_variant(ok_variant) - .small() - .flex_1() - .on_click({ - let on_ok = on_ok.clone(); - let on_close = on_close.clone(); - - move |_, window, cx| { - if let Some(on_ok) = &on_ok { - if !on_ok(&ClickEvent::default(), window, cx) { - return; - } - } - - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); - } - }) - .into_any_element() - } - }); - - let render_cancel: RenderButtonFn = Box::new({ - let on_cancel = on_cancel.clone(); - let on_close = on_close.clone(); - let cancel_variant = self.button_props.cancel_variant; - let cancel_text = self - .button_props - .cancel_text - .unwrap_or_else(|| "Cancel".into()); - - move |_, _| { - Button::new("cancel") - .label(cancel_text) - .with_variant(cancel_variant) - .small() - .flex_1() - .on_click({ - let on_cancel = on_cancel.clone(); - let on_close = on_close.clone(); - move |_, window, cx| { - if !on_cancel(&ClickEvent::default(), window, cx) { - return; - } - - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); - } - }) - .into_any_element() - } - }); - - let window_paddings = crate::window_border::window_paddings(window, cx); - - let view_size = window.viewport_size() - - gpui::size( - window_paddings.left + window_paddings.right, - window_paddings.top + window_paddings.bottom, - ); - - let bounds = Bounds { - origin: Point::default(), - size: view_size, - }; - - let offset_top = px(layer_ix as f32 * 16.); - let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top; - let x = bounds.center().x - self.width / 2.; - - anchored() - .position(point(window_paddings.left, window_paddings.top)) - .snap_to_window() - .child( - div() - .w(view_size.width) - .h(view_size.height) - .when(self.overlay_visible, |this| { - this.occlude().bg(cx.theme().overlay) - }) - .when(self.overlay_closable, |this| { - // Only the last modal owns the `mouse down - close modal` event. - if (self.layer_ix + 1) != Root::read(window, cx).active_modals.len() { - return this; - } - - this.on_mouse_down(MouseButton::Left, { - let on_cancel = on_cancel.clone(); - let on_close = on_close.clone(); - move |_, window, cx| { - on_cancel(&ClickEvent::default(), window, cx); - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); - } - }) - }) - .child( - self.base - .id(SharedString::from(format!("modal-{layer_ix}"))) - .key_context(CONTEXT) - .track_focus(&self.focus_handle) - .when(self.keyboard, |this| { - this.on_action({ - let on_cancel = on_cancel.clone(); - let on_close = on_close.clone(); - move |_: &Cancel, window, cx| { - // FIXME: - // - // Here some Modal have no focus_handle, so it will not work will Escape key. - // But by now, we `cx.close_modal()` going to close the last active model, so the Escape is unexpected to work. - on_cancel(&ClickEvent::default(), window, cx); - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); - } - }) - .on_action({ - let on_ok = on_ok.clone(); - let on_close = on_close.clone(); - let has_footer = self.footer.is_some(); - move |_: &Confirm, window, cx| { - if let Some(on_ok) = &on_ok { - if on_ok(&ClickEvent::default(), window, cx) { - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); - } - } else if has_footer { - window.close_modal(cx); - } - } - }) - }) - .absolute() - .occlude() - .relative() - .left(x) - .top(y) - .w(self.width) - .when_some(self.max_width, |this, w| this.max_w(w)) - .when_some(self.title, |this, title| { - this.child( - div() - .h_12() - .px_3() - .mb_2() - .flex() - .items_center() - .font_semibold() - .border_b_1() - .border_color(cx.theme().border) - .line_height(relative(1.)) - .child(title), - ) - }) - .when(self.show_close, |this| { - this.child( - Button::new(SharedString::from(format!( - "modal-close-{layer_ix}" - ))) - .icon(IconName::CloseCircleFill) - .absolute() - .top_1p5() - .right_2() - .custom( - ButtonCustomVariant::new(window, cx) - .foreground(cx.theme().icon_muted) - .color(cx.theme().ghost_element_background) - .hover(cx.theme().ghost_element_background) - .active(cx.theme().ghost_element_background), - ) - .on_click( - move |_, window, cx| { - on_cancel(&ClickEvent::default(), window, cx); - on_close(&ClickEvent::default(), window, cx); - window.close_modal(cx); - }, - ), - ) - }) - .child(div().w_full().flex_1().child(self.content)) - .when(self.footer.is_some(), |this| { - let footer = self.footer.unwrap(); - - this.child( - h_flex().p_4().gap_1p5().justify_center().children(footer( - render_ok, - render_cancel, - window, - cx, - )), - ) - }) - .with_animation( - "slide-down", - Animation::new(Duration::from_secs_f64(0.25)) - .with_easing(cubic_bezier(0.32, 0.72, 0., 1.)), - move |this, delta| { - let y_offset = px(0.) + delta * px(30.); - this.top(y + y_offset) - }, - ), - ), - ) - } -} +use std::rc::Rc; +use std::time::Duration; + +use gpui::prelude::FluentBuilder; +use gpui::{ + anchored, div, point, px, relative, Animation, AnimationExt as _, AnyElement, App, Bounds, + ClickEvent, Div, FocusHandle, InteractiveElement, IntoElement, KeyBinding, MouseButton, + ParentElement, Pixels, Point, RenderOnce, SharedString, Styled, Window, +}; +use theme::ActiveTheme; + +use crate::actions::{Cancel, Confirm}; +use crate::animation::cubic_bezier; +use crate::button::{Button, ButtonCustomVariant, ButtonVariant, ButtonVariants as _}; +use crate::{h_flex, v_flex, ContextModal, IconName, Root, Sizable, StyledExt}; + +const CONTEXT: &str = "Modal"; + +pub fn init(cx: &mut App) { + cx.bind_keys([ + KeyBinding::new("escape", Cancel, Some(CONTEXT)), + KeyBinding::new("enter", Confirm { secondary: false }, Some(CONTEXT)), + ]); +} + +type OnClose = Rc; +type OnOk = Option bool + 'static>>; +type OnCancel = Rc bool + 'static>; +type RenderButtonFn = Box AnyElement>; +type FooterFn = + Box Vec>; + +/// Modal button props. +pub struct ModalButtonProps { + ok_text: Option, + ok_variant: ButtonVariant, + cancel_text: Option, + cancel_variant: ButtonVariant, +} + +impl Default for ModalButtonProps { + fn default() -> Self { + Self { + ok_text: None, + ok_variant: ButtonVariant::Primary, + cancel_text: None, + cancel_variant: ButtonVariant::Ghost, + } + } +} + +impl ModalButtonProps { + /// Sets the text of the OK button. Default is `OK`. + pub fn ok_text(mut self, ok_text: impl Into) -> Self { + self.ok_text = Some(ok_text.into()); + self + } + + /// Sets the variant of the OK button. Default is `ButtonVariant::Primary`. + pub fn ok_variant(mut self, ok_variant: ButtonVariant) -> Self { + self.ok_variant = ok_variant; + self + } + + /// Sets the text of the Cancel button. Default is `Cancel`. + pub fn cancel_text(mut self, cancel_text: impl Into) -> Self { + self.cancel_text = Some(cancel_text.into()); + self + } + + /// Sets the variant of the Cancel button. Default is `ButtonVariant::default()`. + pub fn cancel_variant(mut self, cancel_variant: ButtonVariant) -> Self { + self.cancel_variant = cancel_variant; + self + } +} + +#[derive(IntoElement)] +pub struct Modal { + base: Div, + title: Option, + footer: Option, + content: Div, + width: Pixels, + max_width: Option, + margin_top: Option, + + on_close: OnClose, + on_ok: OnOk, + on_cancel: OnCancel, + button_props: ModalButtonProps, + show_close: bool, + overlay: bool, + overlay_closable: bool, + keyboard: bool, + + /// This will be change when open the modal, the focus handle is create when open the modal. + pub(crate) focus_handle: FocusHandle, + pub(crate) layer_ix: usize, + pub(crate) overlay_visible: bool, +} + +impl Modal { + pub fn new(_window: &mut Window, cx: &mut App) -> Self { + let radius = (cx.theme().radius * 2.).min(px(20.)); + + let base = v_flex() + .bg(cx.theme().background) + .border_1() + .border_color(cx.theme().border) + .rounded(radius) + .shadow_xl() + .min_h_24(); + + Self { + base, + focus_handle: cx.focus_handle(), + title: None, + footer: None, + content: v_flex(), + margin_top: None, + width: px(380.), + max_width: None, + overlay: true, + keyboard: true, + layer_ix: 0, + overlay_visible: false, + on_close: Rc::new(|_, _, _| {}), + on_ok: None, + on_cancel: Rc::new(|_, _, _| true), + button_props: ModalButtonProps::default(), + show_close: true, + overlay_closable: true, + } + } + + /// Sets the title of the modal. + pub fn title(mut self, title: impl IntoElement) -> Self { + self.title = Some(title.into_any_element()); + self + } + + /// Set the footer of the modal. + /// + /// The `footer` is a function that takes two `RenderButtonFn` and a `WindowContext` and returns a list of `AnyElement`. + /// + /// - First `RenderButtonFn` is the render function for the OK button. + /// - Second `RenderButtonFn` is the render function for the CANCEL button. + /// + /// When you set the footer, the footer will be placed default footer buttons. + pub fn footer(mut self, footer: F) -> Self + where + E: IntoElement, + F: Fn(RenderButtonFn, RenderButtonFn, &mut Window, &mut App) -> Vec + 'static, + { + self.footer = Some(Box::new(move |ok, cancel, window, cx| { + footer(ok, cancel, window, cx) + .into_iter() + .map(|e| e.into_any_element()) + .collect() + })); + self + } + + /// Set to use confirm modal, with OK and Cancel buttons. + /// + /// See also [`Self::alert`] + pub fn confirm(self) -> Self { + self.footer(|ok, cancel, window, cx| vec![cancel(window, cx), ok(window, cx)]) + .overlay_closable(false) + .show_close(false) + } + + /// Set to as a alter modal, with OK button. + /// + /// See also [`Self::confirm`] + pub fn alert(self) -> Self { + self.footer(|ok, _, window, cx| vec![ok(window, cx)]) + .overlay_closable(false) + .show_close(false) + } + + /// Set the button props of the modal. + pub fn button_props(mut self, button_props: ModalButtonProps) -> Self { + self.button_props = button_props; + self + } + + /// Sets the callback for when the modal is closed. + /// + /// Called after [`Self::on_ok`] or [`Self::on_cancel`] callback. + pub fn on_close( + mut self, + on_close: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, + ) -> Self { + self.on_close = Rc::new(on_close); + self + } + + /// Sets the callback for when the modal is has been confirmed. + /// + /// The callback should return `true` to close the modal, if return `false` the modal will not be closed. + pub fn on_ok( + mut self, + on_ok: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static, + ) -> Self { + self.on_ok = Some(Rc::new(on_ok)); + self + } + + /// Sets the callback for when the modal is has been canceled. + /// + /// The callback should return `true` to close the modal, if return `false` the modal will not be closed. + pub fn on_cancel( + mut self, + on_cancel: impl Fn(&ClickEvent, &mut Window, &mut App) -> bool + 'static, + ) -> Self { + self.on_cancel = Rc::new(on_cancel); + self + } + + /// Sets the false to hide close icon, default: true + pub fn show_close(mut self, show_close: bool) -> Self { + self.show_close = show_close; + self + } + + /// Set the top offset of the modal, defaults to None, will use the 1/10 of the viewport height. + pub fn margin_top(mut self, margin_top: Pixels) -> Self { + self.margin_top = Some(margin_top); + self + } + + /// Sets the width of the modal, defaults to 480px. + pub fn width(mut self, width: Pixels) -> Self { + self.width = width; + self + } + + /// Set the maximum width of the modal, defaults to `None`. + pub fn max_w(mut self, max_width: Pixels) -> Self { + self.max_width = Some(max_width); + self + } + + /// Set the overlay of the modal, defaults to `true`. + pub fn overlay(mut self, overlay: bool) -> Self { + self.overlay = overlay; + self + } + + /// Set the overlay closable of the modal, defaults to `true`. + /// + /// When the overlay is clicked, the modal will be closed. + pub fn overlay_closable(mut self, overlay_closable: bool) -> Self { + self.overlay_closable = overlay_closable; + self + } + + /// Set whether to support keyboard esc to close the modal, defaults to `true`. + pub fn keyboard(mut self, keyboard: bool) -> Self { + self.keyboard = keyboard; + self + } + + pub(crate) fn has_overlay(&self) -> bool { + self.overlay + } +} + +impl ParentElement for Modal { + fn extend(&mut self, elements: impl IntoIterator) { + self.content.extend(elements); + } +} + +impl Styled for Modal { + fn style(&mut self) -> &mut gpui::StyleRefinement { + self.base.style() + } +} + +impl RenderOnce for Modal { + fn render(self, window: &mut Window, cx: &mut App) -> impl gpui::IntoElement { + let layer_ix = self.layer_ix; + let on_close = self.on_close.clone(); + let on_ok = self.on_ok.clone(); + let on_cancel = self.on_cancel.clone(); + + let render_ok: RenderButtonFn = Box::new({ + let on_ok = on_ok.clone(); + let on_close = on_close.clone(); + let ok_variant = self.button_props.ok_variant; + let ok_text = self.button_props.ok_text.unwrap_or_else(|| "OK".into()); + + move |_, _| { + Button::new("ok") + .label(ok_text) + .with_variant(ok_variant) + .small() + .flex_1() + .on_click({ + let on_ok = on_ok.clone(); + let on_close = on_close.clone(); + + move |_, window, cx| { + if let Some(on_ok) = &on_ok { + if !on_ok(&ClickEvent::default(), window, cx) { + return; + } + } + + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + } + }) + .into_any_element() + } + }); + + let render_cancel: RenderButtonFn = Box::new({ + let on_cancel = on_cancel.clone(); + let on_close = on_close.clone(); + let cancel_variant = self.button_props.cancel_variant; + let cancel_text = self + .button_props + .cancel_text + .unwrap_or_else(|| "Cancel".into()); + + move |_, _| { + Button::new("cancel") + .label(cancel_text) + .with_variant(cancel_variant) + .small() + .flex_1() + .on_click({ + let on_cancel = on_cancel.clone(); + let on_close = on_close.clone(); + move |_, window, cx| { + if !on_cancel(&ClickEvent::default(), window, cx) { + return; + } + + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + } + }) + .into_any_element() + } + }); + + let window_paddings = crate::window_border::window_paddings(window, cx); + + let view_size = window.viewport_size() + - gpui::size( + window_paddings.left + window_paddings.right, + window_paddings.top + window_paddings.bottom, + ); + + let bounds = Bounds { + origin: Point::default(), + size: view_size, + }; + + let offset_top = px(layer_ix as f32 * 16.); + let y = self.margin_top.unwrap_or(view_size.height / 10.) + offset_top; + let x = bounds.center().x - self.width / 2.; + + anchored() + .position(point(window_paddings.left, window_paddings.top)) + .snap_to_window() + .child( + div() + .w(view_size.width) + .h(view_size.height) + .when(self.overlay_visible, |this| { + this.occlude().bg(cx.theme().overlay) + }) + .when(self.overlay_closable, |this| { + // Only the last modal owns the `mouse down - close modal` event. + if (self.layer_ix + 1) != Root::read(window, cx).active_modals.len() { + return this; + } + + this.on_mouse_down(MouseButton::Left, { + let on_cancel = on_cancel.clone(); + let on_close = on_close.clone(); + move |_, window, cx| { + on_cancel(&ClickEvent::default(), window, cx); + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + } + }) + }) + .child( + self.base + .id(SharedString::from(format!("modal-{layer_ix}"))) + .key_context(CONTEXT) + .track_focus(&self.focus_handle) + .when(self.keyboard, |this| { + this.on_action({ + let on_cancel = on_cancel.clone(); + let on_close = on_close.clone(); + move |_: &Cancel, window, cx| { + // FIXME: + // + // Here some Modal have no focus_handle, so it will not work will Escape key. + // But by now, we `cx.close_modal()` going to close the last active model, so the Escape is unexpected to work. + on_cancel(&ClickEvent::default(), window, cx); + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + } + }) + .on_action({ + let on_ok = on_ok.clone(); + let on_close = on_close.clone(); + let has_footer = self.footer.is_some(); + move |_: &Confirm, window, cx| { + if let Some(on_ok) = &on_ok { + if on_ok(&ClickEvent::default(), window, cx) { + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + } + } else if has_footer { + window.close_modal(cx); + } + } + }) + }) + .absolute() + .occlude() + .relative() + .left(x) + .top(y) + .w(self.width) + .when_some(self.max_width, |this, w| this.max_w(w)) + .when_some(self.title, |this, title| { + this.child( + div() + .h_12() + .px_3() + .mb_2() + .flex() + .items_center() + .font_semibold() + .border_b_1() + .border_color(cx.theme().border) + .line_height(relative(1.)) + .child(title), + ) + }) + .when(self.show_close, |this| { + this.child( + Button::new(SharedString::from(format!( + "modal-close-{layer_ix}" + ))) + .icon(IconName::CloseCircleFill) + .absolute() + .top_1p5() + .right_2() + .custom( + ButtonCustomVariant::new(window, cx) + .foreground(cx.theme().icon_muted) + .color(cx.theme().ghost_element_background) + .hover(cx.theme().ghost_element_background) + .active(cx.theme().ghost_element_background), + ) + .on_click( + move |_, window, cx| { + on_cancel(&ClickEvent::default(), window, cx); + on_close(&ClickEvent::default(), window, cx); + window.close_modal(cx); + }, + ), + ) + }) + .child(div().relative().w_full().flex_1().child(self.content)) + .when(self.footer.is_some(), |this| { + let footer = self.footer.unwrap(); + + this.child( + h_flex().p_4().gap_1p5().justify_center().children(footer( + render_ok, + render_cancel, + window, + cx, + )), + ) + }) + .with_animation( + "slide-down", + Animation::new(Duration::from_secs_f64(0.25)) + .with_easing(cubic_bezier(0.32, 0.72, 0., 1.)), + move |this, delta| { + let y_offset = px(0.) + delta * px(30.); + this.top(y + y_offset) + }, + ), + ), + ) + } +} diff --git a/locales/app.yml b/locales/app.yml index d544285..cd6339f 100644 --- a/locales/app.yml +++ b/locales/app.yml @@ -548,7 +548,128 @@ subject: pt: "O assunto será atualizado quando você enviar uma mensagem." ko: "메시지를 보낼 때 제목이 업데이트됩니다." +screening: + ignore: + en: "Ignore" + zh-CN: "忽略" + zh-TW: "忽略" + ru: "Игнорировать" + vi: "Bỏ qua" + ja: "無視" + es: "Ignorar" + pt: "Ignorar" + ko: "무시" + response: + en: "Response" + zh-CN: "回复" + zh-TW: "回覆" + ru: "Ответ" + vi: "Trả lời" + ja: "応答" + es: "Respuesta" + pt: "Resposta" + ko: "응답" + verified: + en: "%{address} matches the user's public key." + zh-CN: "%{address} 匹配用户的公钥。" + zh-TW: "%{address} 匹配用戶的公鑰。" + ru: "%{address} соответствует публичному ключу пользователя." + vi: "%{address} khớp với khóa công khai của người dùng." + ja: "%{address} はユーザーの公開鍵に一致します。" + es: "%{address} coincide con la clave pública del usuario." + pt: "%{address} corresponde à chave pública do usuário." + ko: "%{address}가 사용자의 공개 키와 일치합니다." + not_verified: + en: "%{address} does not match the user's public key." + zh-CN: "%{address} 不匹配用户的公钥。" + zh-TW: "%{address} 不匹配用戶的公鑰。" + ru: "%{address} не соответствует публичному ключу пользователя." + vi: "%{address} không khớp với khóa công khai của người dùng." + ja: "%{address} はユーザーの公開鍵に一致しません。" + es: "%{address} no coincide con la clave pública del usuario." + pt: "%{address} não corresponde à chave pública do usuário." + ko: "%{address}가 사용자의 공개 키와 일치하지 않습니다." + contact: + en: "This person is in your contact list." + zh-CN: "此人在您的联系人列表中。" + zh-TW: "此人位於您的聯絡人清單中。" + ru: "Этот человек находится в вашем списке контактов." + vi: "Người này có trong danh sách liên hệ của bạn." + ja: "この人はあなたの連絡先リストにあります。" + es: "Esta persona está en su lista de contactos." + pt: "Essa pessoa está na sua lista de contatos." + ko: "이 사람은 사용자의 연락처 목록에 있습니다." + not_contact: + en: "This person is not in your contact list." + zh-CN: "此人在您的联系人列表中。" + zh-TW: "此人位於您的聯絡人清單中。" + ru: "Этот человек находится в вашем списке контактов." + vi: "Người này có trong danh sách liên hệ của bạn." + ja: "この人はあなたの連絡先リストにあります。" + es: "Esta persona está en su lista de contactos." + pt: "Essa pessoa está na sua lista de contatos." + ko: "이 사람은 사용자의 연락처 목록에 있습니다." + total_connections: + en: "You have %{u} mutual contacts with this person." + zh-CN: "您与此人有 %{u} 个共同联系。" + zh-TW: "您與此人有 %{u} 個共同聯繫。" + ru: "Вы связаны с этим человеком %{u} раз." + vi: "Bạn có %{u} liên hệ chung với người này." + ja: "この人と共同の連絡先が %{u} 人あります。" + es: "Tienes %{u} contactos comunes con esta persona." + pt: "Você está vinculado a %{u} contatos com esta pessoa." + no_connections: + en: "You don’t have any mutual contacts with this person." + zh-CN: "您与此人没有任何共同联系。" + zh-TW: "您與此人沒有任何共同聯繫。" + ru: "Вы не связаны с этим человеком." + vi: "Bạn không có liên hệ chung với người này." + ja: "この人と共同の連絡先はありません。" + es: "No tienes contactos comunes con esta persona." + pt: "Você não está vinculado a nenhum contato com esta pessoa." + ko: "이 사람과 공통 연락처가 없습니다." + report: + en: "Report as a scam or impostor" + zh-CN: "举报为诈骗或冒充者" + zh-TW: "檢舉為詐騙或冒充者" + ru: "Сообщить о мошенничестве или подделке" + vi: "Báo cáo" + ja: "詐欺または冒充者として報告する" + es: "Informar como una estafa o impostor" + pt: "Relatar como uma estafa ou impostor" + ko: "신고를 해요" + report_msg: + en: "Report submitted successfully" + zh-CN: "举报成功" + zh-TW: "檢舉成功" + ru: "Жалоба отправлена успешно" + vi: "Báo cáo đã được gửi thành công" + ja: "報告が送信されました。" + es: "Informe enviado con éxito" + pt: "Relatório enviado com sucesso" + ko: "신고가 성공적으로 제출되었습니다." + profile: + title: + en: "Profile" + zh-CN: "个人资料" + zh-TW: "個人資料" + ru: "Профиль" + vi: "Hồ sơ" + ja: "プロフィール" + es: "Perfil" + pt: "Perfil" + ko: "프로필" + view: + en: "View Profile" + zh-CN: "查看个人资料" + zh-TW: "檢視個人資料" + ru: "Просмотреть профиль" + vi: "Xem hồ sơ" + ja: "プロフィールを表示する" + es: "Ver perfil" + pt: "Ver perfil" + ko: "프로필 보기" set_profile_picture: en: "Set Profile Picture" zh-CN: "设置个人资料图片" @@ -639,18 +760,18 @@ profile: es: "Abrir en njump.me" pt: "Abrir no njump.me" ko: "njump.me에서 열기" + no_bio: + en: "No bio." + zh-CN: "无简介" + zh-TW: "無簡介" + ru: "Нет биографии" + vi: "Không có tiểu sử" + ja: "プロフィールなし" + es: "Sin biografía" + pt: "Sem biografia" + ko: "프로필 없음" preferences: - modal_profile_title: - en: "Profile" - zh-CN: "个人资料" - zh-TW: "個人資料" - ru: "Профиль" - vi: "Hồ sơ" - ja: "プロフィール" - es: "Perfil" - pt: "Perfil" - ko: "프로필" modal_relays_title: en: "Edit your Messaging Relays" zh-CN: "编辑您的Messaging Relays" diff --git a/script/linux b/script/linux old mode 100644 new mode 100755