use super::{Block, Entry, EntryKind, Transcript};
use crate::theme;
pub fn render(ui: &mut egui::Ui, transcript: &Transcript, raw: bool) {
egui::ScrollArea::vertical().show(ui, |ui| {
if transcript.entries.is_empty() {
ui.label("(no messages yet)");
return;
}
for entry in &transcript.entries {
if raw {
render_raw(ui, entry);
} else {
render_entry(ui, transcript, entry);
}
}
});
}
fn render_raw(ui: &mut egui::Ui, entry: &Entry) {
ui.monospace(&entry.name);
ui.monospace(String::from_utf8_lossy(&entry.raw).to_string());
}
fn render_entry(ui: &mut egui::Ui, transcript: &Transcript, entry: &Entry) {
match &entry.kind {
EntryKind::Delivered { sender, body } => {
ui.strong(format!("{sender}:"));
ui.label(body);
}
EntryKind::Model { model_id, blocks } => {
ui.weak(model_id);
for block in blocks {
render_block(ui, transcript, block);
}
}
EntryKind::ToolResult {
tool_use_id,
content,
is_error,
} => render_tool_result(ui, tool_use_id, content, *is_error),
EntryKind::Streaming { text } => render_streaming(ui, text),
EntryKind::Raw => render_raw(ui, entry),
}
}
fn render_block(ui: &mut egui::Ui, transcript: &Transcript, block: &Block) {
match block {
Block::Text(text) => {
ui.label(text);
}
Block::Thinking(text) => {
ui.weak(format!("thinking: {text}"));
}
Block::ToolUse {
id,
name,
input_summary,
} => render_tool_use(ui, transcript.tool_in_progress(id), id, name, input_summary),
}
}
fn render_tool_use(ui: &mut egui::Ui, in_progress: bool, id: &str, name: &str, input: &str) {
ui.horizontal(|ui| {
ui.monospace("⚙");
let chip = format!("{name}({input})");
if in_progress {
let time = ui.ctx().input(|i| i.time);
ui.colored_label(theme::pulse(time), chip);
ui.label("(in-flight)");
ui.ctx().request_repaint_after(theme::PULSE_REPAINT_DELAY);
} else {
ui.label(chip);
}
ui.weak(id);
});
}
fn render_tool_result(ui: &mut egui::Ui, tool_use_id: &str, content: &str, is_error: bool) {
ui.horizontal(|ui| {
let (glyph, color) = if is_error {
("✗", theme::ICHOR)
} else {
("✓", theme::HYDRA)
};
ui.colored_label(color, glyph);
ui.weak(tool_use_id);
});
ui.monospace(content);
}
fn render_streaming(ui: &mut egui::Ui, text: &str) {
ui.colored_label(theme::SPECTRE, "live");
ui.label(text);
}