From d4cf387817d9280bc965768337cb1d6a5337743e Mon Sep 17 00:00:00 2001 From: Oz Date: Sat, 27 Jun 2026 01:30:25 +0000 Subject: [PATCH] Fix agent tips not appearing below the warping indicator The rotating agent tips that render below the "Warping..." text stopped appearing. PR #9297 (commit 76cccd24d) wrapped the warping indicator's row in a `Clipped` element to keep the action chips from overflowing into the adjacent pane on narrow widths. That row sits inside a `ConstrainedBox` whose height was fixed to a single line of text. Before the clip, the tip rendered on a second line and simply overflowed the fixed-height box visibly; once the row was clipped to the box bounds, that second line (the tip, or the fallback-model explanation) was clipped away entirely. Reserve room for the secondary line in the footer height when a secondary element is present, so the clip no longer hides it while still clipping the chips horizontally as intended. The height computation is extracted into `warping_footer_height` and covered by a regression test. Co-Authored-By: Oz --- .../ai/blocklist/block/view_impl/common.rs | 26 ++++++++++++++++++- .../blocklist/block/view_impl/common_tests.rs | 24 +++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/app/src/ai/blocklist/block/view_impl/common.rs b/app/src/ai/blocklist/block/view_impl/common.rs index 14b45a90aa..bf77acd6d0 100644 --- a/app/src/ai/blocklist/block/view_impl/common.rs +++ b/app/src/ai/blocklist/block/view_impl/common.rs @@ -526,6 +526,23 @@ pub struct WarpingIndicatorProps { pub secondary_element: Option>, } +/// Computes the fixed height of the warping-indicator footer. +/// +/// The warping text occupies a single line. When a secondary element (an agent +/// tip or fallback-model explanation) is present, it renders on a second line +/// below the warping text, so the footer must reserve room for that extra line; +/// otherwise the `Clipped` wrapper — which keeps action chips from overflowing +/// narrow panes — also clips the secondary line. The extra line accounts for the +/// secondary element's font size (`monospace_font_size - 3`, see +/// `render_agent_tip` / `render_fallback_explanation`) plus its 1px top margin. +fn warping_footer_height(monospace_font_size: f32, has_secondary_element: bool) -> f32 { + let mut height = STATUS_FOOTER_VERTICAL_PADDING * 2. + monospace_font_size; + if has_secondary_element { + height += (monospace_font_size - 3.) + 1.; + } + height +} + /// Helper function to render text in the "warping..." footer. /// Additional text that does not use the shimmering text animation can be passed in via /// `non_shimmering_text` which is useful if you want some part of the text to constantly update @@ -543,6 +560,10 @@ pub fn render_warping_indicator_base( is_passive_code_diff, secondary_element, } = props; + // Whether a secondary element (an agent tip or fallback-model explanation) + // will be rendered on a second line below the warping text. Captured before + // `secondary_element` is consumed so the container can reserve room for it. + let has_secondary_element = secondary_element.is_some(); // Unicode code point for the Warp glyph that is embedded in the version of Roboto we bundle // into the app. This code point MUST be rendered using Roboto (the default ui font) or else the // glyph may not be rendered. @@ -646,7 +667,10 @@ pub fn render_warping_indicator_base( } else { let mut container = Container::new( ConstrainedBox::new(content) - .with_height(STATUS_FOOTER_VERTICAL_PADDING * 2. + appearance.monospace_font_size()) + .with_height(warping_footer_height( + appearance.monospace_font_size(), + has_secondary_element, + )) .finish(), ) .with_padding_right(CONTENT_HORIZONTAL_PADDING); diff --git a/app/src/ai/blocklist/block/view_impl/common_tests.rs b/app/src/ai/blocklist/block/view_impl/common_tests.rs index 4b87303ce9..f2d4ea8439 100644 --- a/app/src/ai/blocklist/block/view_impl/common_tests.rs +++ b/app/src/ai/blocklist/block/view_impl/common_tests.rs @@ -17,8 +17,8 @@ use super::{ collect_visual_markdown_lightbox_collection, compute_visual_section_width, image_tooltip_handles_for_group, inline_image_source_label, is_supported_blocklist_image_source, lightbox_trigger_for_section, query_prefix_highlight_len, - render_scrollable_collapsible_content, text_sections_with_indices, CollapsibleElementState, - CollapsibleExpansionState, VisualMarkdownLightboxCollection, + render_scrollable_collapsible_content, text_sections_with_indices, warping_footer_height, + CollapsibleElementState, CollapsibleExpansionState, VisualMarkdownLightboxCollection, }; use crate::ai::agent::{ AIAgentInput, AIAgentTextSection, AgentOutputImage, AgentOutputImageLayout, @@ -161,6 +161,26 @@ fn render_scrollable_collapsible_content_returns_none_when_collapsed() { ); } +#[test] +fn warping_footer_height_reserves_a_line_for_the_secondary_element() { + // Regression: the warping indicator's footer is a fixed-height, clipped + // container. When an agent tip (or fallback-model explanation) is present it + // renders on a second line, so the footer must be taller than the + // single-line case — otherwise the clip (added to keep action chips from + // overflowing narrow panes) hides the tip entirely. + let font_size = 13.; + let without_tip = warping_footer_height(font_size, false); + let with_tip = warping_footer_height(font_size, true); + + assert!( + with_tip > without_tip, + "footer with a secondary element ({with_tip}) should be taller than without ({without_tip})", + ); + // The extra room must cover the secondary line: its font size + // (monospace_font_size - 3) plus the 1px top margin on the tip container. + assert_eq!(with_tip - without_tip, (font_size - 3.) + 1.); +} + #[test] fn compute_visual_section_width_rejects_non_finite_dimensions() { assert_eq!(compute_visual_section_width(f32::INFINITY, 20., 40.), None);