Align settings and workspaces native surfaces
This commit is contained in:
1 parent
e1b7f52444
commit
0a5be5ea97
3 files changed
+245
-94
No files matched your search
@@ -220,37 +220,6 @@ impl AppRoot {
|
|||||||
Err(error) => self.set_status(error.to_string(), cx),
|
Err(error) => self.set_status(error.to_string(), cx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metric_card(
|
|
||||||
&self,
|
|
||||||
label: impl Into<String>,
|
|
||||||
value: String,
|
|
||||||
detail: impl Into<String>,
|
|
||||||
) -> impl IntoElement + '_ {
|
|
||||||
let label = label.into();
|
|
||||||
let detail = detail.into();
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.p_4()
|
|
||||||
.gap_2()
|
|
||||||
.border_1()
|
|
||||||
.rounded_md()
|
|
||||||
.border_color(theme::panel_border())
|
|
||||||
.bg(theme::panel())
|
|
||||||
.child(div().text_sm().text_color(theme::text_muted()).child(label))
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_3xl()
|
|
||||||
.text_color(theme::text_primary())
|
|
||||||
.child(value),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_sm()
|
|
||||||
.text_color(theme::text_muted())
|
|
||||||
.child(detail),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for AppRoot {
|
impl Render for AppRoot {
|
||||||
|
|||||||
@@ -6,29 +6,113 @@ impl AppRoot {
|
|||||||
let execution_mode = current
|
let execution_mode = current
|
||||||
.map(|item| item.execution_mode.clone())
|
.map(|item| item.execution_mode.clone())
|
||||||
.unwrap_or_else(|| "ai_ceo".to_string());
|
.unwrap_or_else(|| "ai_ceo".to_string());
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.gap_6()
|
||||||
.gap_4()
|
.child(
|
||||||
.child(self.metric_card(
|
div()
|
||||||
"Profile path",
|
.w(px(220.0))
|
||||||
self.snapshot.profile_path.clone(),
|
.p_4()
|
||||||
"Electron-compatible local profile",
|
.gap_2()
|
||||||
))
|
.rounded_lg()
|
||||||
.child(self.metric_card(
|
.border_1()
|
||||||
"Theme",
|
.border_color(theme::panel_border())
|
||||||
self.snapshot.theme.clone(),
|
.bg(theme::panel())
|
||||||
"Stored in the shared settings table",
|
.child(settings_nav_row("General", true))
|
||||||
))
|
.child(settings_nav_row("Execution mode", false))
|
||||||
.child(self.metric_card(
|
.child(settings_nav_row("Providers", false))
|
||||||
"Locale",
|
.child(settings_nav_row("Shortcuts", false))
|
||||||
self.snapshot.locale.clone(),
|
.child(settings_nav_row("Data", false))
|
||||||
"Read from settings or the OS locale",
|
.child(settings_nav_row("Secrets", false)),
|
||||||
))
|
)
|
||||||
.child(self.metric_card(
|
.child(
|
||||||
"Connectors",
|
div()
|
||||||
self.snapshot.connectors.len().to_string(),
|
.flex_1()
|
||||||
execution_mode,
|
.gap_6()
|
||||||
))
|
.child(settings_section(
|
||||||
|
"General",
|
||||||
|
vec![
|
||||||
|
settings_row("Profile path", self.snapshot.profile_path.clone()),
|
||||||
|
settings_row("Theme", self.snapshot.theme.clone()),
|
||||||
|
settings_row("Locale", self.snapshot.locale.clone()),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
.child(settings_section(
|
||||||
|
"Execution mode",
|
||||||
|
vec![settings_row("Company mode", execution_mode)],
|
||||||
|
))
|
||||||
|
.child(settings_section(
|
||||||
|
"Providers",
|
||||||
|
vec![settings_row(
|
||||||
|
"Configured connectors",
|
||||||
|
self.snapshot.connectors.len().to_string(),
|
||||||
|
)],
|
||||||
|
))
|
||||||
|
.child(settings_section(
|
||||||
|
"Data",
|
||||||
|
vec![
|
||||||
|
settings_row("Stored profile", "Local SQLite".to_string()),
|
||||||
|
settings_row("Current company", current_company_name(current)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn settings_nav_row(label: &'static str, active: bool) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.px_4()
|
||||||
|
.py_3()
|
||||||
|
.rounded_lg()
|
||||||
|
.bg(if active {
|
||||||
|
theme::selection_fill()
|
||||||
|
} else {
|
||||||
|
theme::panel()
|
||||||
|
})
|
||||||
|
.text_color(if active {
|
||||||
|
theme::sidebar_text_active()
|
||||||
|
} else {
|
||||||
|
theme::text_secondary()
|
||||||
|
})
|
||||||
|
.child(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_section(label: &'static str, rows: Vec<gpui::Div>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.p_5()
|
||||||
|
.rounded_lg()
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme::panel_border())
|
||||||
|
.bg(theme::panel())
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_lg()
|
||||||
|
.text_color(theme::text_primary())
|
||||||
|
.child(label),
|
||||||
|
)
|
||||||
|
.child(div().mt(px(16.0)).children(rows))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn settings_row(label: &'static str, value: String) -> gpui::Div {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.justify_between()
|
||||||
|
.items_center()
|
||||||
|
.py_3()
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(theme::panel_border())
|
||||||
|
.child(div().text_sm().text_color(theme::text_muted()).child(label))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.text_color(theme::text_primary())
|
||||||
|
.child(value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_company_name(company: Option<&domain::Company>) -> String {
|
||||||
|
company
|
||||||
|
.map(|item| item.name.clone())
|
||||||
|
.unwrap_or_else(|| "No company".to_string())
|
||||||
|
}
|
||||||
@@ -5,50 +5,148 @@ impl AppRoot {
|
|||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.gap_3()
|
.gap_6()
|
||||||
.children(self.snapshot.workspaces.iter().map(|workspace| {
|
.child(
|
||||||
let active =
|
|
||||||
self.state.selected_workspace_id.as_deref() == Some(workspace.id.as_str());
|
|
||||||
let workspace_id = workspace.id.clone();
|
|
||||||
div()
|
div()
|
||||||
.id(SharedString::from(format!("workspace-{}", workspace.id)))
|
.flex()
|
||||||
.p_4()
|
.justify_between()
|
||||||
.gap_2()
|
.items_start()
|
||||||
.border_1()
|
|
||||||
.rounded_md()
|
|
||||||
.border_color(theme::panel_border())
|
|
||||||
.bg(if active {
|
|
||||||
theme::panel_inset()
|
|
||||||
} else {
|
|
||||||
theme::panel()
|
|
||||||
})
|
|
||||||
.cursor_pointer()
|
|
||||||
.hover(|style| style.bg(theme::panel_hover()))
|
|
||||||
.on_click(cx.listener(move |this, _, _, cx| {
|
|
||||||
this.state.selected_workspace_id = Some(workspace_id.clone());
|
|
||||||
cx.notify();
|
|
||||||
}))
|
|
||||||
.child(div().text_lg().child(workspace.name.clone()))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.text_sm()
|
.child(
|
||||||
.text_color(theme::text_muted())
|
div()
|
||||||
.child(workspace.local_path.clone()),
|
.text_2xl()
|
||||||
)
|
.text_color(theme::text_primary())
|
||||||
.child(
|
.child("Workspaces"),
|
||||||
div()
|
)
|
||||||
.text_sm()
|
.child(
|
||||||
.text_color(theme::text_muted())
|
div()
|
||||||
.child(format!(
|
.mt(px(6.0))
|
||||||
"{} · {}",
|
.text_sm()
|
||||||
workspace.state,
|
.text_color(theme::text_muted())
|
||||||
if workspace.is_primary {
|
.child(format!("{} linked workspaces", self.snapshot.workspaces.len())),
|
||||||
"primary"
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(div().h(px(1.0)).bg(theme::panel_border()))
|
||||||
|
.when(self.snapshot.workspaces.is_empty(), |container| {
|
||||||
|
container.child(
|
||||||
|
div()
|
||||||
|
.min_h(px(260.0))
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_center()
|
||||||
|
.rounded_lg()
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme::panel_border())
|
||||||
|
.bg(theme::panel())
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.w(px(420.0))
|
||||||
|
.text_center()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_xl()
|
||||||
|
.text_color(theme::text_primary())
|
||||||
|
.child("No workspaces linked"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.mt(px(10.0))
|
||||||
|
.text_base()
|
||||||
|
.text_color(theme::text_muted())
|
||||||
|
.child("The active company will show its local workspaces here."),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when(!self.snapshot.workspaces.is_empty(), |container| {
|
||||||
|
container.child(
|
||||||
|
div()
|
||||||
|
.children(self.snapshot.workspaces.iter().map(|workspace| {
|
||||||
|
let active = self.state.selected_workspace_id.as_deref()
|
||||||
|
== Some(workspace.id.as_str());
|
||||||
|
let workspace_id = workspace.id.clone();
|
||||||
|
div()
|
||||||
|
.id(SharedString::from(format!("workspace-{}", workspace.id)))
|
||||||
|
.flex()
|
||||||
|
.justify_between()
|
||||||
|
.items_center()
|
||||||
|
.px_6()
|
||||||
|
.py_5()
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(theme::panel_border())
|
||||||
|
.cursor_pointer()
|
||||||
|
.bg(if active {
|
||||||
|
theme::selection_fill()
|
||||||
} else {
|
} else {
|
||||||
"linked"
|
theme::window_bg()
|
||||||
}
|
})
|
||||||
)),
|
.hover(|style| style.bg(theme::panel_hover()))
|
||||||
)
|
.on_click(cx.listener(move |this, _, _, cx| {
|
||||||
}))
|
this.state.selected_workspace_id = Some(workspace_id.clone());
|
||||||
|
cx.notify();
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_base()
|
||||||
|
.text_color(theme::text_primary())
|
||||||
|
.child(workspace.name.clone()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.mt(px(8.0))
|
||||||
|
.text_sm()
|
||||||
|
.text_color(theme::text_muted())
|
||||||
|
.child(workspace.local_path.clone()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.mt(px(8.0))
|
||||||
|
.text_xs()
|
||||||
|
.text_color(theme::text_subtle())
|
||||||
|
.child(format!(
|
||||||
|
"{} · {}",
|
||||||
|
workspace.state,
|
||||||
|
workspace_kind(workspace.is_primary)
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(workspace_state_badge(
|
||||||
|
workspace.state.as_str(),
|
||||||
|
workspace.is_primary,
|
||||||
|
))
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn workspace_kind(is_primary: bool) -> &'static str {
|
||||||
|
if is_primary { "primary" } else { "linked" }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspace_state_badge(state: &str, is_primary: bool) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_2()
|
||||||
|
.child(div().w(px(8.0)).h(px(8.0)).rounded_full().bg(match state {
|
||||||
|
"ready" => theme::accent_teal(),
|
||||||
|
"blocked" | "missing" | "error" => theme::danger(),
|
||||||
|
_ => theme::accent_blue(),
|
||||||
|
}))
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(theme::text_secondary())
|
||||||
|
.child(if is_primary {
|
||||||
|
format!("{} · PRIMARY", state.to_uppercase())
|
||||||
|
} else {
|
||||||
|
state.to_uppercase()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user