@@ -4,8 +4,8 @@ use editor::actions::MoveUp;
use editor::{Editor, EditorElement, EditorEvent, EditorStyle};
use fs::Fs;
use gpui::{
- pulsating_between, Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription,
- TextStyle, WeakEntity,
+ Animation, AnimationExt, App, DismissEvent, Entity, Focusable, Subscription, TextStyle,
+ WeakEntity,
};
use language_model::LanguageModelRegistry;
use language_model_selector::ToggleModelSelector;
@@ -16,7 +16,7 @@ use text::Bias;
use theme::ThemeSettings;
use ui::{
prelude::*, ButtonLike, KeyBinding, PlatformStyle, PopoverMenu, PopoverMenuHandle, Switch,
- TintColor, Tooltip,
+ Tooltip,
};
use vim_mode_setting::VimModeSetting;
use workspace::Workspace;
@@ -298,166 +298,210 @@ impl Render for MessageEditor {
let linux = platform == PlatformStyle::Linux;
let windows = platform == PlatformStyle::Windows;
let button_width = if linux || windows || vim_mode_enabled {
- px(92.)
+ px(82.)
} else {
px(64.)
};
v_flex()
- .key_context("MessageEditor")
- .on_action(cx.listener(Self::chat))
- .on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
- this.model_selector
- .update(cx, |model_selector, cx| model_selector.toggle(window, cx));
- }))
- .on_action(cx.listener(Self::toggle_context_picker))
- .on_action(cx.listener(Self::remove_all_context))
- .on_action(cx.listener(Self::move_up))
- .on_action(cx.listener(Self::toggle_chat_mode))
.size_full()
- .gap_2()
- .p_2()
- .bg(bg_color)
- .child(self.context_strip.clone())
+ .when(is_streaming_completion, |parent| {
+ let focus_handle = self.editor.focus_handle(cx).clone();
+ parent.child(
+ h_flex().py_3().w_full().justify_center().child(
+ h_flex()
+ .flex_none()
+ .pl_2()
+ .pr_1()
+ .py_1()
+ .bg(cx.theme().colors().editor_background)
+ .border_1()
+ .border_color(cx.theme().colors().border_variant)
+ .rounded_lg()
+ .shadow_md()
+ .gap_1()
+ .child(
+ Icon::new(IconName::ArrowCircle)
+ .size(IconSize::XSmall)
+ .color(Color::Muted)
+ .with_animation(
+ "arrow-circle",
+ Animation::new(Duration::from_secs(2)).repeat(),
+ |icon, delta| {
+ icon.transform(gpui::Transformation::rotate(
+ gpui::percentage(delta),
+ ))
+ },
+ ),
+ )
+ .child(
+ Label::new("Generating…")
+ .size(LabelSize::XSmall)
+ .color(Color::Muted),
+ )
+ .child(ui::Divider::vertical())
+ .child(
+ Button::new("cancel-generation", "Cancel")
+ .label_size(LabelSize::XSmall)
+ .key_binding(
+ KeyBinding::for_action_in(
+ &editor::actions::Cancel,
+ &focus_handle,
+ window,
+ cx,
+ )
+ .map(|kb| kb.size(rems_from_px(10.))),
+ )
+ .on_click(move |_event, window, cx| {
+ focus_handle.dispatch_action(
+ &editor::actions::Cancel,
+ window,
+ cx,
+ );
+ }),
+ ),
+ ),
+ )
+ })
.child(
v_flex()
- .gap_5()
- .child({
- let settings = ThemeSettings::get_global(cx);
- let text_style = TextStyle {
- color: cx.theme().colors().text,
- font_family: settings.ui_font.family.clone(),
- font_features: settings.ui_font.features.clone(),
- font_size: font_size.into(),
- font_weight: settings.ui_font.weight,
- line_height: line_height.into(),
- ..Default::default()
- };
-
- EditorElement::new(
- &self.editor,
- EditorStyle {
- background: bg_color,
- local_player: cx.theme().players().local(),
- text: text_style,
- ..Default::default()
- },
- )
- })
+ .key_context("MessageEditor")
+ .on_action(cx.listener(Self::chat))
+ .on_action(cx.listener(|this, _: &ToggleModelSelector, window, cx| {
+ this.model_selector
+ .update(cx, |model_selector, cx| model_selector.toggle(window, cx));
+ }))
+ .on_action(cx.listener(Self::toggle_context_picker))
+ .on_action(cx.listener(Self::remove_all_context))
+ .on_action(cx.listener(Self::move_up))
+ .on_action(cx.listener(Self::toggle_chat_mode))
+ .gap_2()
+ .p_2()
+ .bg(bg_color)
+ .border_t_1()
+ .border_color(cx.theme().colors().border)
+ .child(self.context_strip.clone())
.child(
- PopoverMenu::new("inline-context-picker")
- .menu(move |window, cx| {
- inline_context_picker.update(cx, |this, cx| {
- this.init(window, cx);
- });
+ v_flex()
+ .gap_5()
+ .child({
+ let settings = ThemeSettings::get_global(cx);
+ let text_style = TextStyle {
+ color: cx.theme().colors().text,
+ font_family: settings.ui_font.family.clone(),
+ font_features: settings.ui_font.features.clone(),
+ font_size: font_size.into(),
+ font_weight: settings.ui_font.weight,
+ line_height: line_height.into(),
+ ..Default::default()
+ };
- Some(inline_context_picker.clone())
- })
- .attach(gpui::Corner::TopLeft)
- .anchor(gpui::Corner::BottomLeft)
- .offset(gpui::Point {
- x: px(0.0),
- y: (-ThemeSettings::get_global(cx).ui_font_size(cx) * 2) - px(4.0),
+ EditorElement::new(
+ &self.editor,
+ EditorStyle {
+ background: bg_color,
+ local_player: cx.theme().players().local(),
+ text: text_style,
+ ..Default::default()
+ },
+ )
})
- .with_handle(self.inline_context_picker_menu_handle.clone()),
- )
- .child(
- h_flex()
- .justify_between()
.child(
- Switch::new("use-tools", self.use_tools.into())
- .label("Tools")
- .on_click(cx.listener(|this, selection, _window, _cx| {
- this.use_tools = match selection {
- ToggleState::Selected => true,
- ToggleState::Unselected
- | ToggleState::Indeterminate => false,
- };
- }))
- .key_binding(KeyBinding::for_action_in(
- &ChatMode,
- &focus_handle,
- window,
- cx,
- )),
+ PopoverMenu::new("inline-context-picker")
+ .menu(move |window, cx| {
+ inline_context_picker.update(cx, |this, cx| {
+ this.init(window, cx);
+ });
+
+ Some(inline_context_picker.clone())
+ })
+ .attach(gpui::Corner::TopLeft)
+ .anchor(gpui::Corner::BottomLeft)
+ .offset(gpui::Point {
+ x: px(0.0),
+ y: (-ThemeSettings::get_global(cx).ui_font_size(cx) * 2)
+ - px(4.0),
+ })
+ .with_handle(self.inline_context_picker_menu_handle.clone()),
)
- .child(h_flex().gap_1().child(self.model_selector.clone()).child(
- if is_streaming_completion {
- ButtonLike::new("cancel-generation")
- .width(button_width.into())
- .style(ButtonStyle::Tinted(TintColor::Accent))
- .child(
- h_flex()
- .w_full()
- .justify_between()
- .child(
- Label::new("Cancel")
- .size(LabelSize::Small)
- .with_animation(
- "pulsating-label",
- Animation::new(Duration::from_secs(2))
- .repeat()
- .with_easing(pulsating_between(
- 0.4, 0.8,
- )),
- |label, delta| label.alpha(delta),
- ),
- )
- .children(
- KeyBinding::for_action_in(
- &editor::actions::Cancel,
- &focus_handle,
- window,
- cx,
- )
- .map(|binding| binding.into_any_element()),
- ),
- )
- .on_click(move |_event, window, cx| {
- focus_handle.dispatch_action(
- &editor::actions::Cancel,
+ .child(
+ h_flex()
+ .justify_between()
+ .child(
+ Switch::new("use-tools", self.use_tools.into())
+ .label("Tools")
+ .on_click(cx.listener(
+ |this, selection, _window, _cx| {
+ this.use_tools = match selection {
+ ToggleState::Selected => true,
+ ToggleState::Unselected
+ | ToggleState::Indeterminate => false,
+ };
+ },
+ ))
+ .key_binding(KeyBinding::for_action_in(
+ &ChatMode,
+ &focus_handle,
window,
cx,
- );
- })
- } else {
- ButtonLike::new("submit-message")
- .width(button_width.into())
- .style(ButtonStyle::Filled)
- .disabled(is_editor_empty || !is_model_selected)
- .child(
- h_flex()
- .w_full()
- .justify_between()
+ )),
+ )
+ .child(
+ h_flex().gap_1().child(self.model_selector.clone()).child(
+ ButtonLike::new("submit-message")
+ .width(button_width.into())
+ .style(ButtonStyle::Filled)
+ .disabled(
+ is_editor_empty
+ || !is_model_selected
+ || is_streaming_completion,
+ )
.child(
- Label::new("Submit")
- .size(LabelSize::Small)
- .color(submit_label_color),
+ h_flex()
+ .w_full()
+ .justify_between()
+ .child(
+ Label::new("Submit")
+ .size(LabelSize::Small)
+ .color(submit_label_color),
+ )
+ .children(
+ KeyBinding::for_action_in(
+ &Chat,
+ &focus_handle,
+ window,
+ cx,
+ )
+ .map(|binding| {
+ binding
+ .when(vim_mode_enabled, |kb| {
+ kb.size(rems_from_px(12.))
+ })
+ .into_any_element()
+ }),
+ ),
)
- .children(
- KeyBinding::for_action_in(
- &Chat,
- &focus_handle,
- window,
- cx,
- )
- .map(|binding| binding.into_any_element()),
- ),
- )
- .on_click(move |_event, window, cx| {
- focus_handle.dispatch_action(&Chat, window, cx);
- })
- .when(is_editor_empty, |button| {
- button
- .tooltip(Tooltip::text("Type a message to submit"))
- })
- .when(!is_model_selected, |button| {
- button.tooltip(Tooltip::text(
- "Select a model to continue",
- ))
- })
- },
- )),
+ .on_click(move |_event, window, cx| {
+ focus_handle.dispatch_action(&Chat, window, cx);
+ })
+ .when(is_editor_empty, |button| {
+ button.tooltip(Tooltip::text(
+ "Type a message to submit",
+ ))
+ })
+ .when(is_streaming_completion, |button| {
+ button.tooltip(Tooltip::text(
+ "Cancel to submit a new message",
+ ))
+ })
+ .when(!is_model_selected, |button| {
+ button.tooltip(Tooltip::text(
+ "Select a model to continue",
+ ))
+ }),
+ ),
+ ),
+ ),
),
)
}