Add version detection for CC (#36502)
- Render a helpful message when the installed CC version is too old - Show the full path for agent binaries when the version is not recent enough (helps in cases where multiple binaries are installed in different places) - Add UI for the case where a server binary is not installed at all - Refresh thread view after installing/updating server binary Release Notes: - N/A
This commit is contained in:
parent
7c7043947b
commit
3996587c0b
8 changed files with 195 additions and 85 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -285,6 +285,7 @@ dependencies = [
|
||||||
"project",
|
"project",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"settings",
|
"settings",
|
||||||
|
|
|
@ -707,7 +707,7 @@ pub enum AcpThreadEvent {
|
||||||
Retry(RetryStatus),
|
Retry(RetryStatus),
|
||||||
Stopped,
|
Stopped,
|
||||||
Error,
|
Error,
|
||||||
ServerExited(ExitStatus),
|
LoadError(LoadError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<AcpThreadEvent> for AcpThread {}
|
impl EventEmitter<AcpThreadEvent> for AcpThread {}
|
||||||
|
@ -721,20 +721,30 @@ pub enum ThreadStatus {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LoadError {
|
pub enum LoadError {
|
||||||
|
NotInstalled {
|
||||||
|
error_message: SharedString,
|
||||||
|
install_message: SharedString,
|
||||||
|
install_command: String,
|
||||||
|
},
|
||||||
Unsupported {
|
Unsupported {
|
||||||
error_message: SharedString,
|
error_message: SharedString,
|
||||||
upgrade_message: SharedString,
|
upgrade_message: SharedString,
|
||||||
upgrade_command: String,
|
upgrade_command: String,
|
||||||
},
|
},
|
||||||
Exited(i32),
|
Exited {
|
||||||
|
status: ExitStatus,
|
||||||
|
},
|
||||||
Other(SharedString),
|
Other(SharedString),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for LoadError {
|
impl Display for LoadError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
LoadError::Unsupported { error_message, .. } => write!(f, "{}", error_message),
|
LoadError::NotInstalled { error_message, .. }
|
||||||
LoadError::Exited(status) => write!(f, "Server exited with status {}", status),
|
| LoadError::Unsupported { error_message, .. } => {
|
||||||
|
write!(f, "{error_message}")
|
||||||
|
}
|
||||||
|
LoadError::Exited { status } => write!(f, "Server exited with status {status}"),
|
||||||
LoadError::Other(msg) => write!(f, "{}", msg),
|
LoadError::Other(msg) => write!(f, "{}", msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1683,8 +1693,8 @@ impl AcpThread {
|
||||||
self.entries.iter().map(|e| e.to_markdown(cx)).collect()
|
self.entries.iter().map(|e| e.to_markdown(cx)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_server_exited(&mut self, status: ExitStatus, cx: &mut Context<Self>) {
|
pub fn emit_load_error(&mut self, error: LoadError, cx: &mut Context<Self>) {
|
||||||
cx.emit(AcpThreadEvent::ServerExited(status));
|
cx.emit(AcpThreadEvent::LoadError(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ paths.workspace = true
|
||||||
project.workspace = true
|
project.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
|
semver.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
|
|
|
@ -14,7 +14,7 @@ use anyhow::{Context as _, Result};
|
||||||
use gpui::{App, AppContext as _, AsyncApp, Entity, Task, WeakEntity};
|
use gpui::{App, AppContext as _, AsyncApp, Entity, Task, WeakEntity};
|
||||||
|
|
||||||
use crate::{AgentServerCommand, acp::UnsupportedVersion};
|
use crate::{AgentServerCommand, acp::UnsupportedVersion};
|
||||||
use acp_thread::{AcpThread, AgentConnection, AuthRequired};
|
use acp_thread::{AcpThread, AgentConnection, AuthRequired, LoadError};
|
||||||
|
|
||||||
pub struct AcpConnection {
|
pub struct AcpConnection {
|
||||||
server_name: &'static str,
|
server_name: &'static str,
|
||||||
|
@ -87,7 +87,9 @@ impl AcpConnection {
|
||||||
for session in sessions.borrow().values() {
|
for session in sessions.borrow().values() {
|
||||||
session
|
session
|
||||||
.thread
|
.thread
|
||||||
.update(cx, |thread, cx| thread.emit_server_exited(status, cx))
|
.update(cx, |thread, cx| {
|
||||||
|
thread.emit_load_error(LoadError::Exited { status }, cx)
|
||||||
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,9 @@ use smol::process::Child;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use util::command::new_smol_command;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use agent_client_protocol as acp;
|
use agent_client_protocol as acp;
|
||||||
|
@ -36,7 +37,7 @@ use util::{ResultExt, debug_panic};
|
||||||
use crate::claude::mcp_server::{ClaudeZedMcpServer, McpConfig};
|
use crate::claude::mcp_server::{ClaudeZedMcpServer, McpConfig};
|
||||||
use crate::claude::tools::ClaudeTool;
|
use crate::claude::tools::ClaudeTool;
|
||||||
use crate::{AgentServer, AgentServerCommand, AllAgentServersSettings};
|
use crate::{AgentServer, AgentServerCommand, AllAgentServersSettings};
|
||||||
use acp_thread::{AcpThread, AgentConnection, AuthRequired, MentionUri};
|
use acp_thread::{AcpThread, AgentConnection, AuthRequired, LoadError, MentionUri};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ClaudeCode;
|
pub struct ClaudeCode;
|
||||||
|
@ -103,7 +104,11 @@ impl AgentConnection for ClaudeAgentConnection {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
else {
|
else {
|
||||||
anyhow::bail!("Failed to find claude binary");
|
return Err(LoadError::NotInstalled {
|
||||||
|
error_message: "Failed to find Claude Code binary".into(),
|
||||||
|
install_message: "Install Claude Code".into(),
|
||||||
|
install_command: "npm install -g @anthropic-ai/claude-code@latest".into(),
|
||||||
|
}.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let api_key =
|
let api_key =
|
||||||
|
@ -211,9 +216,32 @@ impl AgentConnection for ClaudeAgentConnection {
|
||||||
if let Some(status) = child.status().await.log_err()
|
if let Some(status) = child.status().await.log_err()
|
||||||
&& let Some(thread) = thread_rx.recv().await.ok()
|
&& let Some(thread) = thread_rx.recv().await.ok()
|
||||||
{
|
{
|
||||||
|
let version = claude_version(command.path.clone(), cx).await.log_err();
|
||||||
|
let help = claude_help(command.path.clone(), cx).await.log_err();
|
||||||
thread
|
thread
|
||||||
.update(cx, |thread, cx| {
|
.update(cx, |thread, cx| {
|
||||||
thread.emit_server_exited(status, cx);
|
let error = if let Some(version) = version
|
||||||
|
&& let Some(help) = help
|
||||||
|
&& (!help.contains("--input-format")
|
||||||
|
|| !help.contains("--session-id"))
|
||||||
|
{
|
||||||
|
LoadError::Unsupported {
|
||||||
|
error_message: format!(
|
||||||
|
"Your installed version of Claude Code ({}, version {}) does not have required features for use with Zed.",
|
||||||
|
command.path.to_string_lossy(),
|
||||||
|
version,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
upgrade_message: "Upgrade Claude Code to latest".into(),
|
||||||
|
upgrade_command: format!(
|
||||||
|
"{} update",
|
||||||
|
command.path.to_string_lossy()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LoadError::Exited { status }
|
||||||
|
};
|
||||||
|
thread.emit_load_error(error, cx);
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
@ -383,6 +411,27 @@ fn spawn_claude(
|
||||||
Ok(child)
|
Ok(child)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn claude_version(path: PathBuf, cx: &mut AsyncApp) -> Task<Result<semver::Version>> {
|
||||||
|
cx.background_spawn(async move {
|
||||||
|
let output = new_smol_command(path).arg("--version").output().await?;
|
||||||
|
let output = String::from_utf8(output.stdout)?;
|
||||||
|
let version = output
|
||||||
|
.trim()
|
||||||
|
.strip_suffix(" (Claude Code)")
|
||||||
|
.context("parsing Claude version")?;
|
||||||
|
let version = semver::Version::parse(version)?;
|
||||||
|
anyhow::Ok(version)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn claude_help(path: PathBuf, cx: &mut AsyncApp) -> Task<Result<String>> {
|
||||||
|
cx.background_spawn(async move {
|
||||||
|
let output = new_smol_command(path).arg("--help").output().await?;
|
||||||
|
let output = String::from_utf8(output.stdout)?;
|
||||||
|
anyhow::Ok(output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
struct ClaudeAgentSession {
|
struct ClaudeAgentSession {
|
||||||
outgoing_tx: UnboundedSender<SdkMessage>,
|
outgoing_tx: UnboundedSender<SdkMessage>,
|
||||||
turn_state: Rc<RefCell<TurnState>>,
|
turn_state: Rc<RefCell<TurnState>>,
|
||||||
|
|
|
@ -50,7 +50,11 @@ impl AgentServer for Gemini {
|
||||||
let Some(command) =
|
let Some(command) =
|
||||||
AgentServerCommand::resolve("gemini", &[ACP_ARG], None, settings, &project, cx).await
|
AgentServerCommand::resolve("gemini", &[ACP_ARG], None, settings, &project, cx).await
|
||||||
else {
|
else {
|
||||||
anyhow::bail!("Failed to find gemini binary");
|
return Err(LoadError::NotInstalled {
|
||||||
|
error_message: "Failed to find Gemini CLI binary".into(),
|
||||||
|
install_message: "Install Gemini CLI".into(),
|
||||||
|
install_command: "npm install -g @google/gemini-cli@latest".into()
|
||||||
|
}.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = crate::acp::connect(server_name, command.clone(), &root_dir, cx).await;
|
let result = crate::acp::connect(server_name, command.clone(), &root_dir, cx).await;
|
||||||
|
@ -75,10 +79,11 @@ impl AgentServer for Gemini {
|
||||||
if !supported {
|
if !supported {
|
||||||
return Err(LoadError::Unsupported {
|
return Err(LoadError::Unsupported {
|
||||||
error_message: format!(
|
error_message: format!(
|
||||||
"Your installed version of Gemini {} doesn't support the Agentic Coding Protocol (ACP).",
|
"Your installed version of Gemini CLI ({}, version {}) doesn't support the Agentic Coding Protocol (ACP).",
|
||||||
|
command.path.to_string_lossy(),
|
||||||
current_version
|
current_version
|
||||||
).into(),
|
).into(),
|
||||||
upgrade_message: "Upgrade Gemini to Latest".into(),
|
upgrade_message: "Upgrade Gemini CLI to latest".into(),
|
||||||
upgrade_command: "npm install -g @google/gemini-cli@latest".into(),
|
upgrade_command: "npm install -g @google/gemini-cli@latest".into(),
|
||||||
}.into())
|
}.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ use rope::Point;
|
||||||
use settings::{Settings as _, SettingsStore};
|
use settings::{Settings as _, SettingsStore};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{collections::BTreeMap, process::ExitStatus, rc::Rc, time::Duration};
|
use std::{collections::BTreeMap, rc::Rc, time::Duration};
|
||||||
use text::Anchor;
|
use text::Anchor;
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{
|
use ui::{
|
||||||
|
@ -149,9 +149,6 @@ enum ThreadState {
|
||||||
configuration_view: Option<AnyView>,
|
configuration_view: Option<AnyView>,
|
||||||
_subscription: Option<Subscription>,
|
_subscription: Option<Subscription>,
|
||||||
},
|
},
|
||||||
ServerExited {
|
|
||||||
status: ExitStatus,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcpThreadView {
|
impl AcpThreadView {
|
||||||
|
@ -451,8 +448,7 @@ impl AcpThreadView {
|
||||||
ThreadState::Ready { thread, .. } => Some(thread),
|
ThreadState::Ready { thread, .. } => Some(thread),
|
||||||
ThreadState::Unauthenticated { .. }
|
ThreadState::Unauthenticated { .. }
|
||||||
| ThreadState::Loading { .. }
|
| ThreadState::Loading { .. }
|
||||||
| ThreadState::LoadError(..)
|
| ThreadState::LoadError { .. } => None,
|
||||||
| ThreadState::ServerExited { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +458,6 @@ impl AcpThreadView {
|
||||||
ThreadState::Loading { .. } => "Loading…".into(),
|
ThreadState::Loading { .. } => "Loading…".into(),
|
||||||
ThreadState::LoadError(_) => "Failed to load".into(),
|
ThreadState::LoadError(_) => "Failed to load".into(),
|
||||||
ThreadState::Unauthenticated { .. } => "Authentication Required".into(),
|
ThreadState::Unauthenticated { .. } => "Authentication Required".into(),
|
||||||
ThreadState::ServerExited { .. } => "Server exited unexpectedly".into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,9 +825,9 @@ impl AcpThreadView {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
AcpThreadEvent::ServerExited(status) => {
|
AcpThreadEvent::LoadError(error) => {
|
||||||
self.thread_retry_status.take();
|
self.thread_retry_status.take();
|
||||||
self.thread_state = ThreadState::ServerExited { status: *status };
|
self.thread_state = ThreadState::LoadError(error.clone());
|
||||||
}
|
}
|
||||||
AcpThreadEvent::TitleUpdated | AcpThreadEvent::TokenUsageUpdated => {}
|
AcpThreadEvent::TitleUpdated | AcpThreadEvent::TokenUsageUpdated => {}
|
||||||
}
|
}
|
||||||
|
@ -2154,28 +2149,6 @@ impl AcpThreadView {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_server_exited(&self, status: ExitStatus, _cx: &Context<Self>) -> AnyElement {
|
|
||||||
v_flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.child(self.render_error_agent_logo())
|
|
||||||
.child(
|
|
||||||
v_flex()
|
|
||||||
.mt_4()
|
|
||||||
.mb_2()
|
|
||||||
.gap_0p5()
|
|
||||||
.text_center()
|
|
||||||
.items_center()
|
|
||||||
.child(Headline::new("Server exited unexpectedly").size(HeadlineSize::Medium))
|
|
||||||
.child(
|
|
||||||
Label::new(format!("Exit status: {}", status.code().unwrap_or(-127)))
|
|
||||||
.size(LabelSize::Small)
|
|
||||||
.color(Color::Muted),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_load_error(&self, e: &LoadError, cx: &Context<Self>) -> AnyElement {
|
fn render_load_error(&self, e: &LoadError, cx: &Context<Self>) -> AnyElement {
|
||||||
let mut container = v_flex()
|
let mut container = v_flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
|
@ -2204,39 +2177,102 @@ impl AcpThreadView {
|
||||||
{
|
{
|
||||||
let upgrade_message = upgrade_message.clone();
|
let upgrade_message = upgrade_message.clone();
|
||||||
let upgrade_command = upgrade_command.clone();
|
let upgrade_command = upgrade_command.clone();
|
||||||
container = container.child(Button::new("upgrade", upgrade_message).on_click(
|
container = container.child(
|
||||||
cx.listener(move |this, _, window, cx| {
|
Button::new("upgrade", upgrade_message)
|
||||||
this.workspace
|
.tooltip(Tooltip::text(upgrade_command.clone()))
|
||||||
.update(cx, |workspace, cx| {
|
.on_click(cx.listener(move |this, _, window, cx| {
|
||||||
let project = workspace.project().read(cx);
|
let task = this
|
||||||
let cwd = project.first_project_directory(cx);
|
.workspace
|
||||||
let shell = project.terminal_settings(&cwd, cx).shell.clone();
|
.update(cx, |workspace, cx| {
|
||||||
let spawn_in_terminal = task::SpawnInTerminal {
|
let project = workspace.project().read(cx);
|
||||||
id: task::TaskId("install".to_string()),
|
let cwd = project.first_project_directory(cx);
|
||||||
full_label: upgrade_command.clone(),
|
let shell = project.terminal_settings(&cwd, cx).shell.clone();
|
||||||
label: upgrade_command.clone(),
|
let spawn_in_terminal = task::SpawnInTerminal {
|
||||||
command: Some(upgrade_command.clone()),
|
id: task::TaskId("upgrade".to_string()),
|
||||||
args: Vec::new(),
|
full_label: upgrade_command.clone(),
|
||||||
command_label: upgrade_command.clone(),
|
label: upgrade_command.clone(),
|
||||||
cwd,
|
command: Some(upgrade_command.clone()),
|
||||||
env: Default::default(),
|
args: Vec::new(),
|
||||||
use_new_terminal: true,
|
command_label: upgrade_command.clone(),
|
||||||
allow_concurrent_runs: true,
|
cwd,
|
||||||
reveal: Default::default(),
|
env: Default::default(),
|
||||||
reveal_target: Default::default(),
|
use_new_terminal: true,
|
||||||
hide: Default::default(),
|
allow_concurrent_runs: true,
|
||||||
shell,
|
reveal: Default::default(),
|
||||||
show_summary: true,
|
reveal_target: Default::default(),
|
||||||
show_command: true,
|
hide: Default::default(),
|
||||||
show_rerun: false,
|
shell,
|
||||||
};
|
show_summary: true,
|
||||||
workspace
|
show_command: true,
|
||||||
.spawn_in_terminal(spawn_in_terminal, window, cx)
|
show_rerun: false,
|
||||||
.detach();
|
};
|
||||||
|
workspace.spawn_in_terminal(spawn_in_terminal, window, cx)
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
let Some(task) = task else { return };
|
||||||
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
|
if let Some(Ok(_)) = task.await {
|
||||||
|
this.update_in(cx, |this, window, cx| {
|
||||||
|
this.reset(window, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.ok();
|
.detach()
|
||||||
}),
|
})),
|
||||||
));
|
);
|
||||||
|
} else if let LoadError::NotInstalled {
|
||||||
|
install_message,
|
||||||
|
install_command,
|
||||||
|
..
|
||||||
|
} = e
|
||||||
|
{
|
||||||
|
let install_message = install_message.clone();
|
||||||
|
let install_command = install_command.clone();
|
||||||
|
container = container.child(
|
||||||
|
Button::new("install", install_message)
|
||||||
|
.tooltip(Tooltip::text(install_command.clone()))
|
||||||
|
.on_click(cx.listener(move |this, _, window, cx| {
|
||||||
|
let task = this
|
||||||
|
.workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
let project = workspace.project().read(cx);
|
||||||
|
let cwd = project.first_project_directory(cx);
|
||||||
|
let shell = project.terminal_settings(&cwd, cx).shell.clone();
|
||||||
|
let spawn_in_terminal = task::SpawnInTerminal {
|
||||||
|
id: task::TaskId("install".to_string()),
|
||||||
|
full_label: install_command.clone(),
|
||||||
|
label: install_command.clone(),
|
||||||
|
command: Some(install_command.clone()),
|
||||||
|
args: Vec::new(),
|
||||||
|
command_label: install_command.clone(),
|
||||||
|
cwd,
|
||||||
|
env: Default::default(),
|
||||||
|
use_new_terminal: true,
|
||||||
|
allow_concurrent_runs: true,
|
||||||
|
reveal: Default::default(),
|
||||||
|
reveal_target: Default::default(),
|
||||||
|
hide: Default::default(),
|
||||||
|
shell,
|
||||||
|
show_summary: true,
|
||||||
|
show_command: true,
|
||||||
|
show_rerun: false,
|
||||||
|
};
|
||||||
|
workspace.spawn_in_terminal(spawn_in_terminal, window, cx)
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
let Some(task) = task else { return };
|
||||||
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
|
if let Some(Ok(_)) = task.await {
|
||||||
|
this.update_in(cx, |this, window, cx| {
|
||||||
|
this.reset(window, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach()
|
||||||
|
})),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
container.into_any()
|
container.into_any()
|
||||||
|
@ -3705,6 +3741,18 @@ impl AcpThreadView {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.thread_state = Self::initial_state(
|
||||||
|
self.agent.clone(),
|
||||||
|
None,
|
||||||
|
self.workspace.clone(),
|
||||||
|
self.project.clone(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Focusable for AcpThreadView {
|
impl Focusable for AcpThreadView {
|
||||||
|
@ -3743,12 +3791,6 @@ impl Render for AcpThreadView {
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.child(self.render_load_error(e, cx)),
|
.child(self.render_load_error(e, cx)),
|
||||||
ThreadState::ServerExited { status } => v_flex()
|
|
||||||
.p_2()
|
|
||||||
.flex_1()
|
|
||||||
.items_center()
|
|
||||||
.justify_center()
|
|
||||||
.child(self.render_server_exited(*status, cx)),
|
|
||||||
ThreadState::Ready { thread, .. } => {
|
ThreadState::Ready { thread, .. } => {
|
||||||
let thread_clone = thread.clone();
|
let thread_clone = thread.clone();
|
||||||
|
|
||||||
|
|
|
@ -1522,7 +1522,7 @@ impl AgentDiff {
|
||||||
self.update_reviewing_editors(workspace, window, cx);
|
self.update_reviewing_editors(workspace, window, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AcpThreadEvent::Stopped | AcpThreadEvent::Error | AcpThreadEvent::ServerExited(_) => {
|
AcpThreadEvent::Stopped | AcpThreadEvent::Error | AcpThreadEvent::LoadError(_) => {
|
||||||
self.update_reviewing_editors(workspace, window, cx);
|
self.update_reviewing_editors(workspace, window, cx);
|
||||||
}
|
}
|
||||||
AcpThreadEvent::TitleUpdated
|
AcpThreadEvent::TitleUpdated
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue