use crate::AppModel;
use crate::boundary::Query;
use crate::boundary::reply::Reply;
use crate::opslog::{OPS_TAIL, OpRow};
use crate::theme;
pub fn accessory(ui: &mut egui::Ui, model: &mut AppModel, open: &mut bool) {
let chrome = super::chrome::workspaces(model).value.unwrap_or_default();
for note in [chrome.stale, chrome.growth].into_iter().flatten() {
ui.colored_label(theme::ICHOR, note);
}
let trail = super::wire::ask(model, Query::Ops { max: OPS_TAIL }, |reply| match reply {
Reply::Ops(rows) => Some(rows),
_ => None,
});
let rows: Vec<OpRow> = trail.value.unwrap_or_default();
let refused = trail.refused;
let summary = crate::opslog::activity(&rows);
let heading = if summary.alarming() {
egui::RichText::new(summary.chip()).color(theme::ICHOR)
} else {
egui::RichText::new(summary.chip()).weak()
};
let header = egui::CollapsingHeader::new(heading)
.id_salt("activity-accessory")
.open(Some(*open))
.show(ui, |ui| {
trail_controls(ui, model, summary);
crate::tail::scroll(ui, true, |ui| {
if let Some(said) = &refused {
ui.colored_label(theme::ICHOR, said);
}
let outcomes = crate::opslog::outcomes(&rows);
for (i, (row, outcome)) in rows.iter().zip(outcomes).enumerate() {
ui.push_id(i, |ui| ops_row(ui, row, outcome));
}
});
})
.header_response
.on_hover_text(
"Open the ops trail: every command yog has run — lernie, bl, bz — with \
its exit code and everything it printed. This is where a failure's \
reason lives (a).",
);
if header.clicked() {
*open = !*open;
}
}
fn trail_controls(ui: &mut egui::Ui, model: &mut AppModel, summary: crate::opslog::Activity) {
use crate::opslog::operator;
ui.horizontal(|ui| {
if summary.alarming()
&& ui
.small_button(operator::ACK_LABEL)
.on_hover_text(operator::ACK_HOVER)
.clicked()
{
model.ack_failures();
}
if ui
.small_button(operator::CLEAR_LABEL)
.on_hover_text(operator::CLEAR_HOVER)
.clicked()
{
model.clear_trail();
}
});
}
fn ops_row(ui: &mut egui::Ui, row: &crate::opslog::OpRow, outcome: crate::opslog::OpOutcome) {
let (glyph, hue, phrase) = theme::op_badge(outcome);
let more = if row.has_output() { " ⋯" } else { "" };
let summary = format!("{glyph} {} {}{more}", row.when(), row.summary());
let heading = egui::RichText::new(summary).color(hue);
ui.collapsing(heading, |ui| {
ui.label(format!("cwd: {}", row.cwd));
ui.label(format!("argv: {}", row.argv));
ui.label(row.exit_label());
if !row.stdout.is_empty() {
ui.label("stdout:");
ui.code(&row.stdout);
}
if !row.stderr.is_empty() {
ui.colored_label(hue, "stderr:");
ui.code(&row.stderr);
}
})
.header_response
.on_hover_text(format!(
"{phrase} — open the row for the command's directory, its whole argv, how it \
exited and everything it printed. No key of its own: Tab reaches it, Space \
presses it."
));
}