use crate::AppModel;
use crate::actions::{
DraftKey, message_enabled, new_prompt_enabled, stop_children_offered, stop_enabled,
};
use crate::boundary::line;
use crate::cli_outbound::Cli;
use crate::git_tree::Agent;
use std::path::PathBuf;
use super::ShellState;
const STOP_HINT: &str = "Kill the driver running this conversation (`lernie stop`). Everything it \
has already committed is kept; you can message it again afterwards (x).";
pub(super) struct VerbCtx {
pub ws: PathBuf,
pub agents: Vec<Agent>,
pub key: DraftKey,
pub text: String,
pub conv_name: Option<String>,
pub entered: bool,
}
pub(super) fn verb_buttons(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
lernie: &Cli,
bl: &Cli,
ctx: &VerbCtx,
) {
if super::slash::seat(ui, model, state, lernie, bl, ctx) {
return;
}
let selected = state.actions.selected_branch.clone();
let said = line::unescape(&ctx.text);
super::row::peers(ui, |ui| {
if let Some(name) = ctx.conv_name.as_deref() {
ui.weak(format!("→ message {name}"));
}
if let Some(agent) = selected.as_deref() {
let msg_on = message_enabled(Some(agent), &ctx.text, &ctx.agents);
let send = ui
.add_enabled(msg_on, egui::Button::new("Message"))
.on_hover_text(
"Deposit this text in the selected conversation's inbox and wake its \
driver so it reads it (`lernie message`). Enter sends it; typed \
whole, it is `/message <text…>`.",
)
.on_disabled_hover_text("type something first — an empty message sends nothing")
.clicked();
if msg_on && (send || ctx.entered) {
super::focus::request(state);
let cleared = super::dispatch::message(model, lernie, bl, &ctx.ws, agent, &said);
if cleared {
state.actions.drafts.set(ctx.key.clone(), String::new());
}
}
hold_controls(ui, model, lernie, bl, ctx, agent);
stop_controls(ui, model, state, lernie, bl, &ctx.agents);
} else {
let on = new_prompt_enabled(&ctx.text, &state.actions.path_dir);
let send = ui
.add_enabled(on, egui::Button::new("New prompt"))
.on_hover_text(
"Start a new conversation in this workspace from this text — a \
detached `lernie prompt` that keeps running whatever yog does. \
Enter starts it; typed whole, it is `/prompt <goal…>`.",
)
.on_disabled_hover_text("type something first — an empty prompt starts nothing")
.clicked();
if on && (send || ctx.entered) {
super::focus::request(state);
if super::fire::fire_start(model, state, lernie, bl, &said) {
state.actions.drafts.set(ctx.key.clone(), String::new());
}
}
}
});
}
fn hold_controls(
ui: &mut egui::Ui,
model: &mut AppModel,
lernie: &Cli,
bl: &Cli,
ctx: &VerbCtx,
agent: &str,
) {
use crate::control::judge::Ruling;
let Some(held) = ctx
.agents
.iter()
.find(|a| a.agent_id == agent)
.and_then(|a| a.held.as_ref())
else {
return;
};
ui.weak("⏸ held").on_hover_text(held.reason.clone());
if ui
.button("Approve")
.on_hover_text(
"Let this one parked tool call through and drive the conversation on. \
The approval is scoped to this exact call — the next one is judged afresh. \
Typed, it is `/answer pass`.",
)
.clicked()
{
super::dispatch::answer_hold(model, lernie, bl, &ctx.ws, agent, Ruling::Pass);
}
if ui
.button("Decline")
.on_hover_text(
"Refuse this one parked tool call and drive the conversation on. The model \
is told why and carries on from there — nothing is stopped or killed. \
Typed, it is `/answer refuse`.",
)
.clicked()
{
super::dispatch::answer_hold(model, lernie, bl, &ctx.ws, agent, Ruling::Refuse);
}
}
fn stop_controls(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
lernie: &Cli,
bl: &Cli,
agents: &[Agent],
) {
let Some(agent) = state.actions.selected_branch.clone() else {
return;
};
if stop_children_offered(&agent, agents) {
ui.checkbox(&mut state.actions.stop_children, "children")
.on_hover_text(
"Stop the agents this conversation spawned too, not only the one at \
its root. Typed, it is `/stop children`.",
);
}
let stop_on = stop_enabled(Some(&agent), agents);
if ui
.add_enabled(stop_on, egui::Button::new("Stop"))
.on_hover_text(STOP_HINT)
.on_disabled_hover_text(
"nothing is running on this conversation — there is no driver to kill",
)
.clicked()
{
super::dispatch::stop_selected(model, state, lernie, bl);
}
}