feat: Add card-based layout with theme tokens

Add a card-based UI layout with consistent spacing and visual hierarchy.
The main content and sidebar are now wrapped in styled cards with gaps
between them. New theme tokens (card, raised, hover, divider,
disabled_fg) provide semantic colors for different surface levels and
states.

Create ui.rs module with helper functions (card_style, divider_h,
section_header, priority_badge) to keep styling consistent across views.
This commit is contained in:
Ignacio Perez
2025-12-27 18:44:45 -03:00
parent 3ce8886fc9
commit 58f4f2bfd0
8 changed files with 377 additions and 199 deletions
+21 -15
View File
@@ -3,6 +3,7 @@ use gpui::prelude::*;
use crate::models::{FilterState, ProjectTree}; use crate::models::{FilterState, ProjectTree};
use crate::task::{TaskOverview, TaskService}; use crate::task::{TaskOverview, TaskService};
use crate::theme::ActiveTheme; use crate::theme::ActiveTheme;
use crate::ui::{card_style, SECTION_GAP, ROOT_PADDING};
use crate::view::sidebar::{Sidebar, TagItem}; use crate::view::sidebar::{Sidebar, TagItem};
use crate::view::status_bar::StatusBar; use crate::view::status_bar::StatusBar;
use crate::view::task_table::TaskTable; use crate::view::task_table::TaskTable;
@@ -24,31 +25,36 @@ impl gpui::Render for App {
) -> impl gpui::IntoElement { ) -> impl gpui::IntoElement {
let theme = cx.theme(); let theme = cx.theme();
let sidebar = card_style(div(), theme)
.w(gpui::px(250.))
.h_full()
.flex_shrink_0()
.overflow_hidden()
.child(self.sidebar.clone());
let main = card_style(div(), theme)
.flex_1()
.h_full()
.min_w_0()
.overflow_hidden()
.p_0()
.child(self.task_table.clone());
let content = div() let content = div()
.flex() .flex()
.flex_1() .flex_1()
.min_h_0() .min_h_0()
.overflow_hidden() .gap(SECTION_GAP)
.child( .child(sidebar)
div() .child(main);
.w(gpui::px(250.))
.h_full()
.flex_shrink_0()
.child(self.sidebar.clone()),
)
.child(
div()
.flex_1()
.h_full()
.min_w_0()
.child(self.task_table.clone()),
);
div() div()
.flex() .flex()
.flex_col() .flex_col()
.size_full() .size_full()
.bg(theme.background) .bg(theme.background)
.p(ROOT_PADDING)
.gap(SECTION_GAP)
.child(content) .child(content)
.child(self.status_bar.clone()) .child(self.status_bar.clone())
} }
+3 -2
View File
@@ -300,6 +300,7 @@ impl gpui::Render for Dropdown {
.bg(theme.background) .bg(theme.background)
.text_sm() .text_sm()
.text_color(theme.foreground) .text_color(theme.foreground)
.whitespace_nowrap()
.child(Label::new(label.clone())) .child(Label::new(label.clone()))
.child(Label::new(arrow).text_color(theme.muted)); .child(Label::new(arrow).text_color(theme.muted));
@@ -308,14 +309,14 @@ impl gpui::Render for Dropdown {
} else { } else {
trigger = trigger trigger = trigger
.cursor_pointer() .cursor_pointer()
.hover(|s: gpui::StyleRefinement| s.bg(theme.selection)); .hover(|s: gpui::StyleRefinement| s.bg(theme.hover));
} }
trigger.into_any_element() trigger.into_any_element()
}; };
let mut trigger_wrap = gpui::div() let mut trigger_wrap = gpui::div()
.min_w(gpui::rems(12.0)) .flex_shrink_0()
.child(trigger); .child(trigger);
if !disabled { if !disabled {
trigger_wrap = trigger_wrap.on_mouse_down( trigger_wrap = trigger_wrap.on_mouse_down(
+1
View File
@@ -5,6 +5,7 @@ mod components;
mod models; mod models;
mod task; mod task;
mod theme; mod theme;
mod ui;
mod view; mod view;
fn main() { fn main() {
+144 -41
View File
@@ -1,79 +1,182 @@
use gpui::prelude::*;
pub type Color = gpui::Rgba; pub type Color = gpui::Rgba;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Theme { pub struct Theme {
pub background: Color, pub background: Color,
pub foreground: Color,
pub panel: Color, pub panel: Color,
pub card: Color,
pub raised: Color,
pub foreground: Color,
pub muted: Color, pub muted: Color,
pub disabled_fg: Color,
pub accent: Color, pub accent: Color,
pub focus_ring: Color,
pub border: Color, pub border: Color,
pub divider: Color,
pub field_bg: Color,
pub field_border: Color,
pub field_placeholder: Color,
pub hover: Color,
pub selection: Color,
pub selection_foreground: Color,
pub backdrop: Color,
pub error: Color, pub error: Color,
pub success: Color, pub success: Color,
pub warning: Color, pub warning: Color,
pub info: Color, pub info: Color,
pub selection: Color,
pub selection_foreground: Color,
pub text: Color,
pub text_size: Option<gpui::Size<u32>>,
pub high: Color, pub high: Color,
pub medium: Color, pub medium: Color,
pub low: Color, pub low: Color,
pub text_size: Option<gpui::Size<u32>>,
} }
impl Theme { impl Theme {
pub fn alpha(c: Color, a: f32) -> Color {
gpui::Rgba { r: c.r, g: c.g, b: c.b, a: a.clamp(0.0, 1.0) }
}
pub fn dark() -> Self { pub fn dark() -> Self {
let background = gpui::rgb(0x0A0E14);
let panel = gpui::rgb(0x0F1419);
let foreground = gpui::rgb(0xB3B1AD);
let muted = gpui::rgb(0x5C6773);
let accent = gpui::rgb(0xFFB454);
let border = gpui::rgb(0x1F2430);
let card = gpui::rgb(0x111823);
let raised = gpui::rgb(0x151E2B);
let divider = Self::alpha(foreground, 0.10);
let hover = Self::alpha(foreground, 0.05);
let field_bg = raised;
let field_border = Self::alpha(foreground, 0.14);
let field_placeholder = Self::alpha(foreground, 0.45);
let focus_ring = Self::alpha(accent, 0.75);
let disabled_fg = Self::alpha(foreground, 0.35);
let backdrop = gpui::rgba(0x0000008C);
let selection = gpui::rgb(0x273747);
let selection_foreground = gpui::rgb(0xE6E1CF);
let error = gpui::rgb(0xF07178);
let success = gpui::rgb(0xAAD94C);
let warning = gpui::rgb(0xFFB454);
let info = gpui::rgb(0x59C2FF);
Self { Self {
// ayu-dark background,
background: gpui::rgb(0x0A0E14), panel,
panel: gpui::rgb(0x0F1419), card,
foreground: gpui::rgb(0xB3B1AD), raised,
muted: gpui::rgb(0x5C6773),
accent: gpui::rgb(0xFFB454),
border: gpui::rgb(0x1F2430),
error: gpui::rgb(0xF07178), foreground,
success: gpui::rgb(0xAAD94C), muted,
warning: gpui::rgb(0xFFB454), disabled_fg,
info: gpui::rgb(0x59C2FF),
selection: gpui::rgb(0x273747), accent,
selection_foreground: gpui::rgb(0xE6E1CF), focus_ring,
border,
divider,
field_bg,
field_border,
field_placeholder,
hover,
selection,
selection_foreground,
backdrop,
error,
success,
warning,
info,
high: error,
medium: warning,
low: success,
text: gpui::rgb(0xB3B1AD),
text_size: Some(gpui::Size::new(14, 14)), text_size: Some(gpui::Size::new(14, 14)),
high: gpui::rgb(0xF07178),
medium: gpui::rgb(0xFFB454),
low: gpui::rgb(0xAAD94C),
} }
} }
pub fn light() -> Self { pub fn light() -> Self {
let background = gpui::rgb(0xFAFAFA);
let panel = gpui::rgb(0xFFFFFF);
let foreground = gpui::rgb(0x5C6166);
let muted = gpui::rgb(0x8A9199);
let accent = gpui::rgb(0xFF8F40);
let border = gpui::rgb(0xE6E6E6);
let card = gpui::rgb(0xF6F7F9);
let raised = gpui::rgb(0xEFF1F5);
let divider = Self::alpha(foreground, 0.12);
let hover = Self::alpha(foreground, 0.06);
let field_bg = gpui::rgb(0xFFFFFF);
let field_border = Self::alpha(foreground, 0.18);
let field_placeholder = Self::alpha(foreground, 0.55);
let focus_ring = Self::alpha(accent, 0.70);
let disabled_fg = Self::alpha(foreground, 0.40);
let backdrop = gpui::rgba(0x00000040);
let selection = gpui::rgb(0xD3EBFF);
let selection_foreground = foreground;
let error = gpui::rgb(0xE65050);
let success = gpui::rgb(0x86B300);
let warning = gpui::rgb(0xF2AE49);
let info = gpui::rgb(0x399EE6);
Self { Self {
// ayu-light background,
background: gpui::rgb(0xFAFAFA), panel,
panel: gpui::rgb(0xFFFFFF), card,
foreground: gpui::rgb(0x5C6166), raised,
muted: gpui::rgb(0x8A9199),
accent: gpui::rgb(0xFF8F40),
border: gpui::rgb(0xE6E6E6),
error: gpui::rgb(0xE65050), foreground,
success: gpui::rgb(0x86B300), muted,
warning: gpui::rgb(0xF2AE49), disabled_fg,
info: gpui::rgb(0x399EE6),
selection: gpui::rgb(0xD3EBFF), accent,
selection_foreground: gpui::rgb(0x5C6166), focus_ring,
border,
divider,
field_bg,
field_border,
field_placeholder,
hover,
selection,
selection_foreground,
backdrop,
error,
success,
warning,
info,
high: error,
medium: warning,
low: success,
text: gpui::rgb(0x5C6166),
text_size: Some(gpui::Size::new(14, 14)), text_size: Some(gpui::Size::new(14, 14)),
high: gpui::rgb(0xE65050),
medium: gpui::rgb(0xF2AE49),
low: gpui::rgb(0x86B300),
} }
} }
+82
View File
@@ -0,0 +1,82 @@
use gpui::prelude::*;
use gpui::{px, Pixels};
use crate::theme::Theme;
pub const CARD_RADIUS: Pixels = px(6.0);
pub const CARD_PADDING: Pixels = px(8.0);
pub const SECTION_GAP: Pixels = px(12.0);
pub const INSET_GAP: Pixels = px(8.0);
pub const ROOT_PADDING: Pixels = px(12.0);
pub fn card_style(div: gpui::Div, theme: &Theme) -> gpui::Div {
div.bg(theme.card)
.border_1()
.border_color(theme.divider)
.rounded(CARD_RADIUS)
.p(CARD_PADDING)
}
pub fn raised_style(div: gpui::Div, theme: &Theme) -> gpui::Div {
div.bg(theme.raised)
.border_1()
.border_color(theme.divider)
.rounded(CARD_RADIUS)
.p(CARD_PADDING)
}
pub fn panel_style(div: gpui::Div, theme: &Theme) -> gpui::Div {
div.bg(theme.panel)
.border_1()
.border_color(theme.border)
.rounded(CARD_RADIUS)
}
pub fn field_style(div: gpui::Div, theme: &Theme, focused: bool) -> gpui::Div {
div.bg(theme.field_bg)
.border_1()
.border_color(if focused {
theme.focus_ring
} else {
theme.field_border
})
.rounded(CARD_RADIUS)
.px_2()
.py_1()
}
pub fn divider_h(theme: &Theme) -> gpui::Div {
gpui::div().w_full().h(px(1.0)).bg(theme.divider)
}
pub fn divider_v(theme: &Theme) -> gpui::Div {
gpui::div().h_full().w(px(1.0)).bg(theme.divider)
}
pub fn section_header(label: &str, theme: &Theme) -> gpui::Div {
gpui::div()
.px_2()
.py_1()
.text_xs()
.text_color(theme.muted)
.child(label.to_uppercase())
}
pub fn priority_badge(priority: &str, theme: &Theme) -> gpui::Div {
let (bg, fg) = match priority {
"High" | "H" => (Theme::alpha(theme.high, 0.15), theme.high),
"Medium" | "M" => (Theme::alpha(theme.medium, 0.15), theme.medium),
"Low" | "L" => (Theme::alpha(theme.low, 0.15), theme.low),
_ => (gpui::rgba(0x00000000), theme.muted),
};
gpui::div()
.px_2()
.py(px(2.0))
.rounded(px(4.0))
.bg(bg)
.text_color(fg)
.text_xs()
.font_weight(gpui::FontWeight::MEDIUM)
.child(priority.to_string())
}
+96 -112
View File
@@ -1,6 +1,6 @@
use crate::components::{label::Label, panel::Panel};
use crate::models::{FilterState, ProjectTree}; use crate::models::{FilterState, ProjectTree};
use crate::theme::ActiveTheme; use crate::theme::ActiveTheme;
use crate::ui::{divider_h, section_header};
use gpui::{Context, Div, Entity, IntoElement, Window, div, prelude::*, px}; use gpui::{Context, Div, Entity, IntoElement, Window, div, prelude::*, px};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -100,9 +100,10 @@ impl Sidebar {
.gap_2() .gap_2()
.px_3() .px_3()
.py_1() .py_1()
.rounded_sm()
.cursor_pointer() .cursor_pointer()
.when(is_all_selected, |this| this.bg(theme.selection)) .when(is_all_selected, |this| this.bg(theme.selection))
.hover(|style| style.bg(theme.panel)) .when(!is_all_selected, |this| this.hover(|style| style.bg(theme.hover)))
.on_mouse_down( .on_mouse_down(
gpui::MouseButton::Left, gpui::MouseButton::Left,
cx.listener(|view, _event, window, cx| { cx.listener(|view, _event, window, cx| {
@@ -149,9 +150,10 @@ impl Sidebar {
.gap_1() .gap_1()
.px_3() .px_3()
.py_1() .py_1()
.rounded_sm()
.cursor_pointer() .cursor_pointer()
.when(is_selected, |this| this.bg(theme.selection)) .when(is_selected, |this| this.bg(theme.selection))
.hover(|style| style.bg(theme.panel)) .when(!is_selected, |this| this.hover(|style| style.bg(theme.hover)))
.child(div().w(px(indent as f32))) .child(div().w(px(indent as f32)))
.child( .child(
div() div()
@@ -230,9 +232,10 @@ impl Sidebar {
.gap_2() .gap_2()
.px_3() .px_3()
.py_1() .py_1()
.rounded_sm()
.cursor_pointer() .cursor_pointer()
.when(is_active, |this| this.bg(theme.selection)) .when(is_active, |this| this.bg(theme.selection))
.hover(|style| style.bg(theme.panel)) .when(!is_active, |this| this.hover(|style| style.bg(theme.hover)))
.on_mouse_down( .on_mouse_down(
gpui::MouseButton::Left, gpui::MouseButton::Left,
cx.listener(move |view, _event, window, cx| { cx.listener(move |view, _event, window, cx| {
@@ -279,115 +282,96 @@ impl Render for Sidebar {
let has_project = filter.selected_project.is_some(); let has_project = filter.selected_project.is_some();
let has_tags = !filter.active_tags.is_empty(); let has_tags = !filter.active_tags.is_empty();
Panel::new("Sidebar").border(1.0).padding(0.0).child( div()
div() .flex()
.flex() .flex_col()
.flex_col() .size_full()
.size_full() .child(
.bg(theme.background) div()
.child( .flex()
div() .flex_col()
.flex() .flex_1()
.flex_col() .min_h_0()
.flex_1() .overflow_hidden()
.min_h_0() .child(
.overflow_hidden() div()
.child( .flex_shrink_0()
div() .flex()
.flex_shrink_0() .items_center()
.flex() .justify_between()
.items_center() .px_2()
.justify_between() .py_2()
.px_3() .child(section_header("Projects", &theme))
.py_2() .when(has_project, |this| {
.border_b_1() this.child(
.border_color(theme.border) div()
.child( .id("clear-project")
Label::new("PROJECTS") .text_xs()
.text_sm() .text_color(theme.muted)
.font_weight(gpui::FontWeight::BOLD) .cursor_pointer()
.text_color(theme.foreground), .hover(|s| s.text_color(theme.accent))
.on_mouse_down(
gpui::MouseButton::Left,
cx.listener(|view, _, window, cx| {
view.handle_clear_project(window, cx);
}),
)
.child("Clear"),
) )
.when(has_project, |this| { }),
this.child( )
div() .child(
.id("clear-project") div()
.text_xs() .id("sidebar-projects")
.text_color(theme.muted) .flex_1()
.cursor_pointer() .min_h_0()
.hover(|s| s.text_color(theme.accent)) .overflow_y_scroll()
.on_mouse_down( .children(projects),
gpui::MouseButton::Left, ),
cx.listener(|view, _, window, cx| { )
view.handle_clear_project(window, cx); .child(divider_h(&theme).my_1())
}), .child(
) div()
.child("Clear"), .flex()
) .flex_col()
}), .flex_1()
) .min_h_0()
.child( .overflow_hidden()
div() .child(
.id("sidebar-projects") div()
.flex_1() .flex_shrink_0()
.min_h_0() .flex()
.py_1() .items_center()
.overflow_y_scroll() .justify_between()
.children(projects), .px_2()
), .py_2()
) .child(section_header("Tags", &theme))
.child(div().flex_shrink_0().h_px().bg(theme.border)) .when(has_tags, |this| {
.child( this.child(
div() div()
.flex() .id("clear-tags")
.flex_col() .text_xs()
.flex_1() .text_color(theme.muted)
.min_h_0() .cursor_pointer()
.overflow_hidden() .hover(|s| s.text_color(theme.accent))
.child( .on_mouse_down(
div() gpui::MouseButton::Left,
.flex_shrink_0() cx.listener(|view, _, window, cx| {
.flex() view.handle_clear_tags(window, cx);
.items_center() }),
.justify_between() )
.px_3() .child("Clear"),
.py_2()
.border_b_1()
.border_color(theme.border)
.child(
Label::new("TAGS")
.text_sm()
.font_weight(gpui::FontWeight::BOLD)
.text_color(theme.foreground),
) )
.when(has_tags, |this| { }),
this.child( )
div() .child(
.id("clear-tags") div()
.text_xs() .id("sidebar-tags")
.text_color(theme.muted) .flex_1()
.cursor_pointer() .min_h_0()
.hover(|s| s.text_color(theme.accent)) .overflow_y_scroll()
.on_mouse_down( .children(tags),
gpui::MouseButton::Left, ),
cx.listener(|view, _, window, cx| { )
view.handle_clear_tags(window, cx);
}),
)
.child("Clear"),
)
}),
)
.child(
div()
.id("sidebar-tags")
.flex_1()
.min_h_0()
.py_1()
.overflow_y_scroll()
.children(tags),
),
),
)
} }
} }
+5 -6
View File
@@ -1,6 +1,7 @@
use gpui::{Context, IntoElement, Render, Window, div, prelude::*, px}; use gpui::{Context, IntoElement, Render, Window, div, prelude::*, px};
use crate::theme::ActiveTheme; use crate::theme::ActiveTheme;
use crate::ui::{card_style, CARD_RADIUS};
pub struct StatusBar { pub struct StatusBar {
// TODO: Add vim_mode, sync_state, last_sync, etc. // TODO: Add vim_mode, sync_state, last_sync, etc.
@@ -16,16 +17,14 @@ impl Render for StatusBar {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme().clone(); let theme = cx.theme().clone();
div() card_style(div(), &theme)
.flex() .flex()
.items_center() .items_center()
.justify_between() .justify_between()
.w_full() .w_full()
.h(px(30.0)) .h(px(28.0))
.px_4() .px_3()
.py_1() .py_1()
.bg(theme.panel) .rounded(CARD_RADIUS)
.border_b_1()
.border_color(theme.border)
} }
} }
+25 -23
View File
@@ -13,6 +13,7 @@ use crate::{
models::{DueFilter, FilterState, PriorityFilter, StatusFilter}, models::{DueFilter, FilterState, PriorityFilter, StatusFilter},
task::{self, TaskFilter, TaskService}, task::{self, TaskFilter, TaskService},
theme::{self, ActiveTheme}, theme::{self, ActiveTheme},
ui::priority_badge,
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -604,15 +605,14 @@ impl TaskTable {
let bar = gpui::div() let bar = gpui::div()
.id("filter-bar") .id("filter-bar")
.flex() .flex()
.flex_wrap() .gap_3()
.gap_2()
.items_center() .items_center()
.px_4() .px_4()
.py_2() .py_2()
.bg(theme.panel)
.child( .child(
gpui::div() gpui::div()
.w(gpui::rems(14.0)) .flex_1()
.min_w(gpui::rems(12.0))
.child(self.search_input.clone()), .child(self.search_input.clone()),
) )
.child(self.status_dropdown.clone()) .child(self.status_dropdown.clone())
@@ -622,13 +622,14 @@ impl TaskTable {
this.child( this.child(
gpui::div() gpui::div()
.id("clear-all-filters") .id("clear-all-filters")
.flex_shrink_0()
.px_2() .px_2()
.py_1() .py_1()
.rounded_md() .rounded_md()
.text_sm() .text_sm()
.text_color(theme.error) .text_color(theme.error)
.cursor_pointer() .cursor_pointer()
.hover(|s| s.bg(theme.selection)) .hover(|s| s.bg(theme.hover))
.on_mouse_down( .on_mouse_down(
gpui::MouseButton::Left, gpui::MouseButton::Left,
cx.listener(|table, _, _, cx| { cx.listener(|table, _, _, cx| {
@@ -702,10 +703,9 @@ impl TaskTable {
.gap_2() .gap_2()
.px_4() .px_4()
.py_2() .py_2()
.border_t_1() .bg(theme.raised)
.border_color(theme.border)
.border_b_1() .border_b_1()
.bg(theme.panel) .border_color(theme.divider)
.text_sm() .text_sm()
.font_weight(gpui::FontWeight::MEDIUM) .font_weight(gpui::FontWeight::MEDIUM)
.child( .child(
@@ -756,12 +756,12 @@ impl TaskTable {
.px_4() .px_4()
.py_1() .py_1()
.border_b_1() .border_b_1()
.border_color(theme.border) .border_color(theme.divider)
.text_color(theme.foreground) .text_color(theme.foreground)
.when(selected, |d| { .when(selected, |d| {
d.bg(theme.selection).text_color(theme.selection_foreground) d.bg(theme.selection).text_color(theme.selection_foreground)
}) })
.when(!selected, |d| d.hover(|s| s.bg(theme.panel))) .when(!selected, |d| d.hover(|s| s.bg(theme.hover)))
.cursor_pointer() .cursor_pointer()
.on_mouse_down( .on_mouse_down(
gpui::MouseButton::Left, gpui::MouseButton::Left,
@@ -802,11 +802,9 @@ impl TaskTable {
components::label::Label::new(row.due.clone()).text_color(self.due_color(row, cx)), components::label::Label::new(row.due.clone()).text_color(self.due_color(row, cx)),
)) ))
.child( .child(
gpui::div().w(gpui::rems(5.0)).child( gpui::div()
components::label::Label::new(row.priority.clone()) .w(gpui::rems(5.0))
.text_color(self.priority_color(row, cx)) .child(priority_badge(&row.priority, theme)),
.font_weight(gpui::FontWeight::BOLD),
),
) )
.child( .child(
gpui::div().w(gpui::rems(6.0)).child( gpui::div().w(gpui::rems(6.0)).child(
@@ -831,8 +829,8 @@ impl TaskTable {
.px_4() .px_4()
.py_2() .py_2()
.border_t_1() .border_t_1()
.border_color(theme.border) .border_color(theme.divider)
.bg(theme.panel) .bg(theme.raised)
.text_sm() .text_sm()
.child( .child(
components::label::Label::new(format!( components::label::Label::new(format!(
@@ -866,13 +864,15 @@ impl TaskTable {
.px_2() .px_2()
.py_1() .py_1()
.rounded_md() .rounded_md()
.border_1()
.border_color(theme.divider)
.text_color(if can_prev { .text_color(if can_prev {
theme.foreground theme.foreground
} else { } else {
theme.muted theme.disabled_fg
}) })
.when(can_prev, |d| { .when(can_prev, |d| {
d.cursor_pointer().hover(|s| s.bg(theme.selection)) d.cursor_pointer().hover(|s| s.bg(theme.hover))
}) })
.when(!can_prev, |d| d.cursor_not_allowed()) .when(!can_prev, |d| d.cursor_not_allowed())
.on_mouse_down( .on_mouse_down(
@@ -887,13 +887,15 @@ impl TaskTable {
.px_2() .px_2()
.py_1() .py_1()
.rounded_md() .rounded_md()
.border_1()
.border_color(theme.divider)
.text_color(if can_next { .text_color(if can_next {
theme.foreground theme.foreground
} else { } else {
theme.muted theme.disabled_fg
}) })
.when(can_next, |d| { .when(can_next, |d| {
d.cursor_pointer().hover(|s| s.bg(theme.selection)) d.cursor_pointer().hover(|s| s.bg(theme.hover))
}) })
.when(!can_next, |d| d.cursor_not_allowed()) .when(!can_next, |d| d.cursor_not_allowed())
.on_mouse_down( .on_mouse_down(
@@ -924,7 +926,7 @@ impl gpui::Render for TaskTable {
.size_full() .size_full()
.items_center() .items_center()
.justify_center() .justify_center()
.child(components::label::Label::new("Loading...").text_color(theme.text)); .child(components::label::Label::new("Loading...").text_color(theme.foreground));
} }
let current_page = self.get_current_page_rows(); let current_page = self.get_current_page_rows();
@@ -945,7 +947,7 @@ impl gpui::Render for TaskTable {
.min_h_0() .min_h_0()
.overflow_hidden() .overflow_hidden()
.bg(theme.background) .bg(theme.background)
.child(gpui::div().h(self.filter_bar_height)) .child(gpui::div().h(self.filter_bar_height).mb_4())
.child(header) .child(header)
.child( .child(
gpui::div() gpui::div()