use crate::reply::convs::Tone;
use crate::reply::step::{Doc, Step, ToolCall};
use crate::ui::{Model, theme};
pub const OPEN: &str = "records…";
pub const CLOSE: &str = "hide";
pub const NO_BYTES: &str = "no bytes — missing, unreadable, or empty";
pub const NO_EVENTS: &str = "its response stream landed no event";
pub const NO_TOOLS: &str = "it called no tool";
pub const ERRORED: &str = "ended in error";
pub fn control(ui: &mut egui::Ui, model: &mut Model, seq: &str) {
let open = model.drilled_into(seq).is_some();
let word = if open { CLOSE } else { OPEN };
let control = ui.button(word);
crate::ui::act::tag(&control, &[crate::verbs::STEP.word]);
if control.clicked() {
if open {
model.records.drilled = None;
} else {
model.ask_step(seq);
}
}
}
pub fn records(ui: &mut egui::Ui, model: &Model, seq: &str) {
if let Some(answer) = model.drilled_into(seq) {
drilled(ui, &answer);
}
}
fn drilled(ui: &mut egui::Ui, records: &Step) {
for (head, doc) in [
("meta", &records.meta),
("request", &records.request),
("staging", &records.staging),
] {
record(ui, head, doc);
}
ui.colored_label(theme::tone_ink(&Tone::Weak), "response");
if records.response.is_empty() {
ui.label(NO_EVENTS);
}
for (index, event) in records.response.iter().enumerate() {
record(ui, &format!("event {index}"), event);
}
if records.tools.is_empty() {
ui.colored_label(theme::tone_ink(&Tone::Weak), NO_TOOLS);
}
for call in &records.tools {
tool(ui, call);
}
for (head, log) in [("stderr", &records.stderr), ("driver", &records.driver)] {
if let Some(preview) = log {
ui.colored_label(theme::tone_ink(&Tone::Weak), head.to_owned());
ui.label(egui::RichText::new(super::previewed(preview)).monospace());
}
}
}
fn tool(ui: &mut egui::Ui, call: &ToolCall) {
ui.colored_label(theme::tone_ink(&Tone::Weak), headline(call));
record(ui, "input", &call.input);
record(ui, "output", &call.output);
}
fn record(ui: &mut egui::Ui, head: &str, doc: &Doc) {
ui.colored_label(theme::tone_ink(&Tone::Weak), head.to_owned());
match doc {
Doc::Absent => {
ui.label(NO_BYTES);
}
other => {
if let Some(said) = framing(other) {
ui.colored_label(theme::NOTICE, said);
}
ui.label(egui::RichText::new(bytes(other)).monospace());
}
}
}
pub fn headline(call: &ToolCall) -> String {
if call.is_error {
return format!("{} — {ERRORED}", call.tool_id);
}
call.tool_id.clone()
}
pub fn framing(doc: &Doc) -> Option<String> {
match doc {
Doc::Unparsed { note, .. } => Some(note.clone()),
Doc::Unknown(word) => Some(format!("a {word:?} record, which this seat cannot show")),
Doc::Json { .. } | Doc::Absent => None,
}
}
pub fn bytes(doc: &Doc) -> String {
match doc {
Doc::Json { raw } | Doc::Unparsed { raw, .. } => raw.clone(),
Doc::Absent | Doc::Unknown(_) => String::new(),
}
}
#[cfg(test)]
mod tests;