Add the ability to follow the agent as it makes edits (#29839)
Nathan here: I also tacked on a bunch of UI refinement. Release Notes: - Introduced the ability to follow the agent around as it reads and edits files. --------- Co-authored-by: Nathan Sobo <nathan@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
425f32e068
commit
545ae27079
37 changed files with 1255 additions and 567 deletions
|
@ -1,11 +1,10 @@
|
|||
use crate::{
|
||||
AppState, FollowerState, Pane, Workspace, WorkspaceSettings,
|
||||
AppState, CollaboratorId, FollowerState, Pane, Workspace, WorkspaceSettings,
|
||||
pane_group::element::pane_axis,
|
||||
workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical},
|
||||
};
|
||||
use anyhow::{Result, anyhow};
|
||||
use call::{ActiveCall, ParticipantLocation};
|
||||
use client::proto::PeerId;
|
||||
use collections::HashMap;
|
||||
use gpui::{
|
||||
Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, MouseButton, Pixels,
|
||||
|
@ -188,7 +187,7 @@ pub enum Member {
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct PaneRenderContext<'a> {
|
||||
pub project: &'a Entity<Project>,
|
||||
pub follower_states: &'a HashMap<PeerId, FollowerState>,
|
||||
pub follower_states: &'a HashMap<CollaboratorId, FollowerState>,
|
||||
pub active_call: Option<&'a Entity<ActiveCall>>,
|
||||
pub active_pane: &'a Entity<Pane>,
|
||||
pub app_state: &'a Arc<AppState>,
|
||||
|
@ -243,88 +242,104 @@ impl PaneLeaderDecorator for PaneRenderContext<'_> {
|
|||
None
|
||||
}
|
||||
});
|
||||
let leader = follower_state.as_ref().and_then(|(leader_id, _)| {
|
||||
let room = self.active_call?.read(cx).room()?.read(cx);
|
||||
room.remote_participant_for_peer_id(*leader_id)
|
||||
});
|
||||
let Some(leader) = leader else {
|
||||
let Some((leader_id, follower_state)) = follower_state else {
|
||||
return LeaderDecoration::default();
|
||||
};
|
||||
let is_in_unshared_view = follower_state.as_ref().map_or(false, |(_, state)| {
|
||||
state
|
||||
.active_view_id
|
||||
.is_some_and(|view_id| !state.items_by_leader_view_id.contains_key(&view_id))
|
||||
});
|
||||
let is_in_panel = follower_state
|
||||
.as_ref()
|
||||
.map_or(false, |(_, state)| state.dock_pane.is_some());
|
||||
|
||||
let mut leader_join_data = None;
|
||||
let leader_status_box = match leader.location {
|
||||
ParticipantLocation::SharedProject {
|
||||
project_id: leader_project_id,
|
||||
} => {
|
||||
if Some(leader_project_id) == self.project.read(cx).remote_id() {
|
||||
is_in_unshared_view.then(|| {
|
||||
Label::new(format!(
|
||||
"{} is in an unshared pane",
|
||||
leader.user.github_login
|
||||
))
|
||||
})
|
||||
} else {
|
||||
leader_join_data = Some((leader_project_id, leader.user.id));
|
||||
Some(Label::new(format!(
|
||||
"Follow {} to their active project",
|
||||
leader.user.github_login,
|
||||
)))
|
||||
}
|
||||
let mut leader_color;
|
||||
let status_box;
|
||||
match leader_id {
|
||||
CollaboratorId::PeerId(peer_id) => {
|
||||
let Some(leader) = self.active_call.as_ref().and_then(|call| {
|
||||
let room = call.read(cx).room()?.read(cx);
|
||||
room.remote_participant_for_peer_id(peer_id)
|
||||
}) else {
|
||||
return LeaderDecoration::default();
|
||||
};
|
||||
|
||||
let is_in_unshared_view = follower_state.active_view_id.is_some_and(|view_id| {
|
||||
!follower_state
|
||||
.items_by_leader_view_id
|
||||
.contains_key(&view_id)
|
||||
});
|
||||
|
||||
let mut leader_join_data = None;
|
||||
let leader_status_box = match leader.location {
|
||||
ParticipantLocation::SharedProject {
|
||||
project_id: leader_project_id,
|
||||
} => {
|
||||
if Some(leader_project_id) == self.project.read(cx).remote_id() {
|
||||
is_in_unshared_view.then(|| {
|
||||
Label::new(format!(
|
||||
"{} is in an unshared pane",
|
||||
leader.user.github_login
|
||||
))
|
||||
})
|
||||
} else {
|
||||
leader_join_data = Some((leader_project_id, leader.user.id));
|
||||
Some(Label::new(format!(
|
||||
"Follow {} to their active project",
|
||||
leader.user.github_login,
|
||||
)))
|
||||
}
|
||||
}
|
||||
ParticipantLocation::UnsharedProject => Some(Label::new(format!(
|
||||
"{} is viewing an unshared Zed project",
|
||||
leader.user.github_login
|
||||
))),
|
||||
ParticipantLocation::External => Some(Label::new(format!(
|
||||
"{} is viewing a window outside of Zed",
|
||||
leader.user.github_login
|
||||
))),
|
||||
};
|
||||
status_box = leader_status_box.map(|status| {
|
||||
div()
|
||||
.absolute()
|
||||
.w_96()
|
||||
.bottom_3()
|
||||
.right_3()
|
||||
.elevation_2(cx)
|
||||
.p_1()
|
||||
.child(status)
|
||||
.when_some(
|
||||
leader_join_data,
|
||||
|this, (leader_project_id, leader_user_id)| {
|
||||
let app_state = self.app_state.clone();
|
||||
this.cursor_pointer().on_mouse_down(
|
||||
MouseButton::Left,
|
||||
move |_, _, cx| {
|
||||
crate::join_in_room_project(
|
||||
leader_project_id,
|
||||
leader_user_id,
|
||||
app_state.clone(),
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
.into_any_element()
|
||||
});
|
||||
leader_color = cx
|
||||
.theme()
|
||||
.players()
|
||||
.color_for_participant(leader.participant_index.0)
|
||||
.cursor;
|
||||
}
|
||||
ParticipantLocation::UnsharedProject => Some(Label::new(format!(
|
||||
"{} is viewing an unshared Zed project",
|
||||
leader.user.github_login
|
||||
))),
|
||||
ParticipantLocation::External => Some(Label::new(format!(
|
||||
"{} is viewing a window outside of Zed",
|
||||
leader.user.github_login
|
||||
))),
|
||||
};
|
||||
let mut leader_color = cx
|
||||
.theme()
|
||||
.players()
|
||||
.color_for_participant(leader.participant_index.0)
|
||||
.cursor;
|
||||
CollaboratorId::Agent => {
|
||||
status_box = None;
|
||||
leader_color = cx.theme().players().agent().cursor;
|
||||
}
|
||||
}
|
||||
|
||||
let is_in_panel = follower_state.dock_pane.is_some();
|
||||
if is_in_panel {
|
||||
leader_color.fade_out(0.75);
|
||||
} else {
|
||||
leader_color.fade_out(0.3);
|
||||
}
|
||||
let status_box = leader_status_box.map(|status| {
|
||||
div()
|
||||
.absolute()
|
||||
.w_96()
|
||||
.bottom_3()
|
||||
.right_3()
|
||||
.elevation_2(cx)
|
||||
.p_1()
|
||||
.child(status)
|
||||
.when_some(
|
||||
leader_join_data,
|
||||
|this, (leader_project_id, leader_user_id)| {
|
||||
let app_state = self.app_state.clone();
|
||||
this.cursor_pointer()
|
||||
.on_mouse_down(MouseButton::Left, move |_, _, cx| {
|
||||
crate::join_in_room_project(
|
||||
leader_project_id,
|
||||
leader_user_id,
|
||||
app_state.clone(),
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
},
|
||||
)
|
||||
.into_any_element()
|
||||
});
|
||||
|
||||
LeaderDecoration {
|
||||
status_box,
|
||||
border: Some(leader_color),
|
||||
|
@ -339,6 +354,7 @@ impl PaneLeaderDecorator for PaneRenderContext<'_> {
|
|||
self.workspace
|
||||
}
|
||||
}
|
||||
|
||||
impl Member {
|
||||
fn new_axis(old_pane: Entity<Pane>, new_pane: Entity<Pane>, direction: SplitDirection) -> Self {
|
||||
use Axis::*;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue