Use agent panel font size for all content in thread / history views and fix text thread font size adjust (#30041)

Michael Sloan created

Release Notes:

- N/A

Change summary

crates/agent/src/active_thread.rs   | 356 ++++++++++++++----------------
crates/agent/src/assistant_panel.rs | 188 ++++++++++-----
2 files changed, 289 insertions(+), 255 deletions(-)

Detailed changes

crates/agent/src/active_thread.rs 🔗

@@ -43,7 +43,6 @@ use std::sync::Arc;
 use std::time::Duration;
 use text::ToPoint;
 use theme::ThemeSettings;
-use ui::utils::WithRemSize;
 use ui::{
     Disclosure, IconButton, KeyBinding, PopoverMenuHandle, Scrollbar, ScrollbarState, TextSize,
     Tooltip, prelude::*,
@@ -2073,202 +2072,185 @@ impl ActiveThread {
 
         let panel_background = cx.theme().colors().panel_background;
 
-        WithRemSize::new(ThemeSettings::get_global(cx).agent_font_size(cx))
-            .size_full()
-            .child(
-                v_flex()
-                    .w_full()
-                    .map(|parent| {
-                        if let Some(checkpoint) = checkpoint.filter(|_| !is_generating) {
-                            let mut is_pending = false;
-                            let mut error = None;
-                            if let Some(last_restore_checkpoint) =
-                                self.thread.read(cx).last_restore_checkpoint()
-                            {
-                                if last_restore_checkpoint.message_id() == message_id {
-                                    match last_restore_checkpoint {
-                                        LastRestoreCheckpoint::Pending { .. } => is_pending = true,
-                                        LastRestoreCheckpoint::Error { error: err, .. } => {
-                                            error = Some(err.clone());
-                                        }
-                                    }
+        v_flex()
+            .w_full()
+            .map(|parent| {
+                if let Some(checkpoint) = checkpoint.filter(|_| !is_generating) {
+                    let mut is_pending = false;
+                    let mut error = None;
+                    if let Some(last_restore_checkpoint) =
+                        self.thread.read(cx).last_restore_checkpoint()
+                    {
+                        if last_restore_checkpoint.message_id() == message_id {
+                            match last_restore_checkpoint {
+                                LastRestoreCheckpoint::Pending { .. } => is_pending = true,
+                                LastRestoreCheckpoint::Error { error: err, .. } => {
+                                    error = Some(err.clone());
                                 }
                             }
+                        }
+                    }
 
-                            let restore_checkpoint_button =
-                                Button::new(("restore-checkpoint", ix), "Restore Checkpoint")
-                                    .icon(if error.is_some() {
-                                        IconName::XCircle
-                                    } else {
-                                        IconName::Undo
-                                    })
-                                    .icon_size(IconSize::XSmall)
-                                    .icon_position(IconPosition::Start)
-                                    .icon_color(if error.is_some() {
-                                        Some(Color::Error)
-                                    } else {
-                                        None
-                                    })
-                                    .label_size(LabelSize::XSmall)
-                                    .disabled(is_pending)
-                                    .on_click(cx.listener(move |this, _, _window, cx| {
-                                        this.thread.update(cx, |thread, cx| {
-                                            thread
-                                                .restore_checkpoint(checkpoint.clone(), cx)
-                                                .detach_and_log_err(cx);
-                                        });
-                                    }));
-
-                            let restore_checkpoint_button = if is_pending {
-                                restore_checkpoint_button
-                                    .with_animation(
-                                        ("pulsating-restore-checkpoint-button", ix),
-                                        Animation::new(Duration::from_secs(2))
-                                            .repeat()
-                                            .with_easing(pulsating_between(0.6, 1.)),
-                                        |label, delta| label.alpha(delta),
-                                    )
-                                    .into_any_element()
-                            } else if let Some(error) = error {
-                                restore_checkpoint_button
-                                    .tooltip(Tooltip::text(error.to_string()))
-                                    .into_any_element()
+                    let restore_checkpoint_button =
+                        Button::new(("restore-checkpoint", ix), "Restore Checkpoint")
+                            .icon(if error.is_some() {
+                                IconName::XCircle
                             } else {
-                                restore_checkpoint_button.into_any_element()
-                            };
-
-                            parent.child(
-                                h_flex()
-                                    .pt_2p5()
-                                    .px_2p5()
-                                    .w_full()
-                                    .gap_1()
-                                    .child(ui::Divider::horizontal())
-                                    .child(restore_checkpoint_button)
-                                    .child(ui::Divider::horizontal()),
+                                IconName::Undo
+                            })
+                            .icon_size(IconSize::XSmall)
+                            .icon_position(IconPosition::Start)
+                            .icon_color(if error.is_some() {
+                                Some(Color::Error)
+                            } else {
+                                None
+                            })
+                            .label_size(LabelSize::XSmall)
+                            .disabled(is_pending)
+                            .on_click(cx.listener(move |this, _, _window, cx| {
+                                this.thread.update(cx, |thread, cx| {
+                                    thread
+                                        .restore_checkpoint(checkpoint.clone(), cx)
+                                        .detach_and_log_err(cx);
+                                });
+                            }));
+
+                    let restore_checkpoint_button = if is_pending {
+                        restore_checkpoint_button
+                            .with_animation(
+                                ("pulsating-restore-checkpoint-button", ix),
+                                Animation::new(Duration::from_secs(2))
+                                    .repeat()
+                                    .with_easing(pulsating_between(0.6, 1.)),
+                                |label, delta| label.alpha(delta),
                             )
-                        } else {
-                            parent
-                        }
-                    })
-                    .when(is_first_message, |parent| {
-                        parent.child(self.render_rules_item(cx))
-                    })
-                    .child(styled_message)
-                    .when(is_generating && is_last_message, |this| {
-                        this.child(
-                            h_flex()
-                                .h_8()
-                                .mt_2()
-                                .mb_4()
-                                .ml_4()
-                                .py_1p5()
-                                .when_some(loading_dots, |this, loading_dots| {
-                                    this.child(loading_dots)
-                                }),
-                        )
-                    })
-                    .when(show_feedback, move |parent| {
-                        parent.child(feedback_items).when_some(
-                            self.open_feedback_editors.get(&message_id),
-                            move |parent, feedback_editor| {
-                                let focus_handle = feedback_editor.focus_handle(cx);
-                                parent.child(
-                                    v_flex()
-                                        .key_context("AgentFeedbackMessageEditor")
-                                        .on_action(cx.listener(
-                                            move |this, _: &menu::Cancel, _, cx| {
-                                                this.open_feedback_editors.remove(&message_id);
-                                                cx.notify();
-                                            },
-                                        ))
-                                        .on_action(cx.listener(
-                                            move |this, _: &menu::Confirm, _, cx| {
-                                                this.submit_feedback_message(message_id, cx);
-                                                cx.notify();
-                                            },
-                                        ))
-                                        .on_action(cx.listener(Self::confirm_editing_message))
-                                        .mb_2()
-                                        .mx_4()
-                                        .p_2()
-                                        .rounded_md()
-                                        .border_1()
-                                        .border_color(cx.theme().colors().border)
-                                        .bg(cx.theme().colors().editor_background)
-                                        .child(feedback_editor.clone())
+                            .into_any_element()
+                    } else if let Some(error) = error {
+                        restore_checkpoint_button
+                            .tooltip(Tooltip::text(error.to_string()))
+                            .into_any_element()
+                    } else {
+                        restore_checkpoint_button.into_any_element()
+                    };
+
+                    parent.child(
+                        h_flex()
+                            .pt_2p5()
+                            .px_2p5()
+                            .w_full()
+                            .gap_1()
+                            .child(ui::Divider::horizontal())
+                            .child(restore_checkpoint_button)
+                            .child(ui::Divider::horizontal()),
+                    )
+                } else {
+                    parent
+                }
+            })
+            .when(is_first_message, |parent| {
+                parent.child(self.render_rules_item(cx))
+            })
+            .child(styled_message)
+            .when(is_generating && is_last_message, |this| {
+                this.child(
+                    h_flex()
+                        .h_8()
+                        .mt_2()
+                        .mb_4()
+                        .ml_4()
+                        .py_1p5()
+                        .when_some(loading_dots, |this, loading_dots| this.child(loading_dots)),
+                )
+            })
+            .when(show_feedback, move |parent| {
+                parent.child(feedback_items).when_some(
+                    self.open_feedback_editors.get(&message_id),
+                    move |parent, feedback_editor| {
+                        let focus_handle = feedback_editor.focus_handle(cx);
+                        parent.child(
+                            v_flex()
+                                .key_context("AgentFeedbackMessageEditor")
+                                .on_action(cx.listener(move |this, _: &menu::Cancel, _, cx| {
+                                    this.open_feedback_editors.remove(&message_id);
+                                    cx.notify();
+                                }))
+                                .on_action(cx.listener(move |this, _: &menu::Confirm, _, cx| {
+                                    this.submit_feedback_message(message_id, cx);
+                                    cx.notify();
+                                }))
+                                .on_action(cx.listener(Self::confirm_editing_message))
+                                .mb_2()
+                                .mx_4()
+                                .p_2()
+                                .rounded_md()
+                                .border_1()
+                                .border_color(cx.theme().colors().border)
+                                .bg(cx.theme().colors().editor_background)
+                                .child(feedback_editor.clone())
+                                .child(
+                                    h_flex()
+                                        .gap_1()
+                                        .justify_end()
                                         .child(
-                                            h_flex()
-                                                .gap_1()
-                                                .justify_end()
-                                                .child(
-                                                    Button::new(
-                                                        "dismiss-feedback-message",
-                                                        "Cancel",
-                                                    )
-                                                    .label_size(LabelSize::Small)
-                                                    .key_binding(
-                                                        KeyBinding::for_action_in(
-                                                            &menu::Cancel,
-                                                            &focus_handle,
-                                                            window,
-                                                            cx,
-                                                        )
-                                                        .map(|kb| kb.size(rems_from_px(10.))),
+                                            Button::new("dismiss-feedback-message", "Cancel")
+                                                .label_size(LabelSize::Small)
+                                                .key_binding(
+                                                    KeyBinding::for_action_in(
+                                                        &menu::Cancel,
+                                                        &focus_handle,
+                                                        window,
+                                                        cx,
                                                     )
-                                                    .on_click(cx.listener(
-                                                        move |this, _, _window, cx| {
-                                                            this.open_feedback_editors
-                                                                .remove(&message_id);
-                                                            cx.notify();
-                                                        },
-                                                    )),
+                                                    .map(|kb| kb.size(rems_from_px(10.))),
                                                 )
-                                                .child(
-                                                    Button::new(
-                                                        "submit-feedback-message",
-                                                        "Share Feedback",
-                                                    )
-                                                    .style(ButtonStyle::Tinted(
-                                                        ui::TintColor::Accent,
-                                                    ))
-                                                    .label_size(LabelSize::Small)
-                                                    .key_binding(
-                                                        KeyBinding::for_action_in(
-                                                            &menu::Confirm,
-                                                            &focus_handle,
-                                                            window,
-                                                            cx,
-                                                        )
-                                                        .map(|kb| kb.size(rems_from_px(10.))),
-                                                    )
-                                                    .on_click(cx.listener(
-                                                        move |this, _, _window, cx| {
-                                                            this.submit_feedback_message(
-                                                                message_id, cx,
-                                                            );
-                                                            cx.notify()
-                                                        },
-                                                    )),
-                                                ),
+                                                .on_click(cx.listener(
+                                                    move |this, _, _window, cx| {
+                                                        this.open_feedback_editors
+                                                            .remove(&message_id);
+                                                        cx.notify();
+                                                    },
+                                                )),
+                                        )
+                                        .child(
+                                            Button::new(
+                                                "submit-feedback-message",
+                                                "Share Feedback",
+                                            )
+                                            .style(ButtonStyle::Tinted(ui::TintColor::Accent))
+                                            .label_size(LabelSize::Small)
+                                            .key_binding(
+                                                KeyBinding::for_action_in(
+                                                    &menu::Confirm,
+                                                    &focus_handle,
+                                                    window,
+                                                    cx,
+                                                )
+                                                .map(|kb| kb.size(rems_from_px(10.))),
+                                            )
+                                            .on_click(
+                                                cx.listener(move |this, _, _window, cx| {
+                                                    this.submit_feedback_message(message_id, cx);
+                                                    cx.notify()
+                                                }),
+                                            ),
                                         ),
-                                )
-                            },
-                        )
-                    })
-                    .when(after_editing_message, |parent| {
-                        // Backdrop to dim out the whole thread below the editing user message
-                        parent.relative().child(
-                            div()
-                                .occlude()
-                                .absolute()
-                                .inset_0()
-                                .size_full()
-                                .bg(panel_background)
-                                .opacity(0.8),
+                                ),
                         )
-                    }),
-            )
+                    },
+                )
+            })
+            .when(after_editing_message, |parent| {
+                // Backdrop to dim out the whole thread below the editing user message
+                parent.relative().child(
+                    div()
+                        .occlude()
+                        .absolute()
+                        .inset_0()
+                        .size_full()
+                        .bg(panel_background)
+                        .opacity(0.8),
+                )
+            })
             .into_any()
     }
 

crates/agent/src/assistant_panel.rs 🔗

@@ -39,6 +39,7 @@ use search::{BufferSearchBar, buffer_search};
 use settings::{Settings, update_settings_file};
 use theme::ThemeSettings;
 use time::UtcOffset;
+use ui::utils::WithRemSize;
 use ui::{
     Banner, CheckboxWithLabel, ContextMenu, KeyBinding, PopoverMenu, PopoverMenuHandle,
     ProgressBar, Tab, Tooltip, Vector, VectorName, prelude::*,
@@ -177,7 +178,21 @@ enum ActiveView {
     Configuration,
 }
 
+enum WhichFontSize {
+    AgentFont,
+    BufferFont,
+    None,
+}
+
 impl ActiveView {
+    pub fn which_font_size_used(&self) -> WhichFontSize {
+        match self {
+            ActiveView::Thread { .. } | ActiveView::History => WhichFontSize::AgentFont,
+            ActiveView::PromptEditor { .. } => WhichFontSize::BufferFont,
+            ActiveView::Configuration => WhichFontSize::None,
+        }
+    }
+
     pub fn thread(thread: Entity<Thread>, window: &mut Window, cx: &mut App) -> Self {
         let summary = thread.read(cx).summary_or_default();
 
@@ -1070,7 +1085,7 @@ impl AssistantPanel {
         _: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        self.adjust_font_size(action.persist, px(1.0), cx);
+        self.handle_font_size_action(action.persist, px(1.0), cx);
     }
 
     pub fn decrease_font_size(
@@ -1079,21 +1094,36 @@ impl AssistantPanel {
         _: &mut Window,
         cx: &mut Context<Self>,
     ) {
-        self.adjust_font_size(action.persist, px(-1.0), cx);
+        self.handle_font_size_action(action.persist, px(-1.0), cx);
     }
 
-    fn adjust_font_size(&mut self, persist: bool, delta: Pixels, cx: &mut Context<Self>) {
-        if persist {
-            update_settings_file::<ThemeSettings>(self.fs.clone(), cx, move |settings, cx| {
-                let agent_font_size = ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
-                let _ = settings
-                    .agent_font_size
-                    .insert(theme::clamp_font_size(agent_font_size).0);
-            });
-        } else {
-            theme::adjust_agent_font_size(cx, |size| {
-                *size += delta;
-            });
+    fn handle_font_size_action(&mut self, persist: bool, delta: Pixels, cx: &mut Context<Self>) {
+        match self.active_view.which_font_size_used() {
+            WhichFontSize::AgentFont => {
+                if persist {
+                    update_settings_file::<ThemeSettings>(
+                        self.fs.clone(),
+                        cx,
+                        move |settings, cx| {
+                            let agent_font_size =
+                                ThemeSettings::get_global(cx).agent_font_size(cx) + delta;
+                            let _ = settings
+                                .agent_font_size
+                                .insert(theme::clamp_font_size(agent_font_size).0);
+                        },
+                    );
+                } else {
+                    theme::adjust_agent_font_size(cx, |size| {
+                        *size += delta;
+                    });
+                }
+            }
+            WhichFontSize::BufferFont => {
+                // Prompt editor uses the buffer font size, so allow the action to propagate to the
+                // default handler that changes that font size.
+                cx.propagate();
+            }
+            WhichFontSize::None => {}
         }
     }
 
@@ -2553,6 +2583,46 @@ impl AssistantPanel {
             .into_any()
     }
 
+    fn render_prompt_editor(
+        &self,
+        context_editor: &Entity<ContextEditor>,
+        buffer_search_bar: &Entity<BufferSearchBar>,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> Div {
+        let mut registrar = buffer_search::DivRegistrar::new(
+            |this, _, _cx| match &this.active_view {
+                ActiveView::PromptEditor {
+                    buffer_search_bar, ..
+                } => Some(buffer_search_bar.clone()),
+                _ => None,
+            },
+            cx,
+        );
+        BufferSearchBar::register(&mut registrar);
+        registrar
+            .into_div()
+            .size_full()
+            .relative()
+            .map(|parent| {
+                buffer_search_bar.update(cx, |buffer_search_bar, cx| {
+                    if buffer_search_bar.is_dismissed() {
+                        return parent;
+                    }
+                    parent.child(
+                        div()
+                            .p(DynamicSpacing::Base08.rems(cx))
+                            .border_b_1()
+                            .border_color(cx.theme().colors().border_variant)
+                            .bg(cx.theme().colors().editor_background)
+                            .child(buffer_search_bar.render(window, cx)),
+                    )
+                })
+            })
+            .child(context_editor.clone())
+            .child(self.render_drag_target(cx))
+    }
+
     fn render_drag_target(&self, cx: &Context<Self>) -> Div {
         let is_local = self.project.read(cx).is_local();
         div()
@@ -2676,6 +2746,41 @@ impl AssistantPanel {
 
 impl Render for AssistantPanel {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+        let content = match &self.active_view {
+            ActiveView::Thread { .. } => v_flex()
+                .relative()
+                .justify_between()
+                .size_full()
+                .child(self.render_active_thread_or_empty_state(window, cx))
+                .children(self.render_tool_use_limit_reached(cx))
+                .child(h_flex().child(self.message_editor.clone()))
+                .children(self.render_last_error(cx))
+                .child(self.render_drag_target(cx))
+                .into_any(),
+            ActiveView::History => self.history.clone().into_any_element(),
+            ActiveView::PromptEditor {
+                context_editor,
+                buffer_search_bar,
+                ..
+            } => self
+                .render_prompt_editor(context_editor, buffer_search_bar, window, cx)
+                .into_any(),
+            ActiveView::Configuration => v_flex()
+                .size_full()
+                .children(self.configuration.clone())
+                .into_any(),
+        };
+
+        let content = match self.active_view.which_font_size_used() {
+            WhichFontSize::AgentFont => {
+                WithRemSize::new(ThemeSettings::get_global(cx).agent_font_size(cx))
+                    .size_full()
+                    .child(content)
+                    .into_any()
+            }
+            _ => content,
+        };
+
         v_flex()
             .key_context(self.key_context())
             .justify_between()
@@ -2701,60 +2806,7 @@ impl Render for AssistantPanel {
             .on_action(cx.listener(Self::reset_font_size))
             .child(self.render_toolbar(window, cx))
             .children(self.render_trial_upsell(window, cx))
-            .map(|parent| match &self.active_view {
-                ActiveView::Thread { .. } => parent.child(
-                    v_flex()
-                        .relative()
-                        .justify_between()
-                        .size_full()
-                        .child(self.render_active_thread_or_empty_state(window, cx))
-                        .children(self.render_tool_use_limit_reached(cx))
-                        .child(h_flex().child(self.message_editor.clone()))
-                        .children(self.render_last_error(cx))
-                        .child(self.render_drag_target(cx)),
-                ),
-                ActiveView::History => parent.child(self.history.clone()),
-                ActiveView::PromptEditor {
-                    context_editor,
-                    buffer_search_bar,
-                    ..
-                } => {
-                    let mut registrar = buffer_search::DivRegistrar::new(
-                        |this, _, _cx| match &this.active_view {
-                            ActiveView::PromptEditor {
-                                buffer_search_bar, ..
-                            } => Some(buffer_search_bar.clone()),
-                            _ => None,
-                        },
-                        cx,
-                    );
-                    BufferSearchBar::register(&mut registrar);
-                    parent.child(
-                        registrar
-                            .into_div()
-                            .size_full()
-                            .relative()
-                            .map(|parent| {
-                                buffer_search_bar.update(cx, |buffer_search_bar, cx| {
-                                    if buffer_search_bar.is_dismissed() {
-                                        return parent;
-                                    }
-                                    parent.child(
-                                        div()
-                                            .p(DynamicSpacing::Base08.rems(cx))
-                                            .border_b_1()
-                                            .border_color(cx.theme().colors().border_variant)
-                                            .bg(cx.theme().colors().editor_background)
-                                            .child(buffer_search_bar.render(window, cx)),
-                                    )
-                                })
-                            })
-                            .child(context_editor.clone())
-                            .child(self.render_drag_target(cx)),
-                    )
-                }
-                ActiveView::Configuration => parent.children(self.configuration.clone()),
-            })
+            .child(content)
     }
 }