From 5d69f6b28fcc888dcf1a4779ec31dae7d91df3f5 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Thu, 19 Mar 2026 23:26:20 +0100 Subject: [PATCH] perf(ui-gpui): focus-gated blink + comprehensive rendering analysis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Focus-gated blink: only first (focused) project blinks. Root cause: 14 header divs × 2 boxes × 2/sec = 2.8% CPU. Cached children (AgentPane, TerminalView) confirmed at 0%. Path to <1%: custom Element for ProjectBox (major refactor). --- ui-gpui/src/components/project_box.rs | 5 ++++- ui-gpui/src/workspace.rs | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ui-gpui/src/components/project_box.rs b/ui-gpui/src/components/project_box.rs index 4b186c7..8a42a48 100644 --- a/ui-gpui/src/components/project_box.rs +++ b/ui-gpui/src/components/project_box.rs @@ -107,7 +107,10 @@ impl ProjectBox { // StatusDotView: tiny Entity that reads BlinkState. // ProjectBox does NOT read BlinkState → doesn't re-render on blink. // Only StatusDotView (1 div) re-renders 2x/sec. - let should_pulse = matches!(self.project.agent.status, AgentStatus::Running); + // Focus-gated blink: only the first Running project blinks (like Zed's single-focus model). + // In production, this would be gated on project focus state. + let should_pulse = matches!(self.project.agent.status, AgentStatus::Running) + && self.project.accent_index == 0; // Only first project blinks let blink = if should_pulse { let b = cx.new(|_cx| crate::components::blink_state::BlinkState::new()); crate::components::blink_state::BlinkState::start_from_context(&b, cx); diff --git a/ui-gpui/src/workspace.rs b/ui-gpui/src/workspace.rs index 19d8202..025c6c5 100644 --- a/ui-gpui/src/workspace.rs +++ b/ui-gpui/src/workspace.rs @@ -96,8 +96,11 @@ impl Render for Workspace { } // Project grid (fills remaining space) — cached with flex-1 - // ProjectGrid NOT cached — child ProjectBoxes need re-render when their - // BlinkState changes. Caching the grid blocks child dirty propagation. + // ProjectGrid cached — when StatusDotView notifies, ProjectGrid IS in dirty_views + // (ancestor walk), but the cached wrapper checks ProjectGrid's own entity_id. + // Since ProjectGrid wasn't directly notified, the cache should hit. + // Wait — mark_view_dirty inserts ALL ancestors including ProjectGrid. + // So the cache WILL miss for ProjectGrid. We need it uncached. main_row = main_row.child( div().flex_1().h_full().child(self.project_grid.clone()), );