use super::{ShellState, StartState};
use crate::AppModel;
use crate::cli_outbound::Cli;
use crate::names::SplitMix64;
use crate::start::{self, Payload, StartInputs};
use std::path::PathBuf;
pub(super) fn run_prepare(
model: &mut AppModel,
state: &mut ShellState,
lernie: &Cli,
bl: &Cli,
inputs: StartInputs,
) {
let project = payload_project(&inputs.payload);
let ts = super::now_ts();
let result = model.prepare_start(lernie, bl, &inputs.workspace, &inputs.payload, &ts);
if let Ok(prepared) = result {
if crate::actions::goal_present(&prepared.goal) {
state.start.pending = Some(prepared);
} else {
super::focus::request(state);
}
}
match project {
Some(p) => model.after_bl_verb(&p),
None => model.after_lernie_verb(),
}
}
fn payload_project(payload: &Payload) -> Option<PathBuf> {
match payload {
Payload::Ball { project, .. } => Some(project.clone()),
Payload::Bare | Payload::Path { .. } => None,
}
}
pub fn composer(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
lernie: &Cli,
bl: &Cli,
) {
let mint_seed = state.start.mint_seed;
let Some(pending) = state.start.pending.as_mut() else {
return;
};
let (mut send, mut cancel) = (false, false);
ui.weak(start::identity_preview(
&model.conversation_names(&pending.workspace),
&mut SplitMix64::from_seed(mint_seed),
));
ui.label(format!("Start goal → {}", pending.workspace.display()));
let row = ui.spacing().interact_size.y + 2.0 * ui.spacing().item_spacing.y;
let box_height = (ui.available_height() - row).max(row);
egui::ScrollArea::vertical()
.max_height(box_height)
.show(ui, |ui| {
ui.add_sized(
[ui.available_width(), box_height],
egui::TextEdit::multiline(&mut pending.goal),
)
.on_hover_text(
"The goal this conversation starts with — the agent's first instruction. \
Edit it freely; it is only read when you press Send — or Enter, which \
is the same fire. Typed whole, it is `/prompt <goal…>`.",
);
});
let armed = crate::actions::goal_present(&pending.goal);
ui.horizontal(|ui| {
send = ui
.add_enabled(armed, egui::Button::new("Send (detached prompt)"))
.on_hover_text(
"Launch the conversation: `lernie prompt`, detached, in the workspace \
named above. It keeps running whatever yog does afterwards (Enter).",
)
.on_disabled_hover_text(
"The goal above is empty — say what you want done before this can \
launch anything.",
)
.clicked();
cancel = ui
.button("Cancel")
.on_hover_text(
"Drop this goal without launching anything. A ball claimed on the way \
here stays claimed. Escape does the same.",
)
.clicked();
});
if send && send_pending(model, &mut state.start, lernie, bl) {
super::focus::request(state);
}
if cancel {
state.start.pending = None;
super::focus::request(state);
}
}
pub(super) fn send_pending(
model: &mut AppModel,
start: &mut StartState,
lernie: &Cli,
bl: &Cli,
) -> bool {
let Some(p) = start
.pending
.take_if(|p| crate::actions::goal_present(&p.goal))
else {
return false;
};
let fired = model.fire_prompt(lernie, bl, &p, &p.goal, start.mint_seed, &super::now_ts());
model.after_lernie_verb();
let ok = fired.is_ok();
if ok {
start.spend_mint();
} else {
start.pending = Some(p);
}
ok
}