@@ -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()
}
@@ -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)
}
}