use super::ShellState;
use super::start_rows::new_ball_form;
use crate::AppModel;
use crate::board::Column;
mod rows;
use crate::boundary::Query;
use crate::boundary::reply::Reply;
use crate::cli_outbound::Cli;
use rows::{Affordances, board_row};
pub fn board(ui: &mut egui::Ui, model: &mut AppModel, state: &mut ShellState, lernie: &Cli) {
if let Some(failure) = model.last_failure(crate::opslog::Origin::Balls) {
super::banner::failure_banner(ui, model, state, &failure);
}
if let Some(hint) = model.empty_project_hint() {
ui.weak(hint.lead);
ui.add(
egui::Label::new(egui::RichText::new(hint.command).monospace())
.wrap()
.selectable(true),
)
.on_hover_text(
"The command that registers a project with yog — selectable, so you \
can copy it out and run it in a terminal. No key of its own: Tab reaches \
it, then copy.",
);
}
columns(ui, model, state, lernie);
ui.separator();
let projects = model.project_paths();
for (project, label) in projects.iter().zip(crate::projects::labels(&projects)) {
new_ball_form(ui, model, state, project, &label);
}
}
fn columns(ui: &mut egui::Ui, model: &mut AppModel, state: &mut ShellState, lernie: &Cli) {
let landed = super::wire::ask(model, Query::Board, |reply| match reply {
Reply::Board(board) => Some(board),
_ => None,
});
if let Some(said) = &landed.refused {
ui.colored_label(crate::theme::ICHOR, said);
}
let board = landed.value.unwrap_or_default();
for fleet in &board.fleet {
ui.horizontal(|ui| {
ui.label(egui::RichText::new("fleet").strong());
ui.weak(fleet.label());
})
.response
.on_hover_text(format!(
"An armed loop on this workspace, taking ready balls from {}. \
It claims and starts; it releases a claim whose conversations have \
gone quiet past the lease; it never stops anything that is running. \
Every spawn and reap is a row on the ops trail.",
fleet.project.display()
));
if let Some(refusal) = &fleet.ceiling {
ui.colored_label(crate::theme::ICHOR, refusal);
}
}
let affordances = Affordances::of(model);
for column in Column::ALL {
let rows = board.in_column(column);
if rows.is_empty() {
continue;
}
egui::CollapsingHeader::new(format!("{} ({})", column.word(), rows.len()))
.id_salt(column.word())
.default_open(true)
.show(ui, |ui| {
for row in rows {
board_row(ui, model, state, lernie, &row, &affordances);
}
})
.header_response
.on_hover_text(column_hint(column));
}
}
fn column_hint(column: Column) -> &'static str {
match column {
Column::Ready => "claimable now: unclaimed, with every dependency resolved.",
Column::Gated => {
"claimable, but not deliverable: a close-gate is still open. You can \
start the work; the ball cannot close until the ball named on the row \
closes first."
}
Column::Claimed => {
"someone holds it. The rows beneath name the conversations working \
it and what they have spent."
}
Column::Blocked => "not claimable yet: a dependency is still open.",
}
}