mirror of
https://github.com/librekeys/picoforge.git
synced 2026-07-28 08:01:19 -07:00
chore(ui): abstract away cards into a card component
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
use gpui::*;
|
||||
use gpui_component::{ActiveTheme, Icon, Theme, h_flex, v_flex};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Card {
|
||||
title: Option<SharedString>,
|
||||
description: Option<SharedString>,
|
||||
icon: Option<Icon>,
|
||||
header_right: Option<AnyElement>,
|
||||
children: Vec<AnyElement>,
|
||||
}
|
||||
|
||||
impl Card {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
title: None,
|
||||
description: None,
|
||||
icon: None,
|
||||
header_right: None,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title(mut self, title: impl Into<SharedString>) -> Self {
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn description(mut self, description: impl Into<SharedString>) -> Self {
|
||||
self.description = Some(description.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: Icon) -> Self {
|
||||
self.icon = Some(icon);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn header_right(mut self, element: impl IntoElement) -> Self {
|
||||
self.header_right = Some(element.into_any_element());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Card {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.children.extend(elements);
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Card {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme: &Theme = cx.theme();
|
||||
|
||||
let has_header = self.title.is_some()
|
||||
|| self.icon.is_some()
|
||||
|| self.description.is_some()
|
||||
|| self.header_right.is_some();
|
||||
|
||||
let header = if has_header {
|
||||
let mut left_side = v_flex().gap_1();
|
||||
|
||||
let mut icon_title_row = h_flex().items_center().gap_2();
|
||||
if let Some(icon) = self.icon {
|
||||
icon_title_row =
|
||||
icon_title_row.child(Icon::new(icon).size_5().text_color(theme.foreground));
|
||||
}
|
||||
if let Some(title) = self.title {
|
||||
icon_title_row = icon_title_row.child(
|
||||
div()
|
||||
.font_weight(FontWeight::BOLD)
|
||||
.text_color(theme.foreground)
|
||||
.child(title),
|
||||
);
|
||||
}
|
||||
|
||||
left_side = left_side.child(icon_title_row);
|
||||
|
||||
if let Some(desc) = self.description {
|
||||
left_side = left_side.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child(desc),
|
||||
);
|
||||
}
|
||||
|
||||
let mut header_row = h_flex().items_center().justify_between().child(left_side);
|
||||
|
||||
if let Some(right) = self.header_right {
|
||||
header_row = header_row.child(right);
|
||||
}
|
||||
|
||||
Some(header_row.into_any_element())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
div()
|
||||
.w_full()
|
||||
.bg(rgb(0x18181b))
|
||||
.border_1()
|
||||
.border_color(theme.border)
|
||||
.rounded_xl()
|
||||
.p_6()
|
||||
.child(v_flex().gap_6().children(header).children(self.children))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod button;
|
||||
pub mod card;
|
||||
pub mod page_view;
|
||||
pub mod sidebar;
|
||||
|
||||
+90
-93
@@ -1,5 +1,5 @@
|
||||
// src/views/about.rs
|
||||
use crate::ui::components::page_view::PageView;
|
||||
use crate::ui::components::{card::Card, page_view::PageView};
|
||||
use gpui::*;
|
||||
use gpui_component::{Icon, StyledExt, Theme, badge::Badge, button::Button, h_flex, v_flex};
|
||||
|
||||
@@ -16,100 +16,97 @@ impl AboutView {
|
||||
.justify_center()
|
||||
.child(
|
||||
div()
|
||||
.max_w(px(1200.0))
|
||||
.w_full()
|
||||
.bg(rgb(0x18181b))
|
||||
.border_1()
|
||||
.border_color(theme.border)
|
||||
.rounded_xl()
|
||||
.p_6()
|
||||
.max_w(px(1200.0))
|
||||
.child(
|
||||
v_flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.gap_4()
|
||||
.py_8()
|
||||
.text_center()
|
||||
.child(
|
||||
img("appIcons/in.suyogtandel.picoforge.svg")
|
||||
.w(px(256.0))
|
||||
.h(px(256.0)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_2xl()
|
||||
.font_bold()
|
||||
.text_color(theme.foreground)
|
||||
.child("PicoForge"),
|
||||
)
|
||||
.child(Badge::new().child("v0.4.0").color(theme.secondary))
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.max_w(px(450.0))
|
||||
.child(
|
||||
"An open source commissioning tool for Pico FIDO security keys. Developed with Rust and GPUI.",
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.muted_foreground)
|
||||
.gap_1()
|
||||
.pt_4()
|
||||
.border_t_1()
|
||||
.border_color(theme.border)
|
||||
.border_t_1()
|
||||
.border_color(theme.border)
|
||||
.w(px(320.0))
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.child("Code By:")
|
||||
.child(
|
||||
div()
|
||||
.font_medium()
|
||||
.text_color(theme.foreground)
|
||||
.child("Suyog Tandel, Fabrice Bellamy"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.pt_2()
|
||||
.mt_2()
|
||||
.child(h_flex().items_center().gap_1().child("Copyright:"))
|
||||
.child(
|
||||
div()
|
||||
.font_medium()
|
||||
.text_color(theme.foreground)
|
||||
.child("© 2026 Suyog Tandel"),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_4()
|
||||
.pt_6()
|
||||
.child(
|
||||
Button::new("github_btn")
|
||||
.outline()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
Icon::default()
|
||||
.path("icons/github.svg")
|
||||
.size_4(),
|
||||
)
|
||||
.child("GitHub"),
|
||||
)
|
||||
.on_click(|_, _, cx| {
|
||||
cx.open_url("https://github.com/librekeys/picoforge")
|
||||
}),
|
||||
),
|
||||
),
|
||||
Card::new().child(
|
||||
v_flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.gap_4()
|
||||
.py_8()
|
||||
.text_center()
|
||||
.child(
|
||||
img("appIcons/in.suyogtandel.picoforge.svg")
|
||||
.w(px(256.0))
|
||||
.h(px(256.0)),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_2xl()
|
||||
.font_bold()
|
||||
.text_color(theme.foreground)
|
||||
.child("PicoForge"),
|
||||
)
|
||||
.child(Badge::new().child("v0.4.0").color(theme.secondary))
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.max_w(px(450.0))
|
||||
.child(
|
||||
"An open source commissioning tool for Pico FIDO security keys. Developed with Rust and GPUI.",
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.muted_foreground)
|
||||
.gap_1()
|
||||
.pt_4()
|
||||
.border_t_1()
|
||||
.border_color(theme.border)
|
||||
.border_t_1()
|
||||
.border_color(theme.border)
|
||||
.w(px(320.0))
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.child("Code By:")
|
||||
.child(
|
||||
div()
|
||||
.font_medium()
|
||||
.text_color(theme.foreground)
|
||||
.child("Suyog Tandel, Fabrice Bellamy"),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.pt_2()
|
||||
.mt_2()
|
||||
.child(h_flex().items_center().gap_1().child("Copyright:"))
|
||||
.child(
|
||||
div()
|
||||
.font_medium()
|
||||
.text_color(theme.foreground)
|
||||
.child("© 2026 Suyog Tandel"),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_4()
|
||||
.pt_6()
|
||||
.child(
|
||||
Button::new("github_btn")
|
||||
.outline()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
Icon::default()
|
||||
.path("icons/github.svg")
|
||||
.size_4(),
|
||||
)
|
||||
.child("GitHub"),
|
||||
)
|
||||
.on_click(|_, _, cx| {
|
||||
cx.open_url("https://github.com/librekeys/picoforge")
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
theme,
|
||||
|
||||
+22
-73
@@ -1,10 +1,10 @@
|
||||
use crate::device::io;
|
||||
use crate::device::types::{AppConfigInput, FullDeviceStatus};
|
||||
use crate::ui::components::page_view::PageView;
|
||||
use crate::ui::components::{card::Card, page_view::PageView};
|
||||
use crate::ui::ui_types::VENDORS;
|
||||
use gpui::*;
|
||||
use gpui_component::{
|
||||
ActiveTheme, Disableable, Icon, StyledExt, Theme,
|
||||
ActiveTheme, Disableable, Icon, Theme,
|
||||
button::Button,
|
||||
input::{Input, InputState},
|
||||
select::{Select, SelectItem, SelectState},
|
||||
@@ -432,13 +432,11 @@ impl ConfigView {
|
||||
.child(Input::new(&self.product_name_input)),
|
||||
);
|
||||
|
||||
Self::config_card(
|
||||
"Identity",
|
||||
"USB Identification settings",
|
||||
Icon::default().path("icons/tag.svg"),
|
||||
content,
|
||||
theme,
|
||||
)
|
||||
Card::new()
|
||||
.title("Identity")
|
||||
.description("USB Identification settings")
|
||||
.icon(Icon::default().path("icons/tag.svg"))
|
||||
.child(content)
|
||||
}
|
||||
|
||||
fn render_led_card(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
@@ -522,13 +520,11 @@ impl ConfigView {
|
||||
),
|
||||
);
|
||||
|
||||
Self::config_card(
|
||||
"LED Settings",
|
||||
"Adjust visual feedback behavior",
|
||||
Icon::default().path("icons/microchip.svg"),
|
||||
content,
|
||||
theme,
|
||||
)
|
||||
Card::new()
|
||||
.title("LED Settings")
|
||||
.description("Adjust visual feedback behavior")
|
||||
.icon(Icon::default().path("icons/microchip.svg"))
|
||||
.child(content)
|
||||
}
|
||||
|
||||
fn render_touch_card(&self, theme: &Theme) -> impl IntoElement {
|
||||
@@ -539,13 +535,11 @@ impl ConfigView {
|
||||
.child(Input::new(&self.touch_timeout_input)),
|
||||
);
|
||||
|
||||
Self::config_card(
|
||||
"Touch & Timing",
|
||||
"Configure interaction timeouts",
|
||||
Icon::default().path("icons/settings.svg"),
|
||||
content,
|
||||
theme,
|
||||
)
|
||||
Card::new()
|
||||
.title("Touch & Timing")
|
||||
.description("Configure interaction timeouts")
|
||||
.icon(Icon::default().path("icons/settings.svg"))
|
||||
.child(content)
|
||||
}
|
||||
|
||||
fn render_options_card(&mut self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
@@ -600,56 +594,11 @@ impl ConfigView {
|
||||
),
|
||||
);
|
||||
|
||||
Self::config_card(
|
||||
"Device Options",
|
||||
"Toggle advanced features",
|
||||
Icon::default().path("icons/settings.svg"),
|
||||
content,
|
||||
&theme,
|
||||
)
|
||||
}
|
||||
|
||||
fn config_card(
|
||||
title: &str,
|
||||
description: &str,
|
||||
icon: Icon,
|
||||
content: impl IntoElement,
|
||||
theme: &Theme,
|
||||
) -> impl IntoElement {
|
||||
div()
|
||||
.w_full()
|
||||
.bg(rgb(0x18181b)) // Using the same bg as home card
|
||||
.border_1()
|
||||
.border_color(theme.border)
|
||||
.rounded_xl()
|
||||
.p_6()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
gpui_component::h_flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(Icon::new(icon).size_5().text_color(theme.foreground))
|
||||
.child(
|
||||
div()
|
||||
.font_bold()
|
||||
.text_color(theme.foreground)
|
||||
.child(title.to_string()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child(description.to_string()),
|
||||
),
|
||||
)
|
||||
.child(content),
|
||||
)
|
||||
Card::new()
|
||||
.title("Device Options")
|
||||
.description("Toggle advanced features")
|
||||
.icon(Icon::default().path("icons/settings.svg"))
|
||||
.child(content)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+150
-187
@@ -1,5 +1,5 @@
|
||||
use crate::device::types::DeviceMethod;
|
||||
use crate::ui::components::page_view::PageView;
|
||||
use crate::ui::components::{card::Card, page_view::PageView};
|
||||
use crate::ui::ui_types::GlobalDeviceState;
|
||||
use gpui::*;
|
||||
use gpui_component::StyledExt;
|
||||
@@ -46,39 +46,6 @@ impl HomeView {
|
||||
)
|
||||
}
|
||||
|
||||
fn home_card(
|
||||
title: &str,
|
||||
icon: Icon,
|
||||
content: impl IntoElement,
|
||||
theme: &Theme,
|
||||
) -> impl IntoElement {
|
||||
div()
|
||||
.w_full()
|
||||
// TODO: REPLACE with a constant or modify default theme
|
||||
.bg(rgb(0x18181b))
|
||||
.border_1()
|
||||
.border_color(theme.border)
|
||||
.rounded_xl()
|
||||
.p_6()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
h_flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(Icon::new(icon).size_5().text_color(theme.foreground))
|
||||
.child(
|
||||
div()
|
||||
.font_bold()
|
||||
.text_color(theme.foreground)
|
||||
.child(title.to_string()),
|
||||
),
|
||||
)
|
||||
.child(content),
|
||||
)
|
||||
}
|
||||
|
||||
// Helper for Key-Value pairs
|
||||
fn render_kv(
|
||||
label: &str,
|
||||
@@ -115,70 +82,70 @@ impl HomeView {
|
||||
|
||||
let flash_percent = (info.flash_used as f32 / info.flash_total as f32) * 100.0;
|
||||
|
||||
Self::home_card(
|
||||
"Device Information",
|
||||
Icon::default().path("icons/cpu.svg"),
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
div()
|
||||
.grid()
|
||||
.grid_cols(2)
|
||||
.gap_4()
|
||||
.child(Self::render_kv(
|
||||
"Serial Number",
|
||||
info.serial.clone(),
|
||||
theme,
|
||||
true,
|
||||
))
|
||||
.child(Self::render_kv(
|
||||
"Firmware Version",
|
||||
format!("v{}", info.firmware_version),
|
||||
theme,
|
||||
true,
|
||||
))
|
||||
.child(Self::render_kv(
|
||||
"VID:PID",
|
||||
format!("{}, {}", config.vid, config.pid),
|
||||
theme,
|
||||
true,
|
||||
))
|
||||
.child(Self::render_kv(
|
||||
"Product Name",
|
||||
config.product_name.clone(),
|
||||
theme,
|
||||
false,
|
||||
)),
|
||||
)
|
||||
.child(div().h_px().bg(theme.border))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.text_sm()
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("Flash Memory"),
|
||||
)
|
||||
.child(div().text_color(theme.foreground).child(format!(
|
||||
"{:.0} / {:.0} KB",
|
||||
info.flash_used, info.flash_total
|
||||
))),
|
||||
)
|
||||
.child(Progress::new().value(flash_percent)),
|
||||
),
|
||||
theme,
|
||||
)
|
||||
Card::new()
|
||||
.title("Device Information")
|
||||
.icon(Icon::default().path("icons/cpu.svg"))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
div()
|
||||
.grid()
|
||||
.grid_cols(2)
|
||||
.gap_4()
|
||||
.child(Self::render_kv(
|
||||
"Serial Number",
|
||||
info.serial.clone(),
|
||||
theme,
|
||||
true,
|
||||
))
|
||||
.child(Self::render_kv(
|
||||
"Firmware Version",
|
||||
format!("v{}", info.firmware_version),
|
||||
theme,
|
||||
true,
|
||||
))
|
||||
.child(Self::render_kv(
|
||||
"VID:PID",
|
||||
format!("{}, {}", config.vid, config.pid),
|
||||
theme,
|
||||
true,
|
||||
))
|
||||
.child(Self::render_kv(
|
||||
"Product Name",
|
||||
config.product_name.clone(),
|
||||
theme,
|
||||
false,
|
||||
)),
|
||||
)
|
||||
.child(div().h_px().bg(theme.border))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.text_sm()
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("Flash Memory"),
|
||||
)
|
||||
.child(div().text_color(theme.foreground).child(format!(
|
||||
"{:.0} / {:.0} KB",
|
||||
info.flash_used, info.flash_total
|
||||
))),
|
||||
)
|
||||
.child(Progress::new().value(flash_percent)),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_fido_info(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
Self::home_card(
|
||||
"FIDO2 Information",
|
||||
Icon::default().path("icons/shield.svg"),
|
||||
if let Some(fido) = &state.fido_info {
|
||||
Card::new()
|
||||
.title("FIDO2 Information")
|
||||
.icon(Icon::default().path("icons/shield.svg"))
|
||||
.child(if let Some(fido) = &state.fido_info {
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
@@ -228,18 +195,16 @@ impl HomeView {
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("FIDO information not available")
|
||||
.into_any_element()
|
||||
},
|
||||
theme,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn render_led_config(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.device_status.as_ref().unwrap();
|
||||
let config = &status.config;
|
||||
Self::home_card(
|
||||
"LED Configuration",
|
||||
Icon::default().path("icons/microchip.svg"),
|
||||
if status.method == DeviceMethod::Fido {
|
||||
Card::new()
|
||||
.title("LED Configuration")
|
||||
.icon(Icon::default().path("icons/microchip.svg"))
|
||||
.child(if status.method == DeviceMethod::Fido {
|
||||
v_flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
@@ -328,95 +293,93 @@ impl HomeView {
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
},
|
||||
theme,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn render_security_status(state: &GlobalDeviceState, theme: &Theme) -> impl IntoElement {
|
||||
let status = state.device_status.as_ref().unwrap();
|
||||
Self::home_card(
|
||||
"Security Status",
|
||||
Icon::default().path("icons/shield-check.svg"),
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.text_sm()
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(div().text_color(theme.muted_foreground).child("Boot Mode"))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.child(if status.secure_boot {
|
||||
Icon::default()
|
||||
.path("icons/lock.svg")
|
||||
.size_3p5()
|
||||
.text_color(gpui::green())
|
||||
Card::new()
|
||||
.title("Security Status")
|
||||
.icon(Icon::default().path("icons/shield-check.svg"))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.text_sm()
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(div().text_color(theme.muted_foreground).child("Boot Mode"))
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.items_center()
|
||||
.child(if status.secure_boot {
|
||||
Icon::default()
|
||||
.path("icons/lock.svg")
|
||||
.size_3p5()
|
||||
.text_color(gpui::green())
|
||||
} else {
|
||||
Icon::default()
|
||||
.path("icons/lock-open.svg")
|
||||
.size_3p5()
|
||||
.text_color(rgb(0xfe9a00))
|
||||
})
|
||||
.child(
|
||||
Badge::new()
|
||||
.child(if status.secure_boot {
|
||||
"Secure Boot"
|
||||
} else {
|
||||
"Development"
|
||||
})
|
||||
.color(if status.secure_boot {
|
||||
theme.primary
|
||||
} else {
|
||||
theme.secondary
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("Debug Interface"),
|
||||
)
|
||||
.child(div().font_medium().text_color(theme.foreground).child(
|
||||
if status.secure_lock {
|
||||
"Read-out Locked"
|
||||
} else {
|
||||
Icon::default()
|
||||
.path("icons/lock-open.svg")
|
||||
.size_3p5()
|
||||
.text_color(rgb(0xfe9a00))
|
||||
})
|
||||
.child(
|
||||
Badge::new()
|
||||
.child(if status.secure_boot {
|
||||
"Secure Boot"
|
||||
} else {
|
||||
"Development"
|
||||
})
|
||||
.color(if status.secure_boot {
|
||||
theme.primary
|
||||
} else {
|
||||
theme.secondary
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("Debug Interface"),
|
||||
)
|
||||
.child(div().font_medium().text_color(theme.foreground).child(
|
||||
if status.secure_lock {
|
||||
"Read-out Locked"
|
||||
} else {
|
||||
"Debug Enabled"
|
||||
},
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("Secure Lock"),
|
||||
)
|
||||
.child(
|
||||
Badge::new()
|
||||
.child(if status.secure_lock {
|
||||
"Acknowledged"
|
||||
} else {
|
||||
"Pending"
|
||||
})
|
||||
.color(if status.secure_lock {
|
||||
gpui::red()
|
||||
} else {
|
||||
theme.secondary
|
||||
}),
|
||||
),
|
||||
),
|
||||
theme,
|
||||
)
|
||||
"Debug Enabled"
|
||||
},
|
||||
)),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.justify_between()
|
||||
.items_center()
|
||||
.child(
|
||||
div()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child("Secure Lock"),
|
||||
)
|
||||
.child(
|
||||
Badge::new()
|
||||
.child(if status.secure_lock {
|
||||
"Acknowledged"
|
||||
} else {
|
||||
"Pending"
|
||||
})
|
||||
.color(if status.secure_lock {
|
||||
gpui::red()
|
||||
} else {
|
||||
theme.secondary
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::ui::components::page_view::PageView;
|
||||
use gpui::*;
|
||||
use gpui_component::{
|
||||
ActiveTheme, Icon, StyledExt, button::Button, h_flex, scroll::ScrollableElement, v_flex,
|
||||
ActiveTheme, Icon, button::Button, h_flex, scroll::ScrollableElement, v_flex,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
||||
+22
-80
@@ -1,4 +1,4 @@
|
||||
use crate::ui::components::page_view::PageView;
|
||||
use crate::ui::components::{card::Card, page_view::PageView};
|
||||
use gpui::*;
|
||||
use gpui_component::StyledExt;
|
||||
use gpui_component::button::ButtonVariants;
|
||||
@@ -125,75 +125,19 @@ impl PasskeysView {
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn main_card(
|
||||
title: &str,
|
||||
icon_path: impl Into<SharedString>,
|
||||
description: &str,
|
||||
content: impl IntoElement,
|
||||
header_right: Option<impl IntoElement>,
|
||||
theme: &Theme,
|
||||
) -> impl IntoElement {
|
||||
div()
|
||||
.w_full()
|
||||
.bg(rgb(0x18181b)) // Using the same dark bg as in HomeView
|
||||
.border_1()
|
||||
.border_color(theme.border)
|
||||
.rounded_xl()
|
||||
.p_6()
|
||||
fn render_pin_management(device: &DeviceState, theme: &Theme) -> impl IntoElement {
|
||||
Card::new()
|
||||
.title("PIN Management")
|
||||
.icon(Icon::default().path("icons/key.svg"))
|
||||
.description("Configure FIDO2 PIN security")
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.child(
|
||||
h_flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
h_flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(
|
||||
Icon::default()
|
||||
.path(icon_path)
|
||||
.size_5()
|
||||
.text_color(theme.foreground),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.font_bold()
|
||||
.text_color(theme.foreground)
|
||||
.child(title.to_string()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.muted_foreground)
|
||||
.child(description.to_string()),
|
||||
),
|
||||
)
|
||||
.children(header_right),
|
||||
)
|
||||
.child(content),
|
||||
.gap_4()
|
||||
.child(Self::render_pin_status_row(device, theme))
|
||||
.child(Self::render_min_pin_length_row(device, theme)),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_pin_management(device: &DeviceState, theme: &Theme) -> impl IntoElement {
|
||||
Self::main_card(
|
||||
"PIN Management",
|
||||
"icons/key.svg",
|
||||
"Configure FIDO2 PIN security",
|
||||
v_flex()
|
||||
.gap_4()
|
||||
.child(Self::render_pin_status_row(device, theme))
|
||||
.child(Self::render_min_pin_length_row(device, theme)),
|
||||
None::<AnyElement>,
|
||||
theme,
|
||||
)
|
||||
}
|
||||
|
||||
fn render_pin_status_row(device: &DeviceState, theme: &Theme) -> impl IntoElement {
|
||||
let pin_set = device
|
||||
.fido_info
|
||||
@@ -263,24 +207,22 @@ impl PasskeysView {
|
||||
}
|
||||
|
||||
fn render_stored_passkeys(device: &DeviceState, theme: &Theme) -> impl IntoElement {
|
||||
Self::main_card(
|
||||
"Stored Passkeys",
|
||||
"icons/key-round.svg",
|
||||
"View and manage your resident credentials",
|
||||
if !device.unlocked {
|
||||
Card::new()
|
||||
.title("Stored Passkeys")
|
||||
.icon(Icon::default().path("icons/key-round.svg"))
|
||||
.description("View and manage your resident credentials")
|
||||
.child(if !device.unlocked {
|
||||
Self::render_locked_state(theme).into_any_element()
|
||||
} else {
|
||||
Self::render_unlocked_state(device, theme).into_any_element()
|
||||
},
|
||||
Some(
|
||||
div()
|
||||
.child("View and manage your resident credentials")
|
||||
.text_sm()
|
||||
.text_color(theme.muted_foreground)
|
||||
.invisible(),
|
||||
),
|
||||
theme,
|
||||
)
|
||||
})
|
||||
// header_right was effectively invisible/placeholder in original code:
|
||||
// Some(div().child("View and manage...").invisible())
|
||||
// We can omit it or add it if needed, but it looked like a hack or mistake in original code?
|
||||
// The original header_right was:
|
||||
// Some(div().child("View and manage your resident credentials").text_sm().text_color(...).invisible())
|
||||
// This suggests it was maybe trying to take up space or something? But description handles the text.
|
||||
// I will simplify and omit it, which is cleaner.
|
||||
}
|
||||
|
||||
fn render_locked_state(theme: &Theme) -> impl IntoElement {
|
||||
|
||||
Reference in New Issue
Block a user