use super::StepSummary;
use super::render::summary_badge;
const SHORT_OID: usize = 7;
pub(super) enum Cell {
Colored(egui::Color32, String),
Mono(String),
Plain(String),
Weak(String),
Empty,
}
pub(super) struct Column {
pub(super) header: &'static str,
pub(super) hint: &'static str,
pub(super) cell: fn(&StepSummary, bool) -> Cell,
}
pub(super) const COLUMNS: &[Column] = &[
Column {
header: "Outcome",
hint: "How this step ended, read back from its own response bytes: \
complete, failed, no clean end — a kill, a crash and a call still \
in flight leave the same trace on disk, so the row claims only \
that it never ended cleanly — or no response at all, which means \
the step produced nothing and nobody is driving the agent.",
cell: |step, _| {
let (glyph, color, phrase) = summary_badge(step);
Cell::Colored(color, format!("{glyph} {phrase}"))
},
},
Column {
header: "Step",
hint: "Where this step falls in the agent's run — 001 is the first \
prompt sent, and each later number is one more turn. ▶ marks the \
step whose records are opened below.",
cell: |step, selected| {
let marker = if selected { "▶" } else { " " };
Cell::Mono(format!("{marker} {}", step.seq))
},
},
Column {
header: "Attempts",
hint: "How many tries finished inside this one step. More than one \
means the model was asked again before the step ended.",
cell: |step, _| Cell::Plain(step.attempts.to_string()),
},
Column {
header: "Tokens",
hint: "Tokens this step billed — its input, output and both cache \
counters summed, the same figure the budget line spends against \
the conversation's ceiling.",
cell: |step, _| Cell::Plain(step.tokens.total_tokens().to_string()),
},
Column {
header: "Commit",
hint: "The agent branch tip when the step started, shortened — the code \
and history this step actually saw. Empty when the step wrote no \
record of itself.",
cell: |step, _| {
step.commit
.as_deref()
.map_or(Cell::Empty, |commit| Cell::Mono(short(commit)))
},
},
Column {
header: "Started",
hint: "When the step began, as the step itself recorded it. Empty when \
that record is missing or unreadable.",
cell: |step, _| step.started_at.clone().map_or(Cell::Empty, Cell::Weak),
},
Column {
header: "Ended",
hint: "When the step finished. Empty while it is still running — and \
also when it died before it could write the time down, which is \
why the Outcome column, not this one, says whether it ended well.",
cell: |step, _| step.ended_at.clone().map_or(Cell::Empty, Cell::Weak),
},
];
fn short(oid: &str) -> String {
oid.chars().take(SHORT_OID).collect()
}