diff --git a/crates/assets/assets/.DS_Store b/crates/assets/assets/.DS_Store index 3a779e1..5db0a1f 100644 Binary files a/crates/assets/assets/.DS_Store and b/crates/assets/assets/.DS_Store differ diff --git a/crates/assets/assets/backgrounds/banner.jpg b/crates/assets/assets/backgrounds/banner.jpg deleted file mode 100644 index d075b55..0000000 Binary files a/crates/assets/assets/backgrounds/banner.jpg and /dev/null differ diff --git a/crates/assets/assets/backgrounds/banner1.jpg b/crates/assets/assets/backgrounds/banner1.jpg new file mode 100644 index 0000000..89995e3 Binary files /dev/null and b/crates/assets/assets/backgrounds/banner1.jpg differ diff --git a/crates/assets/assets/backgrounds/banner2.jpg b/crates/assets/assets/backgrounds/banner2.jpg new file mode 100644 index 0000000..2654893 Binary files /dev/null and b/crates/assets/assets/backgrounds/banner2.jpg differ diff --git a/crates/assets/assets/backgrounds/banner3.jpg b/crates/assets/assets/backgrounds/banner3.jpg new file mode 100644 index 0000000..381235e Binary files /dev/null and b/crates/assets/assets/backgrounds/banner3.jpg differ diff --git a/crates/assets/assets/backgrounds/headline.png b/crates/assets/assets/backgrounds/headline.png new file mode 100644 index 0000000..6fe5467 Binary files /dev/null and b/crates/assets/assets/backgrounds/headline.png differ diff --git a/crates/assets/src/lib.rs b/crates/assets/src/lib.rs index 3fd3419..7e6fc8a 100644 --- a/crates/assets/src/lib.rs +++ b/crates/assets/src/lib.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use gpui::{App, AssetSource, Result, SharedString}; +use gpui::{AssetSource, Result, SharedString}; use gpui_component::IconNamed; use rust_embed::RustEmbed; @@ -33,17 +33,12 @@ impl AssetSource for Assets { } impl Assets { - /// Returns the embedded theme files as `(file name, JSON content)` pairs, - /// e.g. `("signed.json", ...)`. The content is a `ThemeSet` that can be - /// loaded into the [`ThemeRegistry`](gpui_component::ThemeRegistry). pub fn themes(&self) -> Vec<(String, String)> { Self::iter() .filter(|path| path.starts_with("themes/")) .filter_map(|path| { let data = Self::get(path.as_ref())?; let name = path.strip_prefix("themes/").unwrap_or(path.as_ref()); - // Debug builds read files from disk (owned), release builds - // embed them in the binary (borrowed). let content = match data.data { std::borrow::Cow::Borrowed(bytes) => { std::str::from_utf8(bytes).ok()?.to_owned() @@ -54,22 +49,6 @@ impl Assets { }) .collect() } - - pub fn load_fonts(&self, cx: &App) -> anyhow::Result<()> { - let font_paths = self.list("fonts")?; - let mut embedded_fonts = Vec::new(); - for font_path in font_paths { - if font_path.ends_with(".ttf") { - let font_bytes = cx - .asset_source() - .load(&font_path)? - .expect("Assets should never return None"); - embedded_fonts.push(font_bytes); - } - } - - cx.text_system().add_fonts(embedded_fonts) - } } pub enum CustomIconName { diff --git a/crates/workspace/src/views/sidebar/mod.rs b/crates/workspace/src/views/sidebar/mod.rs index a7581f4..dad9bac 100644 --- a/crates/workspace/src/views/sidebar/mod.rs +++ b/crates/workspace/src/views/sidebar/mod.rs @@ -1,4 +1,5 @@ use std::ops::Range; +use std::time::{SystemTime, UNIX_EPOCH}; use assets::CustomIconName; use dock::{ @@ -11,6 +12,7 @@ use gpui::{ Focusable, ObjectFit, Render, SharedString, StyleRefinement, Subscription, WeakEntity, Window, div, img, px, uniform_list, }; +use gpui_base::Button as BaseButton; use gpui_component::avatar::Avatar; use gpui_component::button::{Button, ButtonVariants}; use gpui_component::input::InputState; @@ -41,6 +43,9 @@ pub struct SidebarPanel { /// Observes the current user's repo store so the list re-renders. my_repos_subscription: Option, logged_in: bool, + /// Banner artwork shown behind the sign-in screen, + /// picked at random from the bundled `backgrounds/` assets. + banner: SharedString, _subscription: Subscription, } @@ -57,6 +62,7 @@ impl SidebarPanel { } BackendEvent::SignerRequired => { this.logged_in = false; + this.banner = pick_banner(); this.my_repos = None; this.my_repos_subscription = None; } @@ -72,6 +78,7 @@ impl SidebarPanel { my_repos: None, my_repos_subscription: None, logged_in, + banner: pick_banner(), _subscription: subscription, }; @@ -290,20 +297,28 @@ impl SidebarPanel { } /// Sign-in placeholder shown while logged out: the banner artwork fills the - /// panel behind a scrim that fades into the theme's sidebar color, keeping - /// the CTA buttons readable on a clean surface. - fn render_sign_in(&self, cx: &mut Context) -> Div { - const HEADLINE: &str = "Forge together. Ship anything."; - const SUBHEADLINE: &str = "An open forge for humans and agents"; - + /// panel behind a scrim that ends in a solid black band, keeping the CTA + /// buttons readable on a clean dark surface in both themes. + fn render_sign_in(&self, window: &mut Window, cx: &mut Context) -> Div { v_flex() .size_full() .relative() .bg(cx.theme().sidebar) .text_color(cx.theme().sidebar_foreground) + .child(title_bar_drag_handlers( + div() + .id("onboarding-drag") + .absolute() + .h_12() + .w_full() + .top_0() + .left_0(), + window, + cx, + )) .child( div().absolute().inset_0().child( - img("backgrounds/banner.jpg") + img(self.banner.clone()) .size_full() .object_fit(ObjectFit::Cover), ), @@ -311,33 +326,39 @@ impl SidebarPanel { .child( v_flex() .size_full() - .items_center() .justify_end() .p_4() .mb_4() .gap_4() - .child( - v_flex() - .text_color(gpui::white()) - .child(div().text_sm().font_semibold().child(HEADLINE)) - .child(div().text_xs().child(SUBHEADLINE)), - ) + .child(img("backgrounds/headline.png").max_w_48()) .child( v_flex() .gap_1() .w_full() .child( - Button::new("onboarding") - .label("Join now") - .primary() + BaseButton::new("onboarding") + .h_flex() + .h_8() + .px_2() + .bg(cx.theme().primary) + .hover(|this| this.bg(cx.theme().primary_hover)) + .active(|this| this.bg(cx.theme().primary_active)) + .text_color(cx.theme().primary_foreground) + .child(div().text_sm().font_semibold().child("Join now")) .on_click(cx.listener(|this, _ev, window, cx| { this.open_onboarding(window, cx) })), ) .child( - Button::new("import") - .label("Import identity") - .ghost() + BaseButton::new("onboarding") + .h_flex() + .h_8() + .px_2() + .text_color(gpui::white()) + .bg(gpui::white().opacity(0.1)) + .hover(|this| this.bg(gpui::white().opacity(0.2))) + .active(|this| this.bg(gpui::white().opacity(0.4))) + .child(div().text_sm().child("Import identity")) .on_click(cx.listener(|this, _ev, window, cx| { this.open_import(window, cx) })), @@ -347,6 +368,16 @@ impl SidebarPanel { } } +fn pick_banner() -> SharedString { + let num = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system clock before unix epoch") + .subsec_nanos() + % 3 + + 1; + format!("backgrounds/banner{num}.jpg").into() +} + impl BasePanel for SidebarPanel { fn panel_name(&self) -> &'static str { "sidebar" @@ -374,7 +405,7 @@ impl Focusable for SidebarPanel { impl Render for SidebarPanel { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { if !self.logged_in { - return self.render_sign_in(cx); + return self.render_sign_in(window, cx); } let backend = Backend::global(cx); diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 9ee7edf..930230c 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -14,9 +14,6 @@ fn main() { .with_assets(Assets) .with_http_client(Arc::new(reqwest_client::ReqwestClient::new())) .run(move |cx| { - // Set app identity - cx.set_app_identity("su.reya.signed", "Signed"); - // Initialize components gpui_component::init(cx); @@ -63,6 +60,9 @@ fn main() { std::fs::create_dir_all(paths::repos_dir()).ok(); signed_state::GitStore::set_global(paths::repos_dir().clone(), cx); + // Set app identity + cx.set_app_identity("su.reya.signed", "Signed"); + // Set up the window options let bounds = Bounds::centered(None, size(px(1120.0), px(750.0)), cx);