agent: Add animation in the edit file tool card until a diff is assigned (#29773)
This PR prevents this edit card from being shown expanded but empty, like this: <img width="590" alt="Screenshot 2025-05-01 at 7 38 47 PM" src="https://github.com/user-attachments/assets/147d3d73-05b9-4493-8145-0ee915f12cd9" /> Now, we will show an animation until it has a diff computed. https://github.com/user-attachments/assets/52900cdf-ee3d-4c3b-88c7-c53377543bcf Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
parent
33011f2eaf
commit
225deb6785
@ -7,7 +7,8 @@ use assistant_tool::{ActionLog, AnyToolCard, Tool, ToolCard, ToolResult, ToolUse
|
|||||||
use buffer_diff::{BufferDiff, BufferDiffSnapshot};
|
use buffer_diff::{BufferDiff, BufferDiffSnapshot};
|
||||||
use editor::{Editor, EditorMode, MultiBuffer, PathKey};
|
use editor::{Editor, EditorMode, MultiBuffer, PathKey};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyWindowHandle, App, AppContext, AsyncApp, Context, Entity, EntityId, Task, WeakEntity,
|
Animation, AnimationExt, AnyWindowHandle, App, AppContext, AsyncApp, Context, Entity, EntityId,
|
||||||
|
Task, WeakEntity, pulsating_between,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
Anchor, Buffer, Capability, LanguageRegistry, LineEnding, OffsetRangeExt, Rope, TextBuffer,
|
Anchor, Buffer, Capability, LanguageRegistry, LineEnding, OffsetRangeExt, Rope, TextBuffer,
|
||||||
@ -20,6 +21,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::{
|
use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
use ui::{Disclosure, Tooltip, Window, prelude::*};
|
use ui::{Disclosure, Tooltip, Window, prelude::*};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
@ -323,6 +325,10 @@ impl EditFileToolCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_diff(&self) -> bool {
|
||||||
|
self.total_lines.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_diff(
|
pub fn set_diff(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
@ -463,45 +469,44 @@ impl ToolCard for EditFileToolCard {
|
|||||||
.rounded_t_md()
|
.rounded_t_md()
|
||||||
.when(!failed, |header| header.bg(codeblock_header_bg))
|
.when(!failed, |header| header.bg(codeblock_header_bg))
|
||||||
.child(path_label_button)
|
.child(path_label_button)
|
||||||
.map(|container| {
|
.when(failed, |header| {
|
||||||
if failed {
|
header.child(
|
||||||
container.child(
|
h_flex()
|
||||||
h_flex()
|
.gap_1()
|
||||||
.gap_1()
|
.child(
|
||||||
.child(
|
Icon::new(IconName::Close)
|
||||||
Icon::new(IconName::Close)
|
.size(IconSize::Small)
|
||||||
.size(IconSize::Small)
|
.color(Color::Error),
|
||||||
.color(Color::Error),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Disclosure::new(
|
|
||||||
("edit-file-error-disclosure", self.editor_unique_id),
|
|
||||||
self.error_expanded,
|
|
||||||
)
|
|
||||||
.opened_icon(IconName::ChevronUp)
|
|
||||||
.closed_icon(IconName::ChevronDown)
|
|
||||||
.on_click(cx.listener(
|
|
||||||
move |this, _event, _window, _cx| {
|
|
||||||
this.error_expanded = !this.error_expanded;
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
container.child(
|
|
||||||
Disclosure::new(
|
|
||||||
("edit-file-disclosure", self.editor_unique_id),
|
|
||||||
self.preview_expanded,
|
|
||||||
)
|
)
|
||||||
.opened_icon(IconName::ChevronUp)
|
.child(
|
||||||
.closed_icon(IconName::ChevronDown)
|
Disclosure::new(
|
||||||
.on_click(cx.listener(
|
("edit-file-error-disclosure", self.editor_unique_id),
|
||||||
move |this, _event, _window, _cx| {
|
self.error_expanded,
|
||||||
this.preview_expanded = !this.preview_expanded;
|
)
|
||||||
},
|
.opened_icon(IconName::ChevronUp)
|
||||||
)),
|
.closed_icon(IconName::ChevronDown)
|
||||||
|
.on_click(cx.listener(
|
||||||
|
move |this, _event, _window, _cx| {
|
||||||
|
this.error_expanded = !this.error_expanded;
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.when(!failed && self.has_diff(), |header| {
|
||||||
|
header.child(
|
||||||
|
Disclosure::new(
|
||||||
|
("edit-file-disclosure", self.editor_unique_id),
|
||||||
|
self.preview_expanded,
|
||||||
)
|
)
|
||||||
}
|
.opened_icon(IconName::ChevronUp)
|
||||||
|
.closed_icon(IconName::ChevronDown)
|
||||||
|
.on_click(cx.listener(
|
||||||
|
move |this, _event, _window, _cx| {
|
||||||
|
this.preview_expanded = !this.preview_expanded;
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let (editor, editor_line_height) = self.editor.update(cx, |editor, cx| {
|
let (editor, editor_line_height) = self.editor.update(cx, |editor, cx| {
|
||||||
@ -538,6 +543,50 @@ impl ToolCard for EditFileToolCard {
|
|||||||
const DEFAULT_COLLAPSED_LINES: u32 = 10;
|
const DEFAULT_COLLAPSED_LINES: u32 = 10;
|
||||||
let is_collapsible = self.total_lines.unwrap_or(0) > DEFAULT_COLLAPSED_LINES;
|
let is_collapsible = self.total_lines.unwrap_or(0) > DEFAULT_COLLAPSED_LINES;
|
||||||
|
|
||||||
|
let waiting_for_diff = {
|
||||||
|
let styles = [
|
||||||
|
("w_4_5", (0.1, 0.85), 2000),
|
||||||
|
("w_1_4", (0.2, 0.75), 2200),
|
||||||
|
("w_2_4", (0.15, 0.64), 1900),
|
||||||
|
("w_3_5", (0.25, 0.72), 2300),
|
||||||
|
("w_2_5", (0.3, 0.56), 1800),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut container = v_flex()
|
||||||
|
.p_3()
|
||||||
|
.gap_1p5()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(border_color)
|
||||||
|
.bg(cx.theme().colors().editor_background);
|
||||||
|
|
||||||
|
for (width_method, pulse_range, duration_ms) in styles.iter() {
|
||||||
|
let (min_opacity, max_opacity) = *pulse_range;
|
||||||
|
let placeholder = match *width_method {
|
||||||
|
"w_4_5" => div().w_3_4(),
|
||||||
|
"w_1_4" => div().w_1_4(),
|
||||||
|
"w_2_4" => div().w_2_4(),
|
||||||
|
"w_3_5" => div().w_3_5(),
|
||||||
|
"w_2_5" => div().w_2_5(),
|
||||||
|
_ => div().w_1_2(),
|
||||||
|
}
|
||||||
|
.id("loading_div")
|
||||||
|
.h_2()
|
||||||
|
.rounded_full()
|
||||||
|
.bg(cx.theme().colors().element_active)
|
||||||
|
.with_animation(
|
||||||
|
"loading_pulsate",
|
||||||
|
Animation::new(Duration::from_millis(*duration_ms))
|
||||||
|
.repeat()
|
||||||
|
.with_easing(pulsating_between(min_opacity, max_opacity)),
|
||||||
|
|label, delta| label.opacity(delta),
|
||||||
|
);
|
||||||
|
|
||||||
|
container = container.child(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
container
|
||||||
|
};
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.mb_2()
|
.mb_2()
|
||||||
.border_1()
|
.border_1()
|
||||||
@ -573,50 +622,58 @@ impl ToolCard for EditFileToolCard {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.when(!failed && self.preview_expanded, |card| {
|
.when(!self.has_diff() && !failed, |card| {
|
||||||
card.child(
|
card.child(waiting_for_diff)
|
||||||
v_flex()
|
})
|
||||||
.relative()
|
.when(
|
||||||
.h_full()
|
!failed && self.preview_expanded && self.has_diff(),
|
||||||
.when(!self.full_height_expanded, |editor_container| {
|
|card| {
|
||||||
editor_container
|
card.child(
|
||||||
.max_h(DEFAULT_COLLAPSED_LINES as f32 * editor_line_height)
|
v_flex()
|
||||||
})
|
.relative()
|
||||||
.overflow_hidden()
|
.h_full()
|
||||||
.border_t_1()
|
.when(!self.full_height_expanded, |editor_container| {
|
||||||
.border_color(border_color)
|
editor_container
|
||||||
.bg(cx.theme().colors().editor_background)
|
.max_h(DEFAULT_COLLAPSED_LINES as f32 * editor_line_height)
|
||||||
.child(div().pl_1().child(editor))
|
})
|
||||||
.when(
|
.overflow_hidden()
|
||||||
!self.full_height_expanded && is_collapsible,
|
|
||||||
|editor_container| editor_container.child(gradient_overlay),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.when(is_collapsible, |editor_container| {
|
|
||||||
editor_container.child(
|
|
||||||
h_flex()
|
|
||||||
.id(("expand-button", self.editor_unique_id))
|
|
||||||
.flex_none()
|
|
||||||
.cursor_pointer()
|
|
||||||
.h_5()
|
|
||||||
.justify_center()
|
|
||||||
.rounded_b_md()
|
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
.border_color(border_color)
|
.border_color(border_color)
|
||||||
.bg(cx.theme().colors().editor_background)
|
.bg(cx.theme().colors().editor_background)
|
||||||
.hover(|style| style.bg(cx.theme().colors().element_hover.opacity(0.1)))
|
.child(div().pl_1().child(editor))
|
||||||
.child(
|
.when(
|
||||||
Icon::new(full_height_icon)
|
!self.full_height_expanded && is_collapsible,
|
||||||
.size(IconSize::Small)
|
|editor_container| editor_container.child(gradient_overlay),
|
||||||
.color(Color::Muted),
|
),
|
||||||
)
|
|
||||||
.tooltip(Tooltip::text(full_height_tooltip_label))
|
|
||||||
.on_click(cx.listener(move |this, _event, _window, _cx| {
|
|
||||||
this.full_height_expanded = !this.full_height_expanded;
|
|
||||||
})),
|
|
||||||
)
|
)
|
||||||
})
|
.when(is_collapsible, |editor_container| {
|
||||||
})
|
editor_container.child(
|
||||||
|
h_flex()
|
||||||
|
.id(("expand-button", self.editor_unique_id))
|
||||||
|
.flex_none()
|
||||||
|
.cursor_pointer()
|
||||||
|
.h_5()
|
||||||
|
.justify_center()
|
||||||
|
.rounded_b_md()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(border_color)
|
||||||
|
.bg(cx.theme().colors().editor_background)
|
||||||
|
.hover(|style| {
|
||||||
|
style.bg(cx.theme().colors().element_hover.opacity(0.1))
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
Icon::new(full_height_icon)
|
||||||
|
.size(IconSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
.tooltip(Tooltip::text(full_height_tooltip_label))
|
||||||
|
.on_click(cx.listener(move |this, _event, _window, _cx| {
|
||||||
|
this.full_height_expanded = !this.full_height_expanded;
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user