diff --git a/ui-gpui/src/components/project_box.rs b/ui-gpui/src/components/project_box.rs index c4e4b6e..a93c266 100644 --- a/ui-gpui/src/components/project_box.rs +++ b/ui-gpui/src/components/project_box.rs @@ -110,7 +110,7 @@ impl ProjectBox { let should_pulse = matches!(self.project.agent.status, AgentStatus::Running); if should_pulse { let blink = cx.new(|_cx| crate::components::blink_state::BlinkState::new()); - crate::components::blink_state::BlinkState::start(&blink, cx); + crate::components::blink_state::BlinkState::start(&blink, &mut *cx); self.blink_state = Some(blink); } @@ -151,7 +151,7 @@ impl Render for ProjectBox { div() .flex_1() .w_full() - .child(pane.clone().into_cached_view()), + .child(pane.clone().into_cached_flex()), ); } @@ -172,7 +172,7 @@ impl Render for ProjectBox { div() .w_full() .h(px(180.0)) - .child(term.clone().into_cached_view()), + .child(term.clone().into_cached_flex()), ); } diff --git a/ui-gpui/src/components/project_grid.rs b/ui-gpui/src/components/project_grid.rs index 65a68b6..40588c3 100644 --- a/ui-gpui/src/components/project_grid.rs +++ b/ui-gpui/src/components/project_grid.rs @@ -60,9 +60,7 @@ impl Render for ProjectGrid { .overflow_y_scroll(); for pb in &self.project_boxes { - // Cached: ProjectBox only re-renders when its entity is notified - // (e.g., PulsingDot blink). Sibling ProjectBoxes serve from GPU cache. - grid = grid.child(pb.clone().into_cached_view()); + grid = grid.child(pb.clone()); } if self.project_boxes.is_empty() { diff --git a/ui-gpui/src/main.rs b/ui-gpui/src/main.rs index 9786b0f..cdb858f 100644 --- a/ui-gpui/src/main.rs +++ b/ui-gpui/src/main.rs @@ -43,13 +43,32 @@ mod workspace; /// Extension trait to create cached AnyView from Entity. /// Cached views skip re-render when their entity is not dirty — /// GPUI replays previous frame's GPU scene commands via memcpy. +/// +/// IMPORTANT: The StyleRefinement must specify size/flex for the cached wrapper +/// node. Without it, the wrapper collapses to zero size on first render. pub trait CachedView { - fn into_cached_view(self) -> gpui::AnyView; + /// Cache with flex-1 + full size (for content areas) + fn into_cached_flex(self) -> gpui::AnyView; + /// Cache with natural size (for sidebar, status bar) + fn into_cached_natural(self) -> gpui::AnyView; } impl CachedView for gpui::Entity { - fn into_cached_view(self) -> gpui::AnyView { - gpui::AnyView::from(self).cached(gpui::StyleRefinement::default()) + fn into_cached_flex(self) -> gpui::AnyView { + let mut style = gpui::StyleRefinement::default(); + style.flex_grow = Some(1.0); + style.size.width = Some(gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0)).into()); + style.size.height = Some(gpui::Length::Definite(gpui::DefiniteLength::Fraction(1.0)).into()); + gpui::AnyView::from(self).cached(style) + } + + fn into_cached_natural(self) -> gpui::AnyView { + let mut style = gpui::StyleRefinement::default(); + // Natural size — don't set width/height, let content determine size + // But set min_size to prevent collapse + style.min_size.width = Some(gpui::Length::Definite(gpui::DefiniteLength::Absolute(gpui::AbsoluteLength::Pixels(gpui::px(1.0)))).into()); + style.min_size.height = Some(gpui::Length::Definite(gpui::DefiniteLength::Absolute(gpui::AbsoluteLength::Pixels(gpui::px(1.0)))).into()); + gpui::AnyView::from(self).cached(style) } } diff --git a/ui-gpui/src/workspace.rs b/ui-gpui/src/workspace.rs index 3afe04e..b7e4329 100644 --- a/ui-gpui/src/workspace.rs +++ b/ui-gpui/src/workspace.rs @@ -85,34 +85,23 @@ impl Render for Workspace { .flex_row() .overflow_hidden(); - // Sidebar (icon rail) — cached: only re-renders when sidebar entity is notified + // Sidebar (icon rail) — uncached (tiny, cheap to render) if sidebar_open { - main_row = main_row.child( - self.sidebar.clone().into_cached_view(), - ); + main_row = main_row.child(self.sidebar.clone()); } - // Settings drawer (between sidebar and grid) — cached + // Settings drawer (between sidebar and grid) if settings_open { - main_row = main_row.child( - self.settings_panel.clone().into_cached_view(), - ); + main_row = main_row.child(self.settings_panel.clone()); } - // Project grid (fills remaining space) — cached: only re-renders when grid entity is notified - main_row = main_row.child( - div() - .flex_1() - .h_full() - .child(self.project_grid.clone().into_cached_view()), - ); + // Project grid (fills remaining space) — cached with flex-1 + main_row = main_row.child(self.project_grid.clone().into_cached_flex()); root = root.child(main_row); - // ── Status bar (bottom) — cached: only re-renders on status change - root = root.child( - self.status_bar.clone().into_cached_view(), - ); + // Status bar (bottom) — uncached (tiny, cheap to render) + root = root.child(self.status_bar.clone()); // ── Command palette overlay (if open) ─────────────── if palette_open {