diff --git a/Cargo.lock b/Cargo.lock index 10b68ebf..1befafb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -1136,7 +1136,7 @@ dependencies = [ [[package]] name = "collections" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "indexmap", "rustc-hash 2.1.1", @@ -1240,6 +1240,7 @@ dependencies = [ "serde_json", "smallvec", "smol", + "theme", "tracing-subscriber", "ui", "webbrowser", @@ -1526,7 +1527,7 @@ dependencies = [ [[package]] name = "derive_refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "proc-macro2", "quote", @@ -2317,7 +2318,7 @@ dependencies = [ [[package]] name = "gpui" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "anyhow", "as-raw-xcb-connection", @@ -2409,7 +2410,7 @@ dependencies = [ [[package]] name = "gpui_macros" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "proc-macro2", "quote", @@ -2425,9 +2426,9 @@ checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" [[package]] name = "h2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" dependencies = [ "atomic-waker", "bytes", @@ -2633,7 +2634,7 @@ dependencies = [ [[package]] name = "http_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "anyhow", "bytes", @@ -2650,7 +2651,7 @@ dependencies = [ [[package]] name = "http_client_tls" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "rustls", "rustls-platform-verifier", @@ -2699,7 +2700,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -3214,9 +3215,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -3391,7 +3392,7 @@ dependencies = [ [[package]] name = "media" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "anyhow", "bindgen 0.71.1", @@ -3589,7 +3590,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "nostr" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24" +source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642" dependencies = [ "aes", "base64", @@ -3614,7 +3615,7 @@ dependencies = [ [[package]] name = "nostr-connect" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24" +source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642" dependencies = [ "async-utility", "nostr", @@ -3626,7 +3627,7 @@ dependencies = [ [[package]] name = "nostr-database" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24" +source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642" dependencies = [ "flatbuffers", "lru", @@ -3637,7 +3638,7 @@ dependencies = [ [[package]] name = "nostr-lmdb" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24" +source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642" dependencies = [ "async-utility", "heed", @@ -3650,7 +3651,7 @@ dependencies = [ [[package]] name = "nostr-relay-pool" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24" +source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642" dependencies = [ "async-utility", "async-wsocket", @@ -3666,7 +3667,7 @@ dependencies = [ [[package]] name = "nostr-sdk" version = "0.41.0" -source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24" +source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642" dependencies = [ "async-utility", "nostr", @@ -4842,7 +4843,7 @@ dependencies = [ [[package]] name = "refineable" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "derive_refineable", "workspace-hack", @@ -4926,7 +4927,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 0.26.11", "windows-registry 0.4.0", ] @@ -4981,7 +4982,7 @@ dependencies = [ [[package]] name = "reqwest_client" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "anyhow", "bytes", @@ -5042,9 +5043,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rust-embed" -version = "8.7.0" +version = "8.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fbc0ee50fcb99af7cebb442e5df7b5b45e9460ffa3f8f549cd26b862bec49d" +checksum = "60e425e204264b144d4c929d126d0de524b40a961686414bab5040f7465c71be" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -5130,9 +5131,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.26" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "aws-lc-rs", "log", @@ -5176,9 +5177,9 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4937d110d34408e9e5ad30ba0b0ca3b6a8a390f8db3636db60144ac4fa792750" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ "core-foundation 0.10.0", "core-foundation-sys", @@ -5191,7 +5192,7 @@ dependencies = [ "rustls-webpki", "security-framework 3.2.0", "security-framework-sys", - "webpki-root-certs", + "webpki-root-certs 0.26.11", "windows-sys 0.59.0", ] @@ -5203,9 +5204,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" dependencies = [ "aws-lc-rs", "ring", @@ -5451,7 +5452,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749" [[package]] name = "semantic_version" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "anyhow", "serde", @@ -5774,7 +5775,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sum_tree" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "arrayvec", "log", @@ -6065,6 +6066,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "theme" +version = "0.1.5" +dependencies = [ + "anyhow", + "gpui", + "log", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -6199,9 +6209,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", @@ -6269,7 +6279,7 @@ dependencies = [ "tokio", "tokio-rustls", "tungstenite", - "webpki-roots", + "webpki-roots 0.26.11", ] [[package]] @@ -6499,6 +6509,7 @@ dependencies = [ "serde_json", "smallvec", "smol", + "theme", "unicode-segmentation", "uuid", ] @@ -6684,7 +6695,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "util" version = "0.1.0" -source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b" +source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24" dependencies = [ "anyhow", "async-fs", @@ -7064,18 +7075,36 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.10" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99403924bc5f23afefc319b8ac67ed0e50669f6e52a413314cccb1fdbc93ba0" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.0", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "0.26.10" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.0", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" dependencies = [ "rustls-pki-types", ] @@ -7620,9 +7649,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index b702a6eb..d0678d70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,10 @@ gpui = { git = "https://github.com/zed-industries/zed" } reqwest_client = { git = "https://github.com/zed-industries/zed" } # Nostr -nostr = { git = "https://github.com/rust-nostr/nostr", branch = "nip46", features = ["parser"] } -nostr-sdk = { git = "https://github.com/rust-nostr/nostr", branch = "nip46", features = ["lmdb", "nip96", "nip59", "nip49", "nip44", "nip05"] } -nostr-connect = { git = "https://github.com/rust-nostr/nostr", branch = "nip46" } -nostr-keyring = { git = "https://github.com/rust-nostr/nostr", branch = "nip46" } +nostr = { git = "https://github.com/rust-nostr/nostr", features = ["parser"] } +nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "nip96", "nip59", "nip49", "nip44", "nip05"] } +nostr-connect = { git = "https://github.com/rust-nostr/nostr" } +nostr-keyring = { git = "https://github.com/rust-nostr/nostr" } # Others emojis = "0.6.4" diff --git a/crates/coop/Cargo.toml b/crates/coop/Cargo.toml index fe254871..e41e68a6 100644 --- a/crates/coop/Cargo.toml +++ b/crates/coop/Cargo.toml @@ -10,6 +10,7 @@ path = "src/main.rs" [dependencies] ui = { path = "../ui" } +theme = { path = "../theme" } common = { path = "../common" } global = { path = "../global" } chats = { path = "../chats" } diff --git a/crates/coop/src/chatspace.rs b/crates/coop/src/chatspace.rs index 0c8d0393..c87564ad 100644 --- a/crates/coop/src/chatspace.rs +++ b/crates/coop/src/chatspace.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use account::Account; use anyhow::Error; use global::get_client; @@ -9,11 +11,10 @@ use gpui::{ use nostr_sdk::prelude::*; use serde::Deserialize; use smallvec::{smallvec, SmallVec}; -use std::sync::Arc; +use theme::{ActiveTheme, Theme, ThemeMode}; use ui::{ button::{Button, ButtonVariants}, dock_area::{dock::DockPlacement, panel::PanelView, DockArea, DockItem}, - theme::{ActiveTheme, Appearance, Theme}, ContextModal, IconName, Root, Sizable, TitleBar, }; @@ -324,7 +325,7 @@ impl Render for ChatSpace { .xsmall() .ghost() .map(|this| { - if cx.theme().appearance.is_dark() { + if cx.theme().mode.is_dark() { this.icon(IconName::Sun) } else { this.icon(IconName::Moon) @@ -332,15 +333,15 @@ impl Render for ChatSpace { }) .on_click(cx.listener( |_, _, window, cx| { - if cx.theme().appearance.is_dark() { + if cx.theme().mode.is_dark() { Theme::change( - Appearance::Light, + ThemeMode::Light, Some(window), cx, ); } else { Theme::change( - Appearance::Dark, + ThemeMode::Dark, Some(window), cx, ); diff --git a/crates/coop/src/main.rs b/crates/coop/src/main.rs index 549fcd0b..2ab47098 100644 --- a/crates/coop/src/main.rs +++ b/crates/coop/src/main.rs @@ -27,7 +27,8 @@ use nostr_sdk::{ }; use smol::Timer; use std::{collections::HashSet, mem, sync::Arc, time::Duration}; -use ui::{theme::Theme, Root}; +use theme::Theme; +use ui::Root; pub(crate) mod asset; pub(crate) mod chatspace; @@ -286,7 +287,6 @@ fn main() { // Open a window with default options cx.open_window(opts, |window, cx| { // Automatically sync theme with system appearance - #[cfg(not(target_os = "linux"))] window .observe_window_appearance(|window, cx| { Theme::sync_system_appearance(Some(window), cx); diff --git a/crates/coop/src/views/chat.rs b/crates/coop/src/views/chat.rs index 1ae965fd..9f7a9cff 100644 --- a/crates/coop/src/views/chat.rs +++ b/crates/coop/src/views/chat.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, sync::Arc}; + use anyhow::{anyhow, Error}; use async_utility::task::spawn; use chats::{ @@ -8,7 +10,7 @@ use chats::{ use common::{nip96_upload, profile::SharedProfile}; use global::{constants::IMAGE_SERVICE, get_client}; use gpui::{ - div, img, impl_internal_actions, list, prelude::FluentBuilder, px, relative, svg, white, + div, img, impl_internal_actions, list, prelude::FluentBuilder, px, red, relative, svg, white, AnyElement, App, AppContext, Context, Element, Empty, Entity, EventEmitter, Flatten, FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListState, ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, StatefulInteractiveElement, Styled, @@ -18,7 +20,7 @@ use nostr_sdk::prelude::*; use serde::Deserialize; use smallvec::{smallvec, SmallVec}; use smol::fs; -use std::{collections::HashMap, sync::Arc}; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, dock_area::panel::{Panel, PanelEvent}, @@ -27,7 +29,6 @@ use ui::{ notification::Notification, popup_menu::PopupMenu, text::RichText, - theme::{scale::ColorScaleStep, ActiveTheme}, v_flex, ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, }; @@ -388,7 +389,7 @@ impl Chat { .entry(item.id) .or_insert_with(|| RichText::new(item.content.to_owned(), &item.mentions)); - this.hover(|this| this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE))) + this.hover(|this| this.bg(cx.theme().surface_background)) .text_sm() .child( div() @@ -397,10 +398,8 @@ impl Chat { .top_0() .w(px(2.)) .h_full() - .bg(cx.theme().transparent) - .group_hover("", |this| { - this.bg(cx.theme().accent.step(cx, ColorScaleStep::NINE)) - }), + .bg(cx.theme().border_transparent) + .group_hover("", |this| this.bg(cx.theme().element_active)), ) .child(img(item.author.shared_avatar()).size_8().flex_shrink_0()) .child( @@ -419,9 +418,7 @@ impl Chat { ) .child( div() - .text_color( - cx.theme().base.step(cx, ColorScaleStep::NINE), - ) + .text_color(cx.theme().text_placeholder) .child(item.ago()), ), ) @@ -437,12 +434,12 @@ impl Chat { .top_0() .w(px(2.)) .h_full() - .bg(cx.theme().transparent) - .group_hover("", |this| this.bg(cx.theme().danger)), + .bg(cx.theme().border_transparent) + .group_hover("", |this| this.bg(red())), ) .child(img("brand/avatar.png").size_8().flex_shrink_0()) .text_sm() - .text_color(cx.theme().danger) + .text_color(red()) .child(content.clone()), RoomMessage::Announcement => this .w_full() @@ -453,13 +450,13 @@ impl Chat { .justify_center() .text_center() .text_xs() - .text_color(cx.theme().base.step(cx, ColorScaleStep::NINE)) + .text_color(cx.theme().text_placeholder) .line_height(relative(1.3)) .child( svg() .path("brand/coop.svg") .size_10() - .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), + .text_color(cx.theme().elevated_surface_background), ) .child(DESC), }) @@ -492,10 +489,12 @@ impl Panel for Chat { .items_center() .size_5() .rounded_full() - .bg(cx.theme().accent.step(cx, ColorScaleStep::THREE)) - .child(Icon::new(IconName::UsersThreeFill).xsmall().text_color( - cx.theme().accent.step(cx, ColorScaleStep::TWELVE), - )), + .bg(cx.theme().element_disabled) + .child( + Icon::new(IconName::UsersThreeFill) + .xsmall() + .text_color(cx.theme().icon_accent), + ), ) } }) @@ -567,7 +566,7 @@ impl Render for Chat { )) .size_16() .shadow_lg() - .rounded(px(cx.theme().radius)) + .rounded(cx.theme().radius) .object_fit(ObjectFit::ScaleDown), ) .child( @@ -580,7 +579,7 @@ impl Render for Chat { .items_center() .justify_center() .rounded_full() - .bg(cx.theme().danger) + .bg(red()) .child( Icon::new(IconName::Close) .size_2() @@ -603,9 +602,7 @@ impl Render for Chat { .flex() .items_center() .gap_1() - .text_color( - cx.theme().base.step(cx, ColorScaleStep::ELEVEN), - ) + .text_color(cx.theme().text_muted) .child( Button::new("upload") .icon(Icon::new(IconName::Upload)) diff --git a/crates/coop/src/views/compose.rs b/crates/coop/src/views/compose.rs index 87759e86..f10651ef 100644 --- a/crates/coop/src/views/compose.rs +++ b/crates/coop/src/views/compose.rs @@ -1,9 +1,14 @@ +use std::{ + collections::{BTreeSet, HashSet}, + time::Duration, +}; + use anyhow::Error; use chats::ChatRegistry; use common::{profile::SharedProfile, random_name}; use global::get_client; use gpui::{ - div, img, impl_internal_actions, prelude::FluentBuilder, px, relative, uniform_list, App, + div, img, impl_internal_actions, prelude::FluentBuilder, px, red, relative, uniform_list, App, AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, TextAlign, Window, @@ -12,15 +17,11 @@ use nostr_sdk::prelude::*; use serde::Deserialize; use smallvec::{smallvec, SmallVec}; use smol::Timer; -use std::{ - collections::{BTreeSet, HashSet}, - time::Duration, -}; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, dock_area::dock::DockPlacement, input::{InputEvent, TextInput}, - theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt, }; @@ -335,24 +336,18 @@ impl Render for Compose { div() .px_3() .text_sm() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) + .text_color(cx.theme().text_muted) .child(DESCRIPTION), ) .when_some(self.error_message.read(cx).as_ref(), |this, msg| { - this.child( - div() - .px_3() - .text_xs() - .text_color(cx.theme().danger) - .child(msg.clone()), - ) + this.child(div().px_3().text_xs().text_color(red()).child(msg.clone())) }) .child( div().px_3().flex().flex_col().child( div() .h_10() .border_b_1() - .border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE)) + .border_color(cx.theme().border) .flex() .items_center() .gap_1() @@ -399,9 +394,7 @@ impl Render for Compose { .child( div() .text_xs() - .text_color( - cx.theme().base.step(cx, ColorScaleStep::ELEVEN), - ) + .text_color(cx.theme().text_muted) .child("Your recently contacts will appear here."), ), ) @@ -445,19 +438,13 @@ impl Render for Compose { this.child( Icon::new(IconName::CheckCircleFill) .small() - .text_color( - cx.theme().accent.step( - cx, - ColorScaleStep::NINE, - ), - ), + .text_color(cx.theme().icon_accent), ) }) .hover(|this| { this.bg(cx .theme() - .base - .step(cx, ColorScaleStep::THREE)) + .elevated_surface_background) }) .on_click(move |_, window, cx| { window.dispatch_action( diff --git a/crates/coop/src/views/login.rs b/crates/coop/src/views/login.rs index 097b2c03..90349871 100644 --- a/crates/coop/src/views/login.rs +++ b/crates/coop/src/views/login.rs @@ -4,19 +4,19 @@ use account::Account; use common::create_qr; use global::get_client_keys; use gpui::{ - div, img, prelude::FluentBuilder, relative, AnyElement, App, AppContext, Context, Entity, + div, img, prelude::FluentBuilder, red, relative, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, Image, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window, }; use nostr_connect::prelude::*; use smallvec::{smallvec, SmallVec}; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, dock_area::panel::{Panel, PanelEvent}, input::{InputEvent, TextInput}, notification::Notification, popup_menu::PopupMenu, - theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, Sizable, Size, StyledExt, }; @@ -307,9 +307,7 @@ impl Render for Login { ) .child( div() - .text_color( - cx.theme().base.step(cx, ColorScaleStep::ELEVEN), - ) + .text_color(cx.theme().text_muted) .child("Continue with Private Key or Bunker"), ), ) @@ -334,7 +332,7 @@ impl Render for Login { div() .text_xs() .text_center() - .text_color(cx.theme().danger) + .text_color(red()) .child(error), ) }), @@ -348,7 +346,7 @@ impl Render for Login { .flex() .items_center() .justify_center() - .bg(cx.theme().base.step(cx, ColorScaleStep::TWO)) + .bg(cx.theme().surface_background) .child( div() .flex() @@ -364,17 +362,13 @@ impl Render for Login { div() .font_semibold() .line_height(relative(1.2)) - .text_color( - cx.theme().base.step(cx, ColorScaleStep::TWELVE), - ) + .text_color(cx.theme().text) .child("Continue with Nostr Connect"), ) .child( div() .text_sm() - .text_color( - cx.theme().base.step(cx, ColorScaleStep::ELEVEN), - ) + .text_color(cx.theme().text_muted) .child("Use Nostr Connect apps to scan the code"), ), ) @@ -391,10 +385,10 @@ impl Render for Login { .gap_2() .rounded_2xl() .shadow_md() - .when(cx.theme().appearance.is_dark(), |this| { - this.shadow_none().border_1().border_color( - cx.theme().base.step(cx, ColorScaleStep::SIX), - ) + .when(cx.theme().mode.is_dark(), |this| { + this.shadow_none() + .border_1() + .border_color(cx.theme().border) }) .bg(cx.theme().background) .child(img(qr).h_64()), diff --git a/crates/coop/src/views/new_account.rs b/crates/coop/src/views/new_account.rs index 6d54ec88..5d151422 100644 --- a/crates/coop/src/views/new_account.rs +++ b/crates/coop/src/views/new_account.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use account::Account; use async_utility::task::spawn; use common::nip96_upload; @@ -9,13 +11,12 @@ use gpui::{ }; use nostr_sdk::prelude::*; use smol::fs; -use std::str::FromStr; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, dock_area::panel::{Panel, PanelEvent}, input::TextInput, popup_menu::PopupMenu, - theme::{scale::ColorScaleStep, ActiveTheme}, Disableable, Icon, IconName, Sizable, Size, StyledExt, }; @@ -290,7 +291,7 @@ impl Render for NewAccount { .my_2() .w_full() .h_px() - .bg(cx.theme().base.step(cx, ColorScaleStep::THREE)), + .bg(cx.theme().elevated_surface_background), ) .child( Button::new("submit") diff --git a/crates/coop/src/views/onboarding.rs b/crates/coop/src/views/onboarding.rs index ff9b34ac..e1b39e34 100644 --- a/crates/coop/src/views/onboarding.rs +++ b/crates/coop/src/views/onboarding.rs @@ -2,11 +2,11 @@ use gpui::{ div, relative, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Window, }; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, dock_area::panel::{Panel, PanelEvent}, popup_menu::PopupMenu, - theme::{scale::ColorScaleStep, ActiveTheme}, Icon, IconName, StyledExt, }; @@ -91,7 +91,7 @@ impl Render for Onboarding { svg() .path("brand/coop.svg") .size_16() - .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), + .text_color(cx.theme().elevated_surface_background), ) .child( div() @@ -103,11 +103,7 @@ impl Render for Onboarding { .line_height(relative(1.3)) .child(TITLE), ) - .child( - div() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) - .child(SUBTITLE), - ), + .child(div().text_color(cx.theme().text_muted).child(SUBTITLE)), ), ) .child( diff --git a/crates/coop/src/views/profile.rs b/crates/coop/src/views/profile.rs index f98f3a63..92119399 100644 --- a/crates/coop/src/views/profile.rs +++ b/crates/coop/src/views/profile.rs @@ -2,16 +2,16 @@ use async_utility::task::spawn; use common::nip96_upload; use global::{constants::IMAGE_SERVICE, get_client}; use gpui::{ - div, img, prelude::FluentBuilder, px, App, AppContext, Context, Entity, Flatten, IntoElement, + div, img, prelude::FluentBuilder, App, AppContext, Context, Entity, Flatten, IntoElement, ParentElement, PathPromptOptions, Render, Styled, Task, Window, }; use nostr_sdk::prelude::*; use smol::fs; use std::{str::FromStr, time::Duration}; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, input::TextInput, - theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, IconName, Sizable, Size, }; @@ -249,8 +249,8 @@ impl Render for Profile { div() .w_full() .h_32() - .bg(cx.theme().base.step(cx, ColorScaleStep::TWO)) - .rounded(px(cx.theme().radius)) + .bg(cx.theme().surface_background) + .rounded(cx.theme().radius) .flex() .flex_col() .items_center() diff --git a/crates/coop/src/views/relays.rs b/crates/coop/src/views/relays.rs index 5accf9e5..e6c83875 100644 --- a/crates/coop/src/views/relays.rs +++ b/crates/coop/src/views/relays.rs @@ -7,10 +7,10 @@ use gpui::{ }; use nostr_sdk::prelude::*; use smallvec::{smallvec, SmallVec}; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, input::{InputEvent, TextInput}, - theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, IconName, Sizable, }; @@ -241,8 +241,8 @@ impl Relays { .flex() .items_center() .justify_between() - .rounded(px(cx.theme().radius)) - .bg(cx.theme().base.step(cx, ColorScaleStep::THREE)) + .rounded(cx.theme().radius) + .bg(cx.theme().elevated_surface_background) .text_xs() .child(item) .child( @@ -298,7 +298,7 @@ impl Render for Relays { .child( div() .text_sm() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) + .text_color(cx.theme().text_muted) .child(MESSAGE), ) .child( @@ -320,7 +320,7 @@ impl Render for Relays { .label("Add") .small() .ghost() - .rounded(px(cx.theme().radius)) + .rounded_md() .on_click(cx.listener(|this, _, window, cx| { this.add(window, cx) })), diff --git a/crates/coop/src/views/search.rs b/crates/coop/src/views/search.rs index 9ffea140..2dadda27 100644 --- a/crates/coop/src/views/search.rs +++ b/crates/coop/src/views/search.rs @@ -6,19 +6,19 @@ use chats::ChatRegistry; use common::profile::SharedProfile; use global::{constants::SEARCH_RELAYS, get_client}; use gpui::{ - div, img, prelude::FluentBuilder, px, relative, uniform_list, App, AppContext, Context, Entity, - InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, - Task, Window, + div, img, prelude::FluentBuilder, px, red, relative, uniform_list, App, AppContext, Context, + Entity, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, + Subscription, Task, Window, }; use itertools::Itertools; use nostr_sdk::prelude::*; use smallvec::{smallvec, SmallVec}; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, dock_area::dock::DockPlacement, indicator::Indicator, input::{InputEvent, TextInput}, - theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Disableable, IconName, Sizable, }; @@ -202,8 +202,6 @@ impl Search { impl Render for Search { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - let mute_color = cx.theme().base.step(cx, ColorScaleStep::NINE); - div() .size_full() .flex() @@ -233,7 +231,7 @@ impl Render for Search { div() .px_3() .text_xs() - .text_color(cx.theme().danger) + .text_color(red()) .child(error.clone()), ) }) @@ -254,7 +252,7 @@ impl Render for Search { .items_center() .justify_center() .text_sm() - .text_color(mute_color) + .text_color(cx.theme().text_muted) .child("No one with that query could be found.") } else { this.child( @@ -278,7 +276,7 @@ impl Render for Search { .flex() .items_center() .justify_between() - .rounded(px(cx.theme().radius)) + .rounded(cx.theme().radius) .child( div() .flex() @@ -305,7 +303,10 @@ impl Render for Search { this.child( div() .text_xs() - .text_color(mute_color) + .text_color( + cx.theme() + .text_muted, + ) .child(nip05), ) }, @@ -335,10 +336,7 @@ impl Render for Search { ), ) .hover(|this| { - this.bg(cx - .theme() - .base - .step(cx, ColorScaleStep::THREE)) + this.bg(cx.theme().elevated_surface_background) }), ); } diff --git a/crates/coop/src/views/sidebar/button.rs b/crates/coop/src/views/sidebar/button.rs index 842de9cd..cb328474 100644 --- a/crates/coop/src/views/sidebar/button.rs +++ b/crates/coop/src/views/sidebar/button.rs @@ -1,13 +1,11 @@ use std::rc::Rc; use gpui::{ - div, prelude::FluentBuilder, px, App, ClickEvent, Div, InteractiveElement, IntoElement, + div, prelude::FluentBuilder, App, ClickEvent, Div, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, }; -use ui::{ - theme::{scale::ColorScaleStep, ActiveTheme}, - Icon, -}; +use theme::ActiveTheme; +use ui::Icon; type Handler = Rc; @@ -49,16 +47,12 @@ impl RenderOnce for SidebarButton { self.base .id(self.label.clone()) - .rounded(px(cx.theme().radius)) + .rounded(cx.theme().radius) .when_some(self.icon, |this, icon| { - this.child( - div() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) - .child(icon), - ) + this.child(div().text_color(cx.theme().text_muted).child(icon)) }) .child(self.label.clone()) - .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) + .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/folder.rs b/crates/coop/src/views/sidebar/folder.rs index 599fc50f..eb027181 100644 --- a/crates/coop/src/views/sidebar/folder.rs +++ b/crates/coop/src/views/sidebar/folder.rs @@ -1,15 +1,12 @@ use std::rc::Rc; use gpui::{ - div, percentage, prelude::FluentBuilder, px, App, ClickEvent, Div, Img, InteractiveElement, + div, percentage, prelude::FluentBuilder, App, ClickEvent, Div, Img, InteractiveElement, IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, }; -use ui::{ - theme::{scale::ColorScaleStep, ActiveTheme}, - tooltip::Tooltip, - Collapsible, Icon, IconName, Sizable, StyledExt, -}; +use theme::ActiveTheme; +use ui::{tooltip::Tooltip, Collapsible, Icon, IconName, Sizable, StyledExt}; type Handler = Rc; @@ -96,9 +93,9 @@ impl RenderOnce for Parent { .gap_2() .px_2() .h_8() - .rounded(px(cx.theme().radius)) + .rounded(cx.theme().radius) .text_sm() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) + .text_color(cx.theme().text_muted) .font_medium() .child( Icon::new(IconName::CaretDown) @@ -118,7 +115,7 @@ impl RenderOnce for Parent { Tooltip::new(tooltip.clone(), window, cx).into() }) }) - .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) + .hover(|this| this.bg(cx.theme().elevated_surface_background)) .on_click(move |ev, window, cx| handler(ev, window, cx)), ) .when(!self.collapsed, |this| { @@ -204,9 +201,9 @@ impl RenderOnce for Folder { .gap_2() .px_2() .h_8() - .rounded(px(cx.theme().radius)) + .rounded(cx.theme().radius) .text_sm() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) + .text_color(cx.theme().text_muted) .font_medium() .child( Icon::new(IconName::CaretDown) @@ -226,7 +223,7 @@ impl RenderOnce for Folder { Tooltip::new(tooltip.clone(), window, cx).into() }) }) - .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) + .hover(|this| this.bg(cx.theme().elevated_surface_background)) .on_click(move |ev, window, cx| handler(ev, window, cx)), ) .when(!self.collapsed, |this| { @@ -291,7 +288,7 @@ impl RenderOnce for FolderItem { .items_center() .justify_between() .text_sm() - .rounded(px(cx.theme().radius)) + .rounded(cx.theme().radius) .child( div() .flex_1() @@ -312,11 +309,11 @@ impl RenderOnce for FolderItem { .items_center() .size_5() .rounded_full() - .bg(cx.theme().accent.step(cx, ColorScaleStep::THREE)) + .bg(cx.theme().element_disabled) .child( - Icon::new(IconName::UsersThreeFill).xsmall().text_color( - cx.theme().accent.step(cx, ColorScaleStep::TWELVE), - ), + Icon::new(IconName::UsersThreeFill) + .xsmall() + .text_color(cx.theme().text_accent), ), ) } @@ -328,11 +325,11 @@ impl RenderOnce for FolderItem { div() .flex_shrink_0() .text_xs() - .text_color(cx.theme().base.step(cx, ColorScaleStep::TEN)) + .text_color(cx.theme().text_placeholder) .child(description), ) }) - .hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))) + .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/mod.rs b/crates/coop/src/views/sidebar/mod.rs index 739dac58..94c26dc2 100644 --- a/crates/coop/src/views/sidebar/mod.rs +++ b/crates/coop/src/views/sidebar/mod.rs @@ -15,6 +15,7 @@ use gpui::{ ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Task, Window, }; use itertools::Itertools; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonCustomVariant, ButtonVariants}, dock_area::{ @@ -23,7 +24,6 @@ use ui::{ }, popup_menu::{PopupMenu, PopupMenuExt}, skeleton::Skeleton, - theme::{scale::ColorScaleStep, ActiveTheme}, IconName, Sizable, StyledExt, }; @@ -305,7 +305,7 @@ impl Render for Sidebar { .items_center() .text_xs() .font_semibold() - .text_color(cx.theme().base.step(cx, ColorScaleStep::NINE)) + .text_color(cx.theme().text_placeholder) .child("Messages") .child( Button::new("menu") @@ -320,13 +320,10 @@ impl Render for Sidebar { .small() .custom( ButtonCustomVariant::new(window, cx) - .foreground( - cx.theme().base.step(cx, ColorScaleStep::NINE), - ) - .color(cx.theme().transparent) - .hover(cx.theme().transparent) - .active(cx.theme().transparent) - .border(cx.theme().transparent), + .foreground(cx.theme().text_placeholder) + .color(cx.theme().ghost_element_background) + .hover(cx.theme().ghost_element_background) + .active(cx.theme().ghost_element_background), ) .on_click(cx.listener(move |this, _, _, cx| { this.split_into_folders(cx); diff --git a/crates/coop/src/views/subject.rs b/crates/coop/src/views/subject.rs index ade93a94..f1e735bb 100644 --- a/crates/coop/src/views/subject.rs +++ b/crates/coop/src/views/subject.rs @@ -3,10 +3,10 @@ use gpui::{ div, App, AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement, Render, Styled, Window, }; +use theme::ActiveTheme; use ui::{ button::{Button, ButtonVariants}, input::TextInput, - theme::{scale::ColorScaleStep, ActiveTheme}, ContextModal, Size, }; @@ -90,7 +90,7 @@ impl Render for Subject { .child( div() .text_sm() - .text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)) + .text_color(cx.theme().text_muted) .child("Subject:"), ) .child(self.input.clone()) @@ -98,7 +98,7 @@ impl Render for Subject { div() .text_xs() .italic() - .text_color(cx.theme().base.step(cx, ColorScaleStep::NINE)) + .text_color(cx.theme().text_placeholder) .child(HELP_TEXT), ), ) diff --git a/crates/coop/src/views/welcome.rs b/crates/coop/src/views/welcome.rs index 446fccba..01440956 100644 --- a/crates/coop/src/views/welcome.rs +++ b/crates/coop/src/views/welcome.rs @@ -2,11 +2,11 @@ use gpui::{ div, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Window, }; +use theme::ActiveTheme; use ui::{ button::Button, dock_area::panel::{Panel, PanelEvent}, popup_menu::PopupMenu, - theme::{scale::ColorScaleStep, ActiveTheme}, StyledExt, }; @@ -87,12 +87,12 @@ impl Render for Welcome { svg() .path("brand/coop.svg") .size_12() - .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), + .text_color(cx.theme().elevated_surface_background), ) .child( div() .child("coop on nostr.") - .text_color(cx.theme().base.step(cx, ColorScaleStep::NINE)) + .text_color(cx.theme().text_placeholder) .font_semibold() .text_sm(), ), diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml new file mode 100644 index 00000000..316455e6 --- /dev/null +++ b/crates/theme/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "theme" +version.workspace = true +edition.workspace = true +publish.workspace = true + +[dependencies] +gpui.workspace = true +anyhow.workspace = true +log.workspace = true diff --git a/crates/theme/LICENSE b/crates/theme/LICENSE new file mode 100644 index 00000000..fd50777c --- /dev/null +++ b/crates/theme/LICENSE @@ -0,0 +1 @@ +../../LICENSE diff --git a/crates/theme/README.md b/crates/theme/README.md new file mode 100644 index 00000000..01fa48da --- /dev/null +++ b/crates/theme/README.md @@ -0,0 +1 @@ +CREDITS: [zed/theme](https://github.com/zed-industries/zed/tree/main/crates/theme) diff --git a/crates/ui/src/theme/colors.rs b/crates/theme/src/colors.rs similarity index 99% rename from crates/ui/src/theme/colors.rs rename to crates/theme/src/colors.rs index 24f23f3f..4b8f366b 100644 --- a/crates/ui/src/theme/colors.rs +++ b/crates/theme/src/colors.rs @@ -1,7 +1,15 @@ -use super::scale::{ColorScaleSet, ColorScales}; -use crate::theme::scale::ColorScale; 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 diff --git a/crates/theme/src/lib.rs b/crates/theme/src/lib.rs new file mode 100644 index 00000000..9d77cb75 --- /dev/null +++ b/crates/theme/src/lib.rs @@ -0,0 +1,375 @@ +use std::ops::{Deref, DerefMut}; + +use colors::{brand, hsl, neutral}; +use gpui::{black, px, white, App, Global, Hsla, Pixels, SharedString, Window, WindowAppearance}; + +mod colors; +mod scale; + +pub fn init(cx: &mut App) { + Theme::sync_system_appearance(None, cx); +} + +#[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. + 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, + /// 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 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, + /// Window border color. + /// + /// # Platform specific: + /// + /// This is only works on Linux, other platforms we can't change the window border color. + pub window_border: Hsla, +} + +/// The default colors for the theme. +/// +/// Themes that do not specify all colors are refined off of these defaults. +impl ThemeColor { + /// Returns the default colors for light themes. + /// + /// Themes that do not specify all colors are refined off of these defaults. + pub fn light() -> Self { + Self { + 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(), + 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_10(), + element_disabled: brand().light_alpha().step_3(), + drop_target_background: brand().light_alpha().step_2(), + ghost_element_background: gpui::transparent_black(), + ghost_element_hover: neutral().light_alpha().step_3(), + ghost_element_active: neutral().light_alpha().step_4(), + ghost_element_selected: neutral().light_alpha().step_5(), + ghost_element_disabled: neutral().light_alpha().step_3(), + 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(), + 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: 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(), + window_border: hsl(240.0, 5.9, 78.0), + } + } + + /// Returns the default colors for dark themes. + /// + /// Themes that do not specify all colors are refined off of these defaults. + pub fn dark() -> Self { + Self { + 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().light().step_3(), + elevated_surface_background: neutral().dark().step_3(), + surface_background: neutral().dark().step_2(), + background: neutral().dark().step_1(), + element_foreground: brand().dark().step_12(), + element_background: brand().dark().step_9(), + element_hover: brand().dark_alpha().step_10(), + element_active: brand().dark().step_10(), + element_selected: brand().dark().step_10(), + element_disabled: brand().dark_alpha().step_3(), + drop_target_background: brand().dark_alpha().step_2(), + 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_3(), + 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(), + 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: 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(), + window_border: hsl(240.0, 3.7, 28.0), + } + } +} + +pub trait ActiveTheme { + fn theme(&self) -> &Theme; +} + +impl ActiveTheme for App { + #[inline(always)] + fn theme(&self) -> &Theme { + Theme::global(self) + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq, Hash)] +pub enum ThemeMode { + Light, + #[default] + Dark, +} + +impl ThemeMode { + pub fn is_dark(&self) -> bool { + matches!(self, Self::Dark) + } + + /// Return lower_case theme name: `light`, `dark`. + pub fn name(&self) -> &'static str { + match self { + ThemeMode::Light => "light", + ThemeMode::Dark => "dark", + } + } +} + +impl From for ThemeMode { + fn from(appearance: WindowAppearance) -> Self { + match appearance { + WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark, + WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light, + } + } +} + +#[derive(Debug, Clone)] +pub struct Theme { + pub colors: ThemeColor, + pub mode: ThemeMode, + pub font_family: SharedString, + pub font_size: Pixels, + pub radius: Pixels, +} + +impl Deref for Theme { + type Target = ThemeColor; + + fn deref(&self) -> &Self::Target { + &self.colors + } +} + +impl DerefMut for Theme { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.colors + } +} + +impl Global for Theme {} + +impl Theme { + /// Returns the global theme reference + pub fn global(cx: &App) -> &Theme { + cx.global::() + } + + /// Returns the global theme mutable reference + pub fn global_mut(cx: &mut App) -> &mut Theme { + cx.global_mut::() + } + + /// Returns true if the theme is dark. + pub fn is_dark(&self) -> bool { + self.mode.is_dark() + } + + /// Sync the theme with the system appearance + pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) { + let appearance = window + .as_ref() + .map(|window| window.appearance()) + .unwrap_or_else(|| cx.window_appearance()); + + Self::change(appearance, window, cx); + } + + pub fn change(mode: impl Into, window: Option<&mut Window>, cx: &mut App) { + let mode = mode.into(); + let colors = match mode { + ThemeMode::Light => ThemeColor::light(), + ThemeMode::Dark => ThemeColor::dark(), + }; + + if !cx.has_global::() { + let theme = Theme::from(colors); + cx.set_global(theme); + } + + let theme = cx.global_mut::(); + + theme.mode = mode; + theme.colors = colors; + + if let Some(window) = window { + window.refresh(); + } + } +} + +impl From for Theme { + fn from(colors: ThemeColor) -> Self { + let mode = ThemeMode::default(); + + Theme { + font_size: px(15.), + font_family: if cfg!(target_os = "macos") { + ".SystemUIFont".into() + } else if cfg!(target_os = "windows") { + "Segoe UI".into() + } else { + "FreeMono".into() + }, + radius: px(5.), + mode, + colors, + } + } +} diff --git a/crates/ui/src/theme/scale.rs b/crates/theme/src/scale.rs similarity index 91% rename from crates/ui/src/theme/scale.rs rename to crates/theme/src/scale.rs index 58ac93fb..1434b4f2 100644 --- a/crates/ui/src/theme/scale.rs +++ b/crates/theme/src/scale.rs @@ -1,5 +1,4 @@ -use crate::theme::{ActiveTheme, Appearance}; -use gpui::{App, Hsla, SharedString}; +use gpui::{Hsla, SharedString}; /// A collection of colors that are used to style the UI. /// @@ -278,25 +277,4 @@ impl ColorScaleSet { pub fn dark_alpha(&self) -> &ColorScale { &self.dark_alpha } - - pub fn step(&self, cx: &App, step: ColorScaleStep) -> Hsla { - match cx.theme().appearance { - Appearance::Light => self.light().step(step), - Appearance::Dark => self.dark().step(step), - } - } - - pub fn step_alpha(&self, cx: &App, step: ColorScaleStep) -> Hsla { - match cx.theme().appearance { - Appearance::Light => self.light_alpha.step(step), - Appearance::Dark => self.dark_alpha.step(step), - } - } - - pub fn darken(&self, cx: &App) -> Hsla { - match cx.theme().appearance { - Appearance::Light => self.light.step_12(), - Appearance::Dark => self.dark.step_1(), - } - } } diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml index 7429d5b1..49430bf8 100644 --- a/crates/ui/Cargo.toml +++ b/crates/ui/Cargo.toml @@ -6,6 +6,7 @@ publish.workspace = true [dependencies] common = { path = "../common" } +theme = { path = "../theme" } nostr-sdk.workspace = true gpui.workspace = true diff --git a/crates/ui/src/button.rs b/crates/ui/src/button.rs index f582914e..b07cf6f2 100644 --- a/crates/ui/src/button.rs +++ b/crates/ui/src/button.rs @@ -1,35 +1,23 @@ -use crate::{ - indicator::Indicator, - theme::{scale::ColorScaleStep, ActiveTheme}, - tooltip::Tooltip, - Disableable, Icon, Selectable, Sizable, Size, StyledExt, -}; use gpui::{ - div, prelude::FluentBuilder as _, px, relative, AnyElement, App, ClickEvent, Corners, Div, - Edges, ElementId, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, - RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, Window, + div, prelude::FluentBuilder as _, relative, AnyElement, App, ClickEvent, Div, ElementId, Hsla, + InteractiveElement, IntoElement, MouseButton, ParentElement, RenderOnce, SharedString, + StatefulInteractiveElement as _, Styled, Window, +}; +use theme::ActiveTheme; + +use crate::{ + indicator::Indicator, tooltip::Tooltip, Disableable, Icon, Selectable, Sizable, Size, StyledExt, }; pub enum ButtonRounded { - None, - Small, - Medium, - Large, - Size(Pixels), -} - -impl From for ButtonRounded { - fn from(px: Pixels) -> Self { - ButtonRounded::Size(px) - } + Normal, + Full, } #[derive(Clone, Copy, PartialEq, Eq)] pub struct ButtonCustomVariant { color: Hsla, foreground: Hsla, - border: Hsla, - shadow: bool, hover: Hsla, active: Hsla, } @@ -66,12 +54,10 @@ pub trait ButtonVariants: Sized { impl ButtonCustomVariant { pub fn new(_window: &Window, cx: &App) -> Self { Self { - color: cx.theme().accent.step(cx, ColorScaleStep::NINE), - foreground: cx.theme().accent.step(cx, ColorScaleStep::ONE), - border: cx.theme().accent.step(cx, ColorScaleStep::TEN), - hover: cx.theme().accent.step(cx, ColorScaleStep::TEN), - active: cx.theme().accent.step(cx, ColorScaleStep::ELEVEN), - shadow: true, + color: cx.theme().element_background, + foreground: cx.theme().element_foreground, + hover: cx.theme().element_hover, + active: cx.theme().element_active, } } @@ -85,11 +71,6 @@ impl ButtonCustomVariant { self } - pub fn border(mut self, color: Hsla) -> Self { - self.border = color; - self - } - pub fn hover(mut self, color: Hsla) -> Self { self.hover = color; self @@ -99,11 +80,6 @@ impl ButtonCustomVariant { self.active = color; self } - - pub fn shadow(mut self, shadow: bool) -> Self { - self.shadow = shadow; - self - } } /// The variant of the Button. @@ -149,12 +125,9 @@ pub struct Button { disabled: bool, variant: ButtonVariant, rounded: ButtonRounded, - border_corners: Corners, - border_edges: Edges, size: Size, reverse: bool, bold: bool, - centered: bool, tooltip: Option, on_click: OnClick, loading: bool, @@ -179,9 +152,7 @@ impl Button { disabled: false, selected: false, variant: ButtonVariant::default(), - rounded: ButtonRounded::Medium, - border_corners: Corners::all(true), - border_edges: Edges::all(true), + rounded: ButtonRounded::Normal, size: Size::Medium, tooltip: None, on_click: None, @@ -189,7 +160,6 @@ impl Button { loading: false, reverse: false, bold: false, - centered: true, children: Vec::new(), loading_icon: None, } @@ -201,18 +171,6 @@ impl Button { self } - /// Set the border corners side of the Button. - pub(crate) fn border_corners(mut self, corners: impl Into>) -> Self { - self.border_corners = corners.into(); - self - } - - /// Set the border edges of the Button. - pub(crate) fn border_edges(mut self, edges: impl Into>) -> Self { - self.border_edges = edges.into(); - self - } - /// Set label to the Button, if no label is set, the button will be in Icon Button mode. pub fn label(mut self, label: impl Into) -> Self { self.label = Some(label.into()); @@ -243,11 +201,6 @@ impl Button { self } - pub fn not_centered(mut self) -> Self { - self.centered = false; - self - } - pub fn bold(mut self) -> Self { self.bold = true; self @@ -335,11 +288,12 @@ impl RenderOnce for Button { .id(self.id) .flex() .items_center() - .when(self.centered, |this| this.justify_center()) + .justify_center() .cursor_pointer() .overflow_hidden() - .when(cx.theme().shadow && normal_style.shadow, |this| { - this.shadow_sm() + .map(|this| match self.rounded { + ButtonRounded::Normal => this.rounded(cx.theme().radius), + ButtonRounded::Full => this.rounded_full(), }) .when(!style.no_padding(), |this| { if self.label.is_none() && self.children.is_empty() { @@ -361,50 +315,20 @@ impl RenderOnce for Button { } } }) - .when( - self.border_corners.top_left && self.border_corners.bottom_left, - |this| match self.rounded { - ButtonRounded::Small => this.rounded_l(px(cx.theme().radius * 0.5)), - ButtonRounded::Medium => this.rounded_l(px(cx.theme().radius)), - ButtonRounded::Large => this.rounded_l(px(cx.theme().radius * 1.6)), - ButtonRounded::Size(px) => this.rounded_l(px), - ButtonRounded::None => this.rounded_none(), - }, - ) - .when( - self.border_corners.top_right && self.border_corners.bottom_right, - |this| match self.rounded { - ButtonRounded::Small => this.rounded_r(px(cx.theme().radius * 0.5)), - ButtonRounded::Medium => this.rounded_r(px(cx.theme().radius)), - ButtonRounded::Large => this.rounded_r(px(cx.theme().radius * 1.6)), - ButtonRounded::Size(px) => this.rounded_r(px), - ButtonRounded::None => this.rounded_none(), - }, - ) - .when(self.border_edges.left, |this| this.border_l_1()) - .when(self.border_edges.right, |this| this.border_r_1()) - .when(self.border_edges.top, |this| this.border_t_1()) - .when(self.border_edges.bottom, |this| this.border_b_1()) - .text_color(normal_style.fg) .when(self.selected, |this| { let selected_style = style.selected(window, cx); - this.bg(selected_style.bg) - .border_color(selected_style.border) - .text_color(selected_style.fg) + this.bg(selected_style.bg).text_color(selected_style.fg) }) .when(!self.disabled && !self.selected, |this| { - this.border_color(normal_style.border) - .bg(normal_style.bg) + this.bg(normal_style.bg) .when(normal_style.underline, |this| this.text_decoration_1()) .hover(|this| { let hover_style = style.hovered(window, cx); - this.bg(hover_style.bg).border_color(hover_style.border) + this.bg(hover_style.bg) }) .active(|this| { let active_style = style.active(window, cx); - this.bg(active_style.bg) - .border_color(active_style.border) - .text_color(active_style.fg) + this.bg(active_style.bg).text_color(active_style.fg) }) }) .when_some( @@ -427,9 +351,9 @@ impl RenderOnce for Button { this.cursor_not_allowed() .bg(disabled_style.bg) .text_color(disabled_style.fg) - .border_color(disabled_style.border) .shadow_none() }) + .text_color(normal_style.fg) .child({ div() .flex() @@ -474,45 +398,26 @@ impl RenderOnce for Button { struct ButtonVariantStyle { bg: Hsla, - border: Hsla, fg: Hsla, underline: bool, - shadow: bool, } impl ButtonVariant { fn bg_color(&self, _window: &Window, cx: &App) -> Hsla { match self { - ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE), + ButtonVariant::Primary => cx.theme().element_background, ButtonVariant::Custom(colors) => colors.color, - _ => cx.theme().transparent, + _ => cx.theme().ghost_element_background, } } fn text_color(&self, _window: &Window, cx: &App) -> Hsla { match self { - ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() { - "Sky" => cx.theme().base.darken(cx), - "Mint" => cx.theme().base.darken(cx), - "Lime" => cx.theme().base.darken(cx), - "Amber" => cx.theme().base.darken(cx), - "Yellow" => cx.theme().base.darken(cx), - _ => cx.theme().accent.step(cx, ColorScaleStep::ONE), - }, - ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE), - ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE), + ButtonVariant::Primary => cx.theme().element_foreground, + ButtonVariant::Link => cx.theme().text_accent, + ButtonVariant::Ghost => cx.theme().text_muted, ButtonVariant::Custom(colors) => colors.foreground, - _ => cx.theme().base.step(cx, ColorScaleStep::TWELVE), - } - } - - fn border_color(&self, _window: &Window, cx: &App) -> Hsla { - match self { - ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE), - ButtonVariant::Ghost | ButtonVariant::Link | ButtonVariant::Text => { - cx.theme().transparent - } - ButtonVariant::Custom(colors) => colors.border, + _ => cx.theme().text, } } @@ -520,142 +425,79 @@ impl ButtonVariant { matches!(self, ButtonVariant::Link) } - fn shadow(&self, _window: &Window, _cx: &App) -> bool { - match self { - ButtonVariant::Primary => true, - ButtonVariant::Custom(c) => c.shadow, - _ => false, - } - } - fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle { let bg = self.bg_color(window, cx); - let border = self.border_color(window, cx); let fg = self.text_color(window, cx); let underline = self.underline(window, cx); - let shadow = self.shadow(window, cx); - ButtonVariantStyle { - bg, - border, - fg, - underline, - shadow, - } + ButtonVariantStyle { bg, fg, underline } } fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle { let bg = match self { - ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN), - ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::FOUR), - ButtonVariant::Link => cx.theme().transparent, - ButtonVariant::Text => cx.theme().transparent, + ButtonVariant::Primary => cx.theme().element_hover, + ButtonVariant::Ghost => cx.theme().ghost_element_hover, + ButtonVariant::Link => cx.theme().ghost_element_background, + ButtonVariant::Text => cx.theme().ghost_element_background, ButtonVariant::Custom(colors) => colors.hover, }; - let border = self.border_color(window, cx); let fg = match self { - ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE), - ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN), + ButtonVariant::Ghost => cx.theme().text, + ButtonVariant::Link => cx.theme().text_accent, _ => self.text_color(window, cx), }; let underline = self.underline(window, cx); - let shadow = self.shadow(window, cx); - ButtonVariantStyle { - bg, - border, - fg, - underline, - shadow, - } + ButtonVariantStyle { bg, fg, underline } } fn active(&self, window: &Window, cx: &App) -> ButtonVariantStyle { let bg = match self { - ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN), - ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE), - ButtonVariant::Link => cx.theme().transparent, - ButtonVariant::Text => cx.theme().transparent, + ButtonVariant::Primary => cx.theme().element_active, + ButtonVariant::Ghost => cx.theme().ghost_element_active, ButtonVariant::Custom(colors) => colors.active, + _ => cx.theme().ghost_element_background, }; - let fg = match self { - ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE), - ButtonVariant::Text => cx.theme().base.step(cx, ColorScaleStep::ELEVEN), + ButtonVariant::Link => cx.theme().text_accent, + ButtonVariant::Text => cx.theme().text, _ => self.text_color(window, cx), }; - - let border = self.border_color(window, cx); let underline = self.underline(window, cx); - let shadow = self.shadow(window, cx); - ButtonVariantStyle { - bg, - border, - fg, - underline, - shadow, - } + ButtonVariantStyle { bg, fg, underline } } fn selected(&self, window: &Window, cx: &App) -> ButtonVariantStyle { let bg = match self { - ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN), - ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE), - ButtonVariant::Link => cx.theme().transparent, - ButtonVariant::Text => cx.theme().transparent, + ButtonVariant::Primary => cx.theme().element_selected, + ButtonVariant::Ghost => cx.theme().ghost_element_selected, ButtonVariant::Custom(colors) => colors.active, + _ => cx.theme().ghost_element_background, }; - let fg = match self { - ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN), - ButtonVariant::Text => cx.theme().accent.step(cx, ColorScaleStep::TEN), + ButtonVariant::Link => cx.theme().text_accent, + ButtonVariant::Text => cx.theme().text, _ => self.text_color(window, cx), }; - - let border = self.border_color(window, cx); let underline = self.underline(window, cx); - let shadow = self.shadow(window, cx); - ButtonVariantStyle { - bg, - border, - fg, - underline, - shadow, - } + ButtonVariantStyle { bg, fg, underline } } fn disabled(&self, window: &Window, cx: &App) -> ButtonVariantStyle { let bg = match self { ButtonVariant::Link | ButtonVariant::Ghost | ButtonVariant::Text => { - cx.theme().transparent + cx.theme().ghost_element_disabled } - _ => cx.theme().base.step(cx, ColorScaleStep::THREE), + _ => cx.theme().element_disabled, }; - let fg = match self { - ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() { - "Sky" => cx.theme().base.darken(cx), - "Mint" => cx.theme().base.darken(cx), - "Lime" => cx.theme().base.darken(cx), - "Amber" => cx.theme().base.darken(cx), - "Yellow" => cx.theme().base.darken(cx), - _ => cx.theme().accent.step(cx, ColorScaleStep::ONE), - }, - _ => cx.theme().base.step(cx, ColorScaleStep::ELEVEN), + ButtonVariant::Primary => cx.theme().text_muted, // TODO: use a different color? + _ => cx.theme().text_muted, }; - - let border = bg; let underline = self.underline(window, cx); - let shadow = false; - ButtonVariantStyle { - bg, - border, - fg, - underline, - shadow, - } + ButtonVariantStyle { bg, fg, underline } } } diff --git a/crates/ui/src/button_group.rs b/crates/ui/src/button_group.rs deleted file mode 100644 index 34519567..00000000 --- a/crates/ui/src/button_group.rs +++ /dev/null @@ -1,201 +0,0 @@ -use gpui::{ - div, prelude::FluentBuilder as _, App, Corners, Div, Edges, ElementId, InteractiveElement, - IntoElement, ParentElement, RenderOnce, StatefulInteractiveElement as _, Styled, Window, -}; -use std::{cell::Cell, rc::Rc}; - -use crate::{ - button::{Button, ButtonVariant, ButtonVariants}, - Disableable, Sizable, Size, -}; - -type OnClick = Option, &mut Window, &mut App) + 'static>>; - -/// A ButtonGroup element, to wrap multiple buttons in a group. -#[derive(IntoElement)] -pub struct ButtonGroup { - pub base: Div, - id: ElementId, - children: Vec