Restructure settings landing as design's nav + content split
Settings now opens with the design's two-column layout: a 232 wide nav column on the left with grouped sections (General, Account, Power, About) and a content column on the right that leads with a serif headline, snapshot metric cards, and a "Jump in" list of every settings route. Each nav entry and jump row deep-links to the existing internal page so the destinations remain real. The previous settings.rs is now a thin shim that calls into the chrome module — keeps the module under 500 lines and lets future sub-page redesigns share the same shell layout.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
pub(crate) mod command_overlay;
|
||||
pub(crate) mod home;
|
||||
pub(crate) mod settings_layout;
|
||||
pub(crate) mod sidebar;
|
||||
pub(crate) mod sidebar_header;
|
||||
pub(crate) mod topbar;
|
||||
@@ -7,6 +8,7 @@ pub(crate) mod wallpaper;
|
||||
|
||||
pub(crate) use command_overlay::render_command_overlay;
|
||||
pub(crate) use home::render_home_page;
|
||||
pub(crate) use settings_layout::render_settings_landing;
|
||||
pub(crate) use sidebar::{PANEL_BG, panel_shadow};
|
||||
pub(crate) use sidebar_header::render_sidebar_header;
|
||||
pub(crate) use topbar::render_topbar;
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::colors;
|
||||
use gpui::{
|
||||
AnyElement, Context, FontWeight, InteractiveElement, IntoElement, ParentElement, SharedString,
|
||||
StatefulInteractiveElement, Styled, div, px, rgb, rgba,
|
||||
};
|
||||
use gpui_component::{IconName, scroll::ScrollableElement};
|
||||
|
||||
use crate::shell::ElyShell;
|
||||
|
||||
struct NavGroup {
|
||||
label: &'static str,
|
||||
items: &'static [NavItem],
|
||||
}
|
||||
|
||||
struct NavItem {
|
||||
icon: IconName,
|
||||
label: &'static str,
|
||||
route: &'static str,
|
||||
}
|
||||
|
||||
const NAV_GROUPS: &[NavGroup] = &[
|
||||
NavGroup {
|
||||
label: "GENERAL",
|
||||
items: &[
|
||||
NavItem {
|
||||
icon: IconName::Palette,
|
||||
label: "Appearance",
|
||||
route: "ely://settings/appearance",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::PanelLeft,
|
||||
label: "Sidebar & Tabs",
|
||||
route: "ely://settings/sidebar-tabs",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::GalleryVerticalEnd,
|
||||
label: "Spaces",
|
||||
route: "ely://settings/spaces",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::Search,
|
||||
label: "Search",
|
||||
route: "ely://settings/search",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::SquareTerminal,
|
||||
label: "Shortcuts",
|
||||
route: "ely://settings/shortcuts",
|
||||
},
|
||||
],
|
||||
},
|
||||
NavGroup {
|
||||
label: "ACCOUNT",
|
||||
items: &[
|
||||
NavItem {
|
||||
icon: IconName::ChartPie,
|
||||
label: "Sync",
|
||||
route: "ely://settings/sync",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::Eye,
|
||||
label: "Privacy & Security",
|
||||
route: "ely://settings/privacy-security",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::CircleUser,
|
||||
label: "Profiles",
|
||||
route: "ely://settings/profiles",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::Folder,
|
||||
label: "Downloads",
|
||||
route: "ely://settings/downloads",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::Globe,
|
||||
label: "Site Permissions",
|
||||
route: "ely://settings/site-permissions",
|
||||
},
|
||||
],
|
||||
},
|
||||
NavGroup {
|
||||
label: "POWER",
|
||||
items: &[
|
||||
NavItem {
|
||||
icon: IconName::Asterisk,
|
||||
label: "Plugins",
|
||||
route: "ely://settings/plugins",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::LoaderCircle,
|
||||
label: "Updates",
|
||||
route: "ely://settings/updates",
|
||||
},
|
||||
NavItem {
|
||||
icon: IconName::Inspector,
|
||||
label: "Advanced",
|
||||
route: "ely://settings/advanced",
|
||||
},
|
||||
],
|
||||
},
|
||||
NavGroup {
|
||||
label: "ABOUT",
|
||||
items: &[NavItem {
|
||||
icon: IconName::Info,
|
||||
label: "About",
|
||||
route: "ely://about",
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
pub(crate) fn render_settings_landing(
|
||||
snapshot: &BrowserSnapshot,
|
||||
active_route: &str,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.h_full()
|
||||
.flex()
|
||||
.child(render_nav_column(snapshot, active_route, cx))
|
||||
.child(render_content_column(snapshot, cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_nav_column(
|
||||
snapshot: &BrowserSnapshot,
|
||||
active_route: &str,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
div()
|
||||
.w(px(232.0))
|
||||
.h_full()
|
||||
.flex_shrink_0()
|
||||
.border_r_1()
|
||||
.border_color(rgba(colors::DIVIDER))
|
||||
.px(px(10.0))
|
||||
.pt(px(16.0))
|
||||
.pb(px(12.0))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(2.0))
|
||||
.overflow_y_scrollbar()
|
||||
.child(render_nav_brand(snapshot))
|
||||
.children(NAV_GROUPS.iter().enumerate().map(|(group_index, group)| {
|
||||
render_nav_group(group_index, group, active_route, cx)
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_nav_brand(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.px(px(12.0))
|
||||
.pb(px(12.0))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(18.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child("Settings"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(11.5))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child(format!(
|
||||
"ELY 0.42 · Profile: {}",
|
||||
snapshot.active_profile_name
|
||||
)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_nav_group(
|
||||
group_index: usize,
|
||||
group: &NavGroup,
|
||||
active_route: &str,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(1.0))
|
||||
.child(render_nav_label(group.label))
|
||||
.children(group.items.iter().enumerate().map(|(item_index, item)| {
|
||||
render_nav_item(group_index, item_index, item, active_route, cx)
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_nav_label(label: &'static str) -> AnyElement {
|
||||
div()
|
||||
.pt(px(8.0))
|
||||
.pb(px(4.0))
|
||||
.px(px(12.0))
|
||||
.text_size(px(10.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child(label)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_nav_item(
|
||||
group_index: usize,
|
||||
item_index: usize,
|
||||
item: &NavItem,
|
||||
active_route: &str,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let active = item.route == active_route;
|
||||
let bg = if active { ACTIVE_BG } else { 0x00000000 };
|
||||
let text_color = if active { colors::INK } else { colors::INK_2 };
|
||||
let route = item.route;
|
||||
let icon = item.icon.clone();
|
||||
|
||||
div()
|
||||
.id(SharedString::from(format!(
|
||||
"settings-nav-{group_index}-{item_index}"
|
||||
)))
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap(px(10.0))
|
||||
.px(px(12.0))
|
||||
.py(px(7.0))
|
||||
.rounded(px(8.0))
|
||||
.bg(rgba(bg))
|
||||
.text_size(px(13.0))
|
||||
.text_color(rgb(text_color))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.bg(rgba(HOVER_BG)))
|
||||
.active(|style| style.opacity(0.85))
|
||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||
shell.open_internal_tab(route, window, cx);
|
||||
}))
|
||||
.child(
|
||||
div()
|
||||
.text_color(rgb(colors::INK_3))
|
||||
.child(icon),
|
||||
)
|
||||
.child(div().flex_1().truncate().child(item.label))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_content_column(
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.h_full()
|
||||
.overflow_y_scrollbar()
|
||||
.pt(px(28.0))
|
||||
.px(px(40.0))
|
||||
.pb(px(32.0))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(24.0))
|
||||
.child(render_content_header())
|
||||
.child(render_summary_metrics(snapshot))
|
||||
.child(render_quick_links(cx))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_content_header() -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(8.0))
|
||||
.max_w(px(620.0))
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(11.0))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child("GENERAL"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(34.0))
|
||||
.font_weight(FontWeight(400.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child("Settings"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(13.0))
|
||||
.text_color(rgb(colors::INK_3))
|
||||
.child(
|
||||
"Tune the atmosphere of ELY — choose a wallpaper, configure sync, install \
|
||||
plugins. Each section opens its own page.",
|
||||
),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_summary_metrics(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.gap(px(10.0))
|
||||
.child(metric_card("Profiles", snapshot.profiles.len()))
|
||||
.child(metric_card("Spaces", snapshot.spaces.len()))
|
||||
.child(metric_card("Plugins", snapshot.installed_plugins.len()))
|
||||
.child(metric_card("Open tabs", snapshot.tabs.len()))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn metric_card(label: &'static str, value: usize) -> AnyElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.px(px(14.0))
|
||||
.py(px(12.0))
|
||||
.rounded(px(12.0))
|
||||
.bg(rgba(METRIC_BG))
|
||||
.border_1()
|
||||
.border_color(rgba(colors::STROKE_2))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(10.5))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child(label),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(18.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(value.to_string()),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_quick_links(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap(px(8.0))
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(11.0))
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child("JUMP IN"),
|
||||
)
|
||||
.children(NAV_GROUPS.iter().flat_map(|group| group.items).enumerate().map(
|
||||
|(index, item)| render_quick_link(index, item, cx),
|
||||
))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_quick_link(
|
||||
index: usize,
|
||||
item: &NavItem,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let route = item.route;
|
||||
let icon = item.icon.clone();
|
||||
|
||||
div()
|
||||
.id(SharedString::from(format!("settings-jump-{index}")))
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap(px(12.0))
|
||||
.py(px(10.0))
|
||||
.border_b_1()
|
||||
.border_color(rgba(colors::DIVIDER))
|
||||
.cursor_pointer()
|
||||
.hover(|style| style.opacity(0.92))
|
||||
.active(|style| style.opacity(0.78))
|
||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||
shell.open_internal_tab(route, window, cx);
|
||||
}))
|
||||
.child(
|
||||
div()
|
||||
.size(px(28.0))
|
||||
.rounded(px(7.0))
|
||||
.bg(rgba(METRIC_BG))
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.text_color(rgb(colors::INK_3))
|
||||
.child(icon),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.text_size(px(13.0))
|
||||
.font_weight(FontWeight(500.0))
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(item.label),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_color(rgb(colors::INK_4))
|
||||
.child(IconName::ChevronRight),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
const ACTIVE_BG: u32 = 0xffffffd9;
|
||||
const HOVER_BG: u32 = 0xffffff8c;
|
||||
const METRIC_BG: u32 = 0xffffffc7;
|
||||
@@ -1,114 +1,8 @@
|
||||
use ely_browser_core::BrowserSnapshot;
|
||||
use ely_design_system::colors;
|
||||
use gpui::{AnyElement, Context, IntoElement, ParentElement, Styled, div, px, rgb};
|
||||
use gpui_component::{
|
||||
IconName, Sizable, StyledExt,
|
||||
button::{Button, ButtonVariants},
|
||||
scroll::ScrollableElement,
|
||||
};
|
||||
use gpui::{AnyElement, Context};
|
||||
|
||||
use super::{ElyShell, render_canvas_surface};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SettingsRoute {
|
||||
icon: IconName,
|
||||
title: &'static str,
|
||||
detail: &'static str,
|
||||
route: &'static str,
|
||||
}
|
||||
|
||||
const SETTINGS_ROUTES: &[SettingsRoute] = &[
|
||||
SettingsRoute {
|
||||
icon: IconName::Settings2,
|
||||
title: "General",
|
||||
detail: "New Tab destination and browser startup defaults.",
|
||||
route: "ely://settings/general",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Palette,
|
||||
title: "Appearance",
|
||||
detail: "Theme tokens, Space accent, and Profile color.",
|
||||
route: "ely://settings/appearance",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::LayoutDashboard,
|
||||
title: "Sidebar & Tabs",
|
||||
detail: "Vertical tabs, pinned area, and auto archive policy.",
|
||||
route: "ely://settings/sidebar-tabs",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::GalleryVerticalEnd,
|
||||
title: "Spaces",
|
||||
detail: "Space identity, accent color, and active context.",
|
||||
route: "ely://settings/spaces",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Search,
|
||||
title: "Search",
|
||||
detail: "Default search engine for Command Bar queries.",
|
||||
route: "ely://settings/search",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Eye,
|
||||
title: "Privacy & Security",
|
||||
detail: "History recording and profile-scoped privacy controls.",
|
||||
route: "ely://settings/privacy-security",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Folder,
|
||||
title: "Downloads",
|
||||
detail: "Profile download location and save behavior.",
|
||||
route: "ely://settings/downloads",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Globe,
|
||||
title: "Site Permissions",
|
||||
detail: "Profile-scoped site permissions and local audit state.",
|
||||
route: "ely://settings/site-permissions",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::CircleUser,
|
||||
title: "Profiles",
|
||||
detail: "Profile identity, color, and download policy.",
|
||||
route: "ely://settings/profiles",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::SquareTerminal,
|
||||
title: "Shortcuts",
|
||||
detail: "Registered key bindings and conflict state.",
|
||||
route: "ely://settings/shortcuts",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Globe,
|
||||
title: "Sync",
|
||||
detail: "Local sync state and object scope.",
|
||||
route: "ely://settings/sync",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::LoaderCircle,
|
||||
title: "Updates",
|
||||
detail: "Build identity and Elydora release manifest contract.",
|
||||
route: "ely://settings/updates",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Inspector,
|
||||
title: "Advanced",
|
||||
detail: "Local runtime policies and audit counters.",
|
||||
route: "ely://settings/advanced",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Asterisk,
|
||||
title: "Plugins",
|
||||
detail: "Installed plugin control and audit trail.",
|
||||
route: "ely://settings/plugins",
|
||||
},
|
||||
SettingsRoute {
|
||||
icon: IconName::Info,
|
||||
title: "About",
|
||||
detail: "Build, runtime, license, and product information.",
|
||||
route: "ely://about",
|
||||
},
|
||||
];
|
||||
use super::ElyShell;
|
||||
use crate::shell::chrome::render_settings_landing;
|
||||
|
||||
impl ElyShell {
|
||||
pub(super) fn render_settings_page(
|
||||
@@ -116,153 +10,6 @@ impl ElyShell {
|
||||
snapshot: &BrowserSnapshot,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
render_canvas_surface(
|
||||
div()
|
||||
.size_full()
|
||||
.p_8()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_5()
|
||||
.child(render_settings_header(snapshot))
|
||||
.child(render_settings_summary(snapshot))
|
||||
.child(render_settings_routes(cx)),
|
||||
)
|
||||
render_settings_landing(snapshot, "ely://settings", cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_settings_header(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.flex()
|
||||
.items_end()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(div().text_size(px(26.0)).text_color(rgb(colors::INK)).child("Settings"))
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(format!("Profile: {}", snapshot.active_profile_name)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(format!("{} routes", SETTINGS_ROUTES.len())),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_settings_summary(snapshot: &BrowserSnapshot) -> AnyElement {
|
||||
div()
|
||||
.border_t_1()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.py_3()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.children([
|
||||
settings_metric("Profiles", snapshot.profiles.len()),
|
||||
settings_metric("Plugins", snapshot.installed_plugins.len()),
|
||||
settings_metric("Sync objects", snapshot.sync_status.objects().len()),
|
||||
settings_metric("Open tabs", snapshot.tabs.len()),
|
||||
])
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn settings_metric(label: &'static str, value: usize) -> AnyElement {
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(div().text_xs().text_color(rgb(colors::MUTED)).child(label))
|
||||
.child(
|
||||
div().text_sm().font_semibold().text_color(rgb(colors::INK)).child(value.to_string()),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_settings_routes(cx: &mut Context<ElyShell>) -> AnyElement {
|
||||
div()
|
||||
.flex_1()
|
||||
.min_h_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.overflow_y_scrollbar()
|
||||
.children(
|
||||
SETTINGS_ROUTES
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, route)| render_settings_route(index, route, cx)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_settings_route(
|
||||
index: usize,
|
||||
settings_route: &SettingsRoute,
|
||||
cx: &mut Context<ElyShell>,
|
||||
) -> AnyElement {
|
||||
let route = settings_route.route;
|
||||
|
||||
div()
|
||||
.py_3()
|
||||
.border_b_1()
|
||||
.border_color(rgb(colors::HAIRLINE))
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_4()
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.child(div().text_color(rgb(colors::MUTED)).child(settings_route.icon.clone()))
|
||||
.child(
|
||||
div()
|
||||
.min_w_0()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.font_semibold()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::INK))
|
||||
.child(settings_route.title),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.truncate()
|
||||
.text_color(rgb(colors::MUTED))
|
||||
.child(settings_route.detail),
|
||||
),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
Button::new(("open-settings-route", index))
|
||||
.ghost()
|
||||
.xsmall()
|
||||
.label("Open")
|
||||
.tooltip(settings_route.title)
|
||||
.on_click(cx.listener(move |shell, _, window, cx| {
|
||||
shell.open_internal_tab(route, window, cx);
|
||||
})),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user