use crate::AppModel;
use crate::boundary::Action;
use crate::cli_outbound::Cli;
use crate::delete::agent::{AgentConfirmation, Census};
use crate::theme;
use crate::wire::post::Ticket;
use std::path::{Path, PathBuf};
use super::ShellState;
#[derive(Default)]
pub struct DeleteAgentState {
pub target: Option<(PathBuf, String)>,
pub census: Option<Census>,
pub typed: String,
pub error: String,
pub ticket: Option<Ticket>,
}
pub(super) fn open(state: &mut ShellState, lernie: &Cli, ws: &Path, root: &str) {
let (census, error) = match crate::delete::agent::census(lernie, ws, root) {
Ok(census) => (Some(census), String::new()),
Err(e) => (None, e),
};
state.delete_agent = DeleteAgentState {
target: Some((ws.to_path_buf(), root.to_owned())),
census,
error,
..DeleteAgentState::default()
};
}
pub(super) fn danger_row(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
lernie: &Cli,
ws: &Path,
) {
let Some(root) = super::seat::selection(model).map(|seat| seat.root) else {
return;
};
let name = model.snap.ws_name(ws);
if !crate::nav::tabs::is_named(&super::chrome::ws_rows(model), &name) {
return;
}
ui.separator();
if ui
.button(egui::RichText::new("delete this conversation…").color(theme::ICHOR))
.on_hover_text(
"removes the agent, its children and their pending inbox — irrecoverable. \
Typed, it is `/delete-agent [typed name]`.",
)
.clicked()
{
open(state, lernie, ws, &root);
}
}
pub(super) fn dialog(ctx: &egui::Context, model: &mut AppModel, state: &mut ShellState) {
if state.delete_agent.target.is_none() {
return;
}
settle(model, state);
window(ctx, model, state);
if state.delete_agent.target.is_none() {
super::focus::request(state);
}
}
fn window(ctx: &egui::Context, model: &mut AppModel, state: &mut ShellState) {
let Some((ws, root)) = state.delete_agent.target.clone() else {
return;
};
let name = model.snap.ws_name(&ws);
if !crate::nav::tabs::is_named(&super::chrome::ws_rows(model), &name) {
state.delete_agent = DeleteAgentState::default();
return;
}
let rows = super::convs::of(model, name).value.unwrap_or_default();
let confirm = crate::delete::agent::confirmation_of_rows(&rows, &root);
let mut shown = true;
egui::Window::new(format!("delete conversation {}", confirm.name))
.collapsible(false)
.resizable(false)
.open(&mut shown)
.show(ctx, |ui| body(ui, model, state, &confirm));
if !shown {
state.delete_agent = DeleteAgentState::default();
}
}
fn body(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
confirm: &AgentConfirmation,
) {
ui.colored_label(
theme::ICHOR,
"this destroys the conversation — transcript, steps, worktree, marks — irrecoverably",
);
let Some(census) = state.delete_agent.census.clone() else {
ui.colored_label(theme::ICHOR, &state.delete_agent.error);
return;
};
ui.label("children that die with it:");
if census.descendants.is_empty() {
ui.weak("(none)");
}
for id in &census.descendants {
ui.weak(id);
}
ui.label(format!(
"pending inbox deposits that die: {}",
census.pending_deposits
));
ui.separator();
if confirm.refused() {
ui.colored_label(
theme::ICHOR,
format!("live: {} — stop them first", confirm.live.join(", ")),
);
return;
}
let confirmed = if census.descendants.is_empty() {
true
} else {
ui.horizontal(|ui| {
ui.label(format!("type “{}” to confirm:", confirm.name));
ui.text_edit_singleline(&mut state.delete_agent.typed)
.on_hover_text(
"Retype the conversation's name exactly — deleting its children \
too is confirmed by the name, not a checkbox. It is the name \
`/delete-agent [typed name]` carries.",
);
});
confirm.subtree_armed(&state.delete_agent.typed)
};
let armed = confirmed && state.delete_agent.ticket.is_none();
if ui
.add_enabled(armed, egui::Button::new("Delete conversation"))
.on_hover_text(
"Remove this conversation from the workspace, permanently. Typed, it is \
`/delete-agent [typed name]`.",
)
.on_disabled_hover_text("Type the conversation's name above to enable this.")
.clicked()
{
fire(model, state);
}
if state.delete_agent.ticket.is_some() {
ui.weak("removing the conversation …");
}
if !state.delete_agent.error.is_empty() {
ui.colored_label(theme::ICHOR, &state.delete_agent.error);
}
}
fn fire(model: &mut AppModel, state: &mut ShellState) {
let Some((ws, root)) = state.delete_agent.target.clone() else {
return;
};
let action = Action::DeleteAgent {
workspace: model.snap.ws_name(&ws),
agent: root,
typed: state.delete_agent.typed.clone(),
};
state.delete_agent.ticket = Some(model.post_act(&action));
}
fn settle(model: &mut AppModel, state: &mut ShellState) {
let (Some(ticket), Some((ws, root))) =
(state.delete_agent.ticket, state.delete_agent.target.clone())
else {
return;
};
let Some(landed) = model.act_receipt(ticket) else {
return;
};
state.delete_agent.ticket = None;
model.deleted_agent(&ws, &root);
match super::act::trouble(&landed) {
Some(reason) => state.delete_agent.error = reason,
None => state.delete_agent = DeleteAgentState::default(),
}
}