use std::collections::HashSet;
use super::{Doc, StepDetail, StepTab, ToolIo, records};
use crate::files_view::Preview;
use crate::theme;
const TOOL_ID_HINT: &str = "The provider's id for this call — it is what ties this \
input and output to the matching tool event in the response record.";
const ABSENT: &str = "(absent)";
pub(super) fn render_detail(
ui: &mut egui::Ui,
detail: &StepDetail,
tab: StepTab,
collapsed: &mut HashSet<String>,
raw: bool,
) {
ui.horizontal(|ui| {
ui.label("Records:").on_hover_text(
"The files behind this step. Pick one to read it in full — nothing \
here is summarized away. A `.log` seat appears when something was \
written to it.",
);
for (which, label, hint) in records::seats(Some(detail)) {
let text = egui::RichText::new(label);
ui.label(if which == tab { text.strong() } else { text })
.on_hover_text(hint);
}
});
match tab {
StepTab::Meta => render_doc(
ui,
&detail.meta,
&format!("{}/meta", detail.seq),
collapsed,
raw,
),
StepTab::Request => render_doc(
ui,
&detail.request,
&format!("{}/request", detail.seq),
collapsed,
raw,
),
StepTab::Staging => render_doc(
ui,
&detail.staging,
&format!("{}/staging", detail.seq),
collapsed,
raw,
),
StepTab::Response => render_response(ui, &detail.response, &detail.seq, collapsed, raw),
StepTab::Tools => render_tools(ui, &detail.tools, &detail.seq, collapsed, raw),
StepTab::Stderr => render_log(ui, detail.stderr.as_ref()),
StepTab::Driver => render_log(ui, detail.driver.as_ref()),
}
}
fn render_log(ui: &mut egui::Ui, log: Option<&Preview>) {
match log {
Some(preview) => crate::files_view::preview_body(ui, preview),
None => {
ui.weak(ABSENT);
}
}
}
fn render_doc(
ui: &mut egui::Ui,
doc: &Doc,
root: &str,
collapsed: &mut HashSet<String>,
raw: bool,
) {
if raw {
render_raw(ui, doc);
return;
}
match doc {
Doc::Json { value, .. } => crate::jsonview::render(ui, value, root, collapsed),
Doc::Absent => {
ui.weak(ABSENT);
}
Doc::Unparsed(bytes) => {
ui.colored_label(theme::ICHOR, super::UNPARSED);
ui.monospace(String::from_utf8_lossy(bytes));
}
}
}
fn render_raw(ui: &mut egui::Ui, doc: &Doc) {
let bytes = doc.raw();
if bytes.is_empty() {
ui.weak(ABSENT);
return;
}
ui.monospace(String::from_utf8_lossy(bytes));
}
fn render_response(
ui: &mut egui::Ui,
events: &[Doc],
seq: &str,
collapsed: &mut HashSet<String>,
raw: bool,
) {
if events.is_empty() {
ui.label("(no events)");
}
for (i, event) in events.iter().enumerate() {
render_doc(ui, event, &format!("{seq}/resp/{i}"), collapsed, raw);
}
}
fn render_tools(
ui: &mut egui::Ui,
tools: &[ToolIo],
seq: &str,
collapsed: &mut HashSet<String>,
raw: bool,
) {
if tools.is_empty() {
ui.label("(no tool calls)");
}
for tool in tools {
ui.horizontal(|ui| {
let (glyph, color, phrase) = theme::tool_result_badge(tool.is_error);
ui.colored_label(color, format!("{glyph} {phrase}"));
ui.label("call");
ui.monospace(&tool.tool_id).on_hover_text(TOOL_ID_HINT);
});
ui.label("input");
render_doc(
ui,
&tool.input,
&format!("{seq}/{}/in", tool.tool_id),
collapsed,
raw,
);
ui.label("output");
render_doc(
ui,
&tool.output,
&format!("{seq}/{}/out", tool.tool_id),
collapsed,
raw,
);
}
}