2026-01-29 10:09:45 +05:30
|
|
|
use crate::device::types::FullDeviceStatus;
|
|
|
|
|
use crate::ui::components::sidebar::{ActiveView, AppSidebar};
|
2026-01-28 23:36:15 +05:30
|
|
|
use crate::ui::{
|
|
|
|
|
colors,
|
|
|
|
|
views::{
|
|
|
|
|
about::AboutView, config::ConfigView, home::HomeView, logs::LogsView,
|
|
|
|
|
passkeys::PasskeysView, security::SecurityView,
|
|
|
|
|
},
|
2026-01-27 22:51:03 +05:30
|
|
|
};
|
|
|
|
|
use gpui::*;
|
2026-01-28 23:36:15 +05:30
|
|
|
use gpui_component::{
|
2026-01-29 10:09:45 +05:30
|
|
|
ActiveTheme, IconName, TitleBar,
|
2026-01-28 23:36:15 +05:30
|
|
|
button::{Button, ButtonVariants},
|
|
|
|
|
h_flex,
|
|
|
|
|
scroll::ScrollableElement,
|
|
|
|
|
v_flex,
|
|
|
|
|
};
|
2026-01-27 22:51:03 +05:30
|
|
|
|
|
|
|
|
pub struct ApplicationRoot {
|
2026-01-28 17:29:07 +05:30
|
|
|
active_view: ActiveView,
|
|
|
|
|
collapsed: bool,
|
2026-01-28 23:36:15 +05:30
|
|
|
device_status: Option<FullDeviceStatus>,
|
|
|
|
|
device_loading: bool,
|
|
|
|
|
device_error: Option<String>,
|
2026-01-29 00:14:37 +05:30
|
|
|
sidebar_width: Pixels,
|
2026-01-27 22:51:03 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ApplicationRoot {
|
2026-01-28 23:36:15 +05:30
|
|
|
pub fn new(cx: &mut Context<Self>) -> Self {
|
|
|
|
|
let mut this = Self {
|
2026-01-28 17:29:07 +05:30
|
|
|
active_view: ActiveView::Home,
|
|
|
|
|
collapsed: false,
|
2026-01-28 23:36:15 +05:30
|
|
|
device_status: None,
|
|
|
|
|
device_loading: false,
|
|
|
|
|
device_error: None,
|
2026-01-29 00:14:37 +05:30
|
|
|
sidebar_width: px(255.),
|
2026-01-28 23:36:15 +05:30
|
|
|
};
|
|
|
|
|
this.refresh_device_status(cx);
|
|
|
|
|
this
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn refresh_device_status(&mut self, cx: &mut Context<Self>) {
|
|
|
|
|
if self.device_loading {
|
|
|
|
|
return;
|
2026-01-28 17:29:07 +05:30
|
|
|
}
|
2026-01-28 23:36:15 +05:30
|
|
|
|
|
|
|
|
self.device_loading = true;
|
|
|
|
|
self.device_error = None;
|
|
|
|
|
cx.notify();
|
|
|
|
|
|
|
|
|
|
// TODO: Enable async refresh once WeakView/handle type is resolved
|
|
|
|
|
self.device_loading = false;
|
2026-01-28 17:29:07 +05:30
|
|
|
}
|
2026-01-27 22:51:03 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Render for ApplicationRoot {
|
2026-01-29 00:14:37 +05:30
|
|
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
|
|
|
|
let target_width = if self.collapsed { px(48.) } else { px(255.) };
|
|
|
|
|
if (self.sidebar_width - target_width).abs() > px(0.1) {
|
|
|
|
|
self.sidebar_width = self.sidebar_width + (target_width - self.sidebar_width) * 0.2;
|
|
|
|
|
window.request_animation_frame();
|
|
|
|
|
} else {
|
|
|
|
|
self.sidebar_width = target_width;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-29 10:09:45 +05:30
|
|
|
div().size_full().overflow_hidden().child(
|
|
|
|
|
h_flex()
|
|
|
|
|
.size_full()
|
|
|
|
|
.child(
|
|
|
|
|
AppSidebar::new(
|
|
|
|
|
self.active_view,
|
|
|
|
|
self.sidebar_width,
|
|
|
|
|
self.collapsed,
|
|
|
|
|
self.device_status.clone(),
|
|
|
|
|
self.device_error.clone(),
|
|
|
|
|
)
|
|
|
|
|
.on_select(|this: &mut Self, view, _, _| {
|
|
|
|
|
this.active_view = view;
|
2026-01-28 18:33:35 +05:30
|
|
|
})
|
2026-01-29 10:09:45 +05:30
|
|
|
.on_refresh(|this: &mut Self, _, cx| {
|
|
|
|
|
this.refresh_device_status(cx);
|
|
|
|
|
})
|
|
|
|
|
.render(cx),
|
|
|
|
|
)
|
|
|
|
|
.child(
|
|
|
|
|
v_flex()
|
|
|
|
|
.size_full()
|
|
|
|
|
.child(
|
|
|
|
|
TitleBar::new().bg(rgba(colors::zinc::ZINC950)).child(
|
|
|
|
|
h_flex()
|
|
|
|
|
.w_full()
|
|
|
|
|
.justify_between()
|
|
|
|
|
.bg(rgba(colors::zinc::ZINC950))
|
2026-01-28 23:36:15 +05:30
|
|
|
.items_center()
|
2026-01-29 10:09:45 +05:30
|
|
|
.cursor(gpui::CursorStyle::OpenHand)
|
2026-01-28 23:36:15 +05:30
|
|
|
.child(
|
2026-01-29 10:09:45 +05:30
|
|
|
Button::new("sidebar_toggle")
|
2026-01-28 23:36:15 +05:30
|
|
|
.ghost()
|
2026-01-29 10:09:45 +05:30
|
|
|
.icon(IconName::PanelLeft)
|
|
|
|
|
.on_click(cx.listener(|this, _, _, _| {
|
|
|
|
|
this.collapsed = !this.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(cx.theme()).into_any_element()
|
|
|
|
|
}
|
|
|
|
|
ActiveView::Passkeys => {
|
2026-01-29 16:00:10 +05:30
|
|
|
PasskeysView::build(cx.theme()).into_any_element()
|
2026-01-29 10:09:45 +05:30
|
|
|
}
|
|
|
|
|
ActiveView::Configuration => {
|
2026-01-29 15:41:37 +05:30
|
|
|
cx.new(|cx| ConfigView::new(window, cx)).into_any_element()
|
2026-01-29 10:09:45 +05:30
|
|
|
}
|
|
|
|
|
ActiveView::Security => {
|
2026-01-29 13:53:03 +05:30
|
|
|
SecurityView::build(cx).into_any_element()
|
2026-01-29 10:09:45 +05:30
|
|
|
}
|
|
|
|
|
ActiveView::Logs => LogsView::build().into_any_element(),
|
2026-01-29 11:45:24 +05:30
|
|
|
ActiveView::About => {
|
|
|
|
|
AboutView::build(cx.theme()).into_any_element()
|
|
|
|
|
}
|
2026-01-29 10:09:45 +05:30
|
|
|
}),
|
2026-01-28 17:29:07 +05:30
|
|
|
),
|
2026-01-29 10:09:45 +05:30
|
|
|
),
|
|
|
|
|
)
|
2026-01-28 17:29:07 +05:30
|
|
|
}
|
2026-01-27 22:51:03 +05:30
|
|
|
}
|