agent_ui: Remove Keep/Reject buttons (#46456)

Release Notes:

- N/A
This commit is contained in:
Michael Benfield 2026-01-12 09:09:24 -08:00 committed by GitHub
parent 2b6e935b09
commit c9e6238163
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 91 additions and 115 deletions

1
Cargo.lock generated
View file

@ -362,6 +362,7 @@ dependencies = [
"fs",
"futures 0.3.31",
"fuzzy",
"git_ui",
"gpui",
"gpui_tokio",
"html_to_markdown",

View file

@ -47,6 +47,7 @@ feature_flags.workspace = true
file_icons.workspace = true
fs.workspace = true
futures.workspace = true
git_ui.workspace = true
fuzzy.workspace = true
gpui.workspace = true
gpui_tokio.workspace = true

View file

@ -4,7 +4,7 @@ use acp_thread::{
ToolCallStatus, UserMessageId,
};
use acp_thread::{AgentConnection, Plan};
use action_log::{ActionLog, ActionLogTelemetry};
use action_log::ActionLogTelemetry;
use agent::{DbThreadMetadata, NativeAgentServer, SharedThread, ThreadStore};
use agent_client_protocol::{self as acp, PromptCapabilities};
use agent_servers::{AgentServer, AgentServerDelegate};
@ -45,7 +45,7 @@ use std::sync::Arc;
use std::time::Instant;
use std::{collections::BTreeMap, rc::Rc, time::Duration};
use terminal_view::terminal_panel::TerminalPanel;
use text::{Anchor, ToPoint as _};
use text::ToPoint as _;
use theme::{AgentFontSize, ThemeSettings};
use ui::{
Callout, CommonAnimationExt, ContextMenu, ContextMenuEntry, CopyButton, DiffStat, Disclosure,
@ -71,8 +71,8 @@ use crate::ui::{AgentNotification, AgentNotificationEvent, BurnModeTooltip, Usag
use crate::{
AgentDiffPane, AgentPanel, AllowAlways, AllowOnce, ClearMessageQueue, ContinueThread,
ContinueWithBurnMode, CycleFavoriteModels, CycleModeSelector, ExpandMessageEditor, Follow,
KeepAll, NewThread, OpenAgentDiff, OpenHistory, QueueMessage, RejectAll, RejectOnce,
SendNextQueuedMessage, ToggleBurnMode, ToggleProfileSelector,
KeepAll, NewThread, OpenHistory, QueueMessage, RejectAll, RejectOnce, SendNextQueuedMessage,
ToggleBurnMode, ToggleProfileSelector,
};
const STOPWATCH_THRESHOLD: Duration = Duration::from_secs(1);
@ -4341,7 +4341,6 @@ impl AcpThreadView {
) -> Option<AnyElement> {
let thread = thread_entity.read(cx);
let action_log = thread.action_log();
let telemetry = ActionLogTelemetry::from(thread);
let changed_buffers = action_log.read(cx).changed_buffers(cx);
let plan = thread.plan();
@ -4387,13 +4386,7 @@ impl AcpThreadView {
cx,
))
.when(self.edits_expanded, |parent| {
parent.child(self.render_edited_files(
action_log,
telemetry,
&changed_buffers,
pending_edits,
cx,
))
parent.child(self.render_edited_files(&changed_buffers, cx))
})
})
.when(!self.message_queue.is_empty(), |this| {
@ -4571,8 +4564,6 @@ impl AcpThreadView {
pending_edits: bool,
cx: &Context<Self>,
) -> Div {
const EDIT_NOT_READY_TOOLTIP_LABEL: &str = "Wait until file edits are complete.";
let focus_handle = self.focus_handle(cx);
h_flex()
@ -4651,66 +4642,21 @@ impl AcpThreadView {
})),
)
.child(
h_flex()
.gap_1()
.child(
IconButton::new("review-changes", IconName::ListTodo)
.icon_size(IconSize::Small)
.tooltip({
let focus_handle = focus_handle.clone();
move |_window, cx| {
Tooltip::for_action_in(
"Review Changes",
&OpenAgentDiff,
&focus_handle,
cx,
)
}
})
.on_click(cx.listener(|_, _, window, cx| {
window.dispatch_action(OpenAgentDiff.boxed_clone(), cx);
})),
Button::new("review-changes", "Review Changes")
.label_size(LabelSize::Small)
.key_binding(
KeyBinding::for_action_in(&git_ui::project_diff::Diff, &focus_handle, cx)
.map(|kb| kb.size(rems_from_px(10.))),
)
.child(Divider::vertical().color(DividerColor::Border))
.child(
Button::new("reject-all-changes", "Reject All")
.label_size(LabelSize::Small)
.disabled(pending_edits)
.when(pending_edits, |this| {
this.tooltip(Tooltip::text(EDIT_NOT_READY_TOOLTIP_LABEL))
})
.key_binding(
KeyBinding::for_action_in(&RejectAll, &focus_handle.clone(), cx)
.map(|kb| kb.size(rems_from_px(10.))),
)
.on_click(cx.listener(move |this, _, window, cx| {
this.reject_all(&RejectAll, window, cx);
})),
)
.child(
Button::new("keep-all-changes", "Keep All")
.label_size(LabelSize::Small)
.disabled(pending_edits)
.when(pending_edits, |this| {
this.tooltip(Tooltip::text(EDIT_NOT_READY_TOOLTIP_LABEL))
})
.key_binding(
KeyBinding::for_action_in(&KeepAll, &focus_handle, cx)
.map(|kb| kb.size(rems_from_px(10.))),
)
.on_click(cx.listener(move |this, _, window, cx| {
this.keep_all(&KeepAll, window, cx);
})),
),
.on_click(cx.listener(move |_, _, window, cx| {
window.dispatch_action(git_ui::project_diff::Diff.boxed_clone(), cx);
})),
)
}
fn render_edited_files(
&self,
action_log: &Entity<ActionLog>,
telemetry: ActionLogTelemetry,
changed_buffers: &BTreeMap<Entity<Buffer>, Entity<BufferDiff>>,
pending_edits: bool,
cx: &Context<Self>,
) -> impl IntoElement {
let editor_bg_color = cx.theme().colors().editor_background;
@ -4843,56 +4789,27 @@ impl AcpThreadView {
.label_size(LabelSize::Small)
.on_click({
let buffer = buffer.clone();
cx.listener(move |this, _, window, cx| {
this.open_edited_buffer(&buffer, window, cx);
})
}),
)
.child(Divider::vertical().color(DividerColor::BorderVariant))
.child(
Button::new("reject-file", "Reject")
.label_size(LabelSize::Small)
.disabled(pending_edits)
.on_click({
let buffer = buffer.clone();
let action_log = action_log.clone();
let telemetry = telemetry.clone();
move |_, _, cx| {
action_log.update(cx, |action_log, cx| {
action_log
.reject_edits_in_ranges(
buffer.clone(),
vec![Anchor::min_max_range_for_buffer(
buffer.read(cx).remote_id(),
)],
Some(telemetry.clone()),
cx,
)
.detach_and_log_err(cx);
})
}
}),
)
.child(
Button::new("keep-file", "Keep")
.label_size(LabelSize::Small)
.disabled(pending_edits)
.on_click({
let buffer = buffer.clone();
let action_log = action_log.clone();
let telemetry = telemetry.clone();
move |_, _, cx| {
action_log.update(cx, |action_log, cx| {
action_log.keep_edits_in_range(
buffer.clone(),
Anchor::min_max_range_for_buffer(
buffer.read(cx).remote_id(),
),
Some(telemetry.clone()),
let workspace = self.workspace.clone();
cx.listener(move |_, _, window, cx| {
let Some(workspace) = workspace.upgrade() else {
return;
};
let Some(file) = buffer.read(cx).file() else {
return;
};
let project_path = project::ProjectPath {
worktree_id: file.worktree_id(cx),
path: file.path().clone(),
};
workspace.update(cx, |workspace, cx| {
git_ui::project_diff::ProjectDiff::deploy_at_project_path(
workspace,
project_path,
window,
cx,
);
})
}
});
})
}),
),
);
@ -7291,6 +7208,7 @@ fn terminal_command_markdown_style(window: &Window, cx: &App) -> MarkdownStyle {
#[cfg(test)]
pub(crate) mod tests {
use acp_thread::StubAgentConnection;
use action_log::ActionLog;
use agent_client_protocol::SessionId;
use editor::MultiBufferOffset;
use fs::FakeFs;

View file

@ -182,6 +182,37 @@ impl ProjectDiff {
}
}
pub fn deploy_at_project_path(
workspace: &mut Workspace,
project_path: ProjectPath,
window: &mut Window,
cx: &mut Context<Workspace>,
) {
telemetry::event!("Git Diff Opened", source = "Agent Panel");
let existing = workspace
.items_of_type::<Self>(cx)
.find(|item| matches!(item.read(cx).diff_base(cx), DiffBase::Head));
let project_diff = if let Some(existing) = existing {
workspace.activate_item(&existing, true, true, window, cx);
existing
} else {
let workspace_handle = cx.entity();
let project_diff =
cx.new(|cx| Self::new(workspace.project().clone(), workspace_handle, window, cx));
workspace.add_item_to_active_pane(
Box::new(project_diff.clone()),
None,
true,
window,
cx,
);
project_diff
};
project_diff.update(cx, |project_diff, cx| {
project_diff.move_to_project_path(&project_path, window, cx);
});
}
pub fn autoscroll(&self, cx: &mut Context<Self>) {
self.editor.update(cx, |editor, cx| {
editor.primary_editor().update(cx, |editor, cx| {
@ -356,6 +387,31 @@ impl ProjectDiff {
self.move_to_path(path_key, window, cx)
}
pub fn move_to_project_path(
&mut self,
project_path: &ProjectPath,
window: &mut Window,
cx: &mut Context<Self>,
) {
let Some(git_repo) = self.branch_diff.read(cx).repo() else {
return;
};
let Some(repo_path) = git_repo
.read(cx)
.project_path_to_repo_path(project_path, cx)
else {
return;
};
let status = git_repo
.read(cx)
.status_for_path(&repo_path)
.map(|entry| entry.status)
.unwrap_or(FileStatus::Untracked);
let sort_prefix = sort_prefix(&git_repo.read(cx), &repo_path, status, cx);
let path_key = PathKey::with_sort_prefix(sort_prefix, repo_path.as_ref().clone());
self.move_to_path(path_key, window, cx)
}
pub fn active_path(&self, cx: &App) -> Option<ProjectPath> {
let editor = self.editor.read(cx).last_selected_editor().read(cx);
let position = editor.selections.newest_anchor().head();