From f6caf33e3463ce80af6720cf5dad71dfb8121f57 Mon Sep 17 00:00:00 2001 From: Fabrice Bellamy Date: Sat, 21 Feb 2026 22:24:16 +0100 Subject: [PATCH 1/5] ui tweaks for macOS --- src/main.rs | 2 +- src/ui/components/sidebar.rs | 12 +++++----- src/ui/rootview.rs | 43 +++++++++++++++++++----------------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/main.rs b/src/main.rs index b4cd353..896ef0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,7 +57,7 @@ fn main() { title: Some("PicoForge".into()), appears_transparent: true, // TODO: This option needs to be tested and adjusted on macos - traffic_light_position: Some(gpui::point(px(12.0), px(12.0))), + traffic_light_position: Some(gpui::point(px(9.0), px(9.0))), }), // Render our own window decorations(shadows and resize attack area) for linux/bsd. diff --git a/src/ui/components/sidebar.rs b/src/ui/components/sidebar.rs index 5fae191..a8d9334 100644 --- a/src/ui/components/sidebar.rs +++ b/src/ui/components/sidebar.rs @@ -73,13 +73,11 @@ impl AppSidebar { .border_color(border_color) .w(width) .child({ - let header = h_flex() - .w_full() - .items_center() - .bg(sidebar_bg) - // .border_r_1() - // .border_color(border_color) - .pt_4(); + #[cfg(not(target_os = "macos"))] + let header = h_flex().w_full().items_center().bg(sidebar_bg).pt_4(); + + #[cfg(target_os = "macos")] + let header = h_flex().w_full().items_center().bg(sidebar_bg).pt_8(); let current_width = width; let t = ((current_width - px(48.)) / (px(255.) - px(48.))).clamp(0.0, 1.0); diff --git a/src/ui/rootview.rs b/src/ui/rootview.rs index af1af48..08e2071 100644 --- a/src/ui/rootview.rs +++ b/src/ui/rootview.rs @@ -137,26 +137,29 @@ impl Render for ApplicationRoot { .child( v_flex() .size_full() - .child( - TitleBar::new().bg(cx.theme().title_bar).child( - h_flex() - .w_full() - .justify_between() - .bg(cx.theme().title_bar) - .items_center() - .cursor(gpui::CursorStyle::OpenHand) - .child( - Button::new("sidebar_toggle") - .ghost() - .icon(IconName::PanelLeft) - .on_click(cx.listener(|this, _, _, _| { - this.is_sidebar_collapsed = - !this.is_sidebar_collapsed; - })) - .tooltip("Toggle Sidebar"), - ), - ), - ) + .child(TitleBar::new().bg(cx.theme().title_bar).child({ + #[cfg(not(target_os = "macos"))] + let container = h_flex(); + + #[cfg(target_os = "macos")] + let container = h_flex().ml(gpui::px(-72.)); + + container + .w_full() + .justify_between() + .bg(cx.theme().title_bar) + .items_center() + .cursor(gpui::CursorStyle::OpenHand) + .child( + Button::new("sidebar_toggle") + .ghost() + .icon(IconName::PanelLeft) + .on_click(cx.listener(|this, _, _, _| { + this.is_sidebar_collapsed = !this.is_sidebar_collapsed; + })) + .tooltip("Toggle Sidebar"), + ) + })) .child( v_flex() .min_h(px(0.)) From 37c56cbb87c3a26fd43ea2f629445f2f0793d666 Mon Sep 17 00:00:00 2001 From: Fabrice Bellamy Date: Sat, 21 Feb 2026 22:36:16 +0100 Subject: [PATCH 2/5] fix sidebar toggle button position when sidebar is minimized --- src/ui/rootview.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ui/rootview.rs b/src/ui/rootview.rs index 08e2071..fc39e30 100644 --- a/src/ui/rootview.rs +++ b/src/ui/rootview.rs @@ -142,7 +142,10 @@ impl Render for ApplicationRoot { let container = h_flex(); #[cfg(target_os = "macos")] - let container = h_flex().ml(gpui::px(-72.)); + let container = { + let offset = self.sidebar_width.min(gpui::px(72.)); + h_flex().ml(gpui::px(-offset.0)) + }; container .w_full() From 997b1056668cba87977699f9126eb8a6e272ba7b Mon Sep 17 00:00:00 2001 From: Fabrice Bellamy Date: Sat, 21 Feb 2026 22:40:40 +0100 Subject: [PATCH 3/5] fix compilation error introduced by previous commit --- src/ui/rootview.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/rootview.rs b/src/ui/rootview.rs index fc39e30..cab4a60 100644 --- a/src/ui/rootview.rs +++ b/src/ui/rootview.rs @@ -144,7 +144,7 @@ impl Render for ApplicationRoot { #[cfg(target_os = "macos")] let container = { let offset = self.sidebar_width.min(gpui::px(72.)); - h_flex().ml(gpui::px(-offset.0)) + h_flex().ml(-offset) }; container From 5f161c8be43c0650561bd9f952a946b2277da888 Mon Sep 17 00:00:00 2001 From: Fabrice Bellamy Date: Sat, 21 Feb 2026 22:58:51 +0100 Subject: [PATCH 4/5] quit the application when main window is closed on macOS --- src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.rs b/src/main.rs index 896ef0b..f4d8548 100644 --- a/src/main.rs +++ b/src/main.rs @@ -82,5 +82,12 @@ fn main() { Ok::<_, anyhow::Error>(()) }) .detach(); + + // Quit the application when the window is closed (specifically needed for macOS) + #[cfg(target_os = "macos")] + { + cx.on_window_closed(|cx| cx.quit()) + .detach(); + } }); } From a034d0d22ef531e55196bff9b27754e757bbc6c0 Mon Sep 17 00:00:00 2001 From: Fabrice Bellamy Date: Sun, 22 Feb 2026 10:38:22 +0100 Subject: [PATCH 5/5] change topbar/sidebar layout --- src/ui/components/sidebar.rs | 4 - src/ui/rootview.rs | 218 ++++++++++++++++------------------- 2 files changed, 100 insertions(+), 122 deletions(-) diff --git a/src/ui/components/sidebar.rs b/src/ui/components/sidebar.rs index a8d9334..aa41cd7 100644 --- a/src/ui/components/sidebar.rs +++ b/src/ui/components/sidebar.rs @@ -73,12 +73,8 @@ impl AppSidebar { .border_color(border_color) .w(width) .child({ - #[cfg(not(target_os = "macos"))] let header = h_flex().w_full().items_center().bg(sidebar_bg).pt_4(); - #[cfg(target_os = "macos")] - let header = h_flex().w_full().items_center().bg(sidebar_bg).pt_8(); - let current_width = width; let t = ((current_width - px(48.)) / (px(255.) - px(48.))).clamp(0.0, 1.0); diff --git a/src/ui/rootview.rs b/src/ui/rootview.rs index cab4a60..c428d2e 100644 --- a/src/ui/rootview.rs +++ b/src/ui/rootview.rs @@ -116,127 +116,109 @@ impl Render for ApplicationRoot { let dialog_layer = Root::render_dialog_layer(window, cx); - div().size_full().overflow_hidden().child( + let title_bar = TitleBar::new().bg(cx.theme().title_bar).child( + h_flex() + .w_full() + .justify_between() + .bg(cx.theme().title_bar) + .items_center() + .cursor(gpui::CursorStyle::OpenHand) + .child( + Button::new("sidebar_toggle") + .ghost() + .icon(IconName::PanelLeft) + .on_click(cx.listener(|this, _, _, _| { + this.is_sidebar_collapsed = !this.is_sidebar_collapsed; + })) + .tooltip("Toggle Sidebar"), + ), + ); + + let content_area = v_flex() + .min_h(px(0.)) + .min_w(px(0.)) + .overflow_y_scrollbar() + .flex_grow() + .bg(cx.theme().background) + .child(match self.active_view { + ActiveView::Home => { + HomeView::build(&self.state, cx.theme(), window.bounds().size.width) + .into_any_element() + } + ActiveView::Passkeys => { + let view = self.passkeys_view.get_or_insert_with(|| { + let view = cx.new(|cx| { + PasskeysView::new( + window, + cx, + self.state.device_status.clone(), + self.state.fido_info.clone(), + ) + }); + cx.subscribe_in( + &view, + window, + |_, _, event: &PasskeysEvent, window, cx| match event { + PasskeysEvent::Notification(msg) => { + window.push_notification(msg.to_string(), cx); + } + PasskeysEvent::CloseDialog => { + window.close_dialog(cx); + } + }, + ) + .detach(); + view + }); + view.clone().into_any_element() + } + ActiveView::Configuration => { + let view = self.config_view.get_or_insert_with(|| { + cx.new(|cx| ConfigView::new(window, cx, self.state.device_status.clone())) + }); + view.clone().into_any_element() + } + ActiveView::Security => SecurityView::build(cx).into_any_element(), + ActiveView::Logs => { + let view = self + .logs_view + .get_or_insert_with(|| cx.new(|cx| LogsView::new(window, cx))); + view.clone().into_any_element() + } + ActiveView::About => AboutView::build(cx.theme()).into_any_element(), + }); + + let sidebar = AppSidebar::new( + self.active_view, + self.sidebar_width, + is_sidebar_collapsed, + self.state.clone(), + ) + .on_select(|this: &mut Self, view, _, _| { + this.active_view = view; + }) + .on_refresh(|this, window, cx| { + this.refresh_device_status(Some(window), cx); + }); + + #[cfg(target_os = "macos")] + let body = v_flex().size_full().child(title_bar).child( h_flex() .size_full() - .child( - AppSidebar::new( - self.active_view, - self.sidebar_width, - is_sidebar_collapsed, - self.state.clone(), - ) - .on_select(|this: &mut Self, view, _, _| { - this.active_view = view; - }) - .on_refresh(|this, window, cx| { - this.refresh_device_status(Some(window), cx); - }) - .render(cx), - ) - .child( - v_flex() - .size_full() - .child(TitleBar::new().bg(cx.theme().title_bar).child({ - #[cfg(not(target_os = "macos"))] - let container = h_flex(); + .child(sidebar.render(cx)) + .child(content_area), + ); - #[cfg(target_os = "macos")] - let container = { - let offset = self.sidebar_width.min(gpui::px(72.)); - h_flex().ml(-offset) - }; + #[cfg(not(target_os = "macos"))] + let body = h_flex() + .size_full() + .child(sidebar.render(cx)) + .child(v_flex().size_full().child(title_bar).child(content_area)); - container - .w_full() - .justify_between() - .bg(cx.theme().title_bar) - .items_center() - .cursor(gpui::CursorStyle::OpenHand) - .child( - Button::new("sidebar_toggle") - .ghost() - .icon(IconName::PanelLeft) - .on_click(cx.listener(|this, _, _, _| { - this.is_sidebar_collapsed = !this.is_sidebar_collapsed; - })) - .tooltip("Toggle Sidebar"), - ) - })) - .child( - v_flex() - .min_h(px(0.)) - .min_w(px(0.)) - .overflow_y_scrollbar() - .flex_grow() - .bg(cx.theme().background) - .child(match self.active_view { - ActiveView::Home => HomeView::build( - &self.state, - cx.theme(), - window.bounds().size.width, - ) - .into_any_element(), - ActiveView::Passkeys => { - let view = self.passkeys_view.get_or_insert_with(|| { - let view = cx.new(|cx| { - PasskeysView::new( - window, - cx, - self.state.device_status.clone(), - self.state.fido_info.clone(), - ) - }); - cx.subscribe_in( - &view, - window, - |_, _, event: &PasskeysEvent, window, cx| { - match event { - PasskeysEvent::Notification(msg) => { - window.push_notification( - msg.to_string(), - cx, - ); - } - PasskeysEvent::CloseDialog => { - window.close_dialog(cx); - } - } - }, - ) - .detach(); - view - }); - view.clone().into_any_element() - } - ActiveView::Configuration => { - let view = self.config_view.get_or_insert_with(|| { - cx.new(|cx| { - ConfigView::new( - window, - cx, - self.state.device_status.clone(), - ) - }) - }); - view.clone().into_any_element() - } - ActiveView::Security => { - SecurityView::build(cx).into_any_element() - } - ActiveView::Logs => { - let view = self.logs_view.get_or_insert_with(|| { - cx.new(|cx| LogsView::new(window, cx)) - }); - view.clone().into_any_element() - } - ActiveView::About => { - AboutView::build(cx.theme()).into_any_element() - } - }), - ), - ) - .children(dialog_layer), - ) + div() + .size_full() + .overflow_hidden() + .child(body) + .children(dialog_layer) } }