use super::{
CenterTab, Cli, ModelRow, ShellState, birth_scope, conversation_scope, lines, marks, pane,
refresh, roster_fault, select, settled, write,
};
use crate::AppModel;
use crate::model_pick::{
ConfigTip, NEW_CONVERSATION_EXIT, Pick, RETARGET_EXIT, RETARGET_HOVER, row_role,
};
use std::path::Path;
struct Subject {
scope: String,
agent: Option<String>,
}
enum Exit {
None,
NewConversation,
Retarget,
}
pub(crate) fn conversation_seat(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
ws: &Path,
agent_id: &str,
config_tip: Option<&ConfigTip>,
bz: &Cli,
) -> bool {
let Some(tip) = crate::shell::seat::detail(model, ws, agent_id)
.value
.map(|view| view.tip)
else {
return false;
};
let Some((frozen_oid, row)) =
lines::conversation_row_of(ws, &tip, config_tip, &mut state.wall.picker)
else {
return false;
};
let subject = Subject {
scope: conversation_scope(ws, &frozen_oid),
agent: Some(agent_id.to_owned()),
};
seat(ui, model, state, ws, &row, &subject, bz)
}
pub(crate) fn birth_seat(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
ws: &Path,
config_tip: Option<&ConfigTip>,
bz: &Cli,
) -> bool {
let Some(row) = lines::birth_row_of(ws, config_tip, &mut state.wall.picker) else {
return false;
};
let subject = Subject {
scope: birth_scope(ws),
agent: None,
};
seat(ui, model, state, ws, &row, &subject, bz);
true
}
fn seat(
ui: &mut egui::Ui,
model: &mut AppModel,
state: &mut ShellState,
ws: &Path,
row: &ModelRow,
subject: &Subject,
bz: &Cli,
) -> bool {
let role = row_role(state.wall.picker.role.as_deref());
let rows = marks::provider_rows(&mut state.wall.picker);
let view = settled(&mut state.wall.picker);
let models = view.as_ref().map(|v| v.models.clone()).unwrap_or_default();
let choice = select::pair_row(
ui,
&mut state.wall.picker,
row,
&rows,
&models,
view.is_none(),
);
if choice.list_open {
refresh(&mut state.wall.picker, &choice.provider, bz);
}
let mut route = choice.add_provider.then_some(CenterTab::Config);
if let Some(view) = &view {
route = route.or(roster_fault(
ui,
view,
rows.iter().find(|r| r.name == choice.provider),
));
}
if let Some(chosen) = choice.chosen {
let pick = Pick {
role: role.clone(),
provider: choice.provider,
model: chosen,
};
write::apply(&mut state.wall.picker, model, ws, &pick);
}
write::settle(&mut state.wall.picker, model);
if !state.wall.picker.act.quiet() {
ui.label(state.wall.picker.act.line());
}
let exit = drift_exit(ui, row);
if let (Exit::Retarget, Some(agent)) = (&exit, subject.agent.as_deref()) {
write::retarget(&mut state.wall.picker, model, ws, agent);
}
route = route.or(pane(ui, &mut state.wall.picker, ws, &subject.scope, &role));
if let Some(tab) = route {
state.wall.picker.toggle();
crate::shell::center::focus(model, state, tab);
}
matches!(exit, Exit::NewConversation)
}
fn drift_exit(ui: &mut egui::Ui, row: &ModelRow) -> Exit {
let Some(clause) = &row.drift else {
return Exit::None;
};
crate::shell::row::peers(ui, |ui| {
ui.weak(clause).on_hover_text(&row.hover);
if ui
.button(RETARGET_EXIT)
.on_hover_text(RETARGET_HOVER)
.clicked()
{
return Exit::Retarget;
}
if ui
.button(NEW_CONVERSATION_EXIT)
.on_hover_text(
"Clear the selection and put the cursor in the composer, so the \
next thing you send starts a conversation on the config this \
workspace has now — leaving this one where it is (n).",
)
.clicked()
{
return Exit::NewConversation;
}
Exit::None
})
}