use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
prelude::Widget,
style::{Color, Style},
text::{Line, Span},
widgets::Paragraph,
Frame,
};
use super::{
ChatDagPanel, ChatNodeKind, ChatNodeState, ChatTaskQueue, ChatTaskQueueItem, ChatTaskState,
ChatTaskVerb, ChatView, DagEdgeData, DagNodeData, WorkflowRole,
};
use crate::tui::Theme;
impl ChatView {
pub(super) fn render_dag_panel(&mut self, frame: &mut Frame, area: Rect, _theme: &Theme) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(60), Constraint::Percentage(40)])
.split(area);
let mut dag_panel = ChatDagPanel::new().with_title("CHAT DAG");
for node in &self.dag_nodes {
dag_panel.add_node(node.clone());
}
for edge in &self.dag_edges {
dag_panel.add_edge(edge.clone());
}
if let Some(idx) = self.dag_selected {
if let Some(node) = self.dag_nodes.get(idx) {
dag_panel.select(&node.id);
}
}
dag_panel.set_animation_tick(self.frame);
dag_panel.render(chunks[0], frame.buffer_mut());
let mut task_queue = ChatTaskQueue::new().with_title("TASK QUEUE");
for item in &self.task_queue {
task_queue.add(item.clone());
}
task_queue.render(chunks[1], frame.buffer_mut());
}
pub fn sync_dag_from_messages(&mut self) {
self.dag_nodes.clear();
self.dag_edges.clear();
let messages = self.workflow.all_messages();
for (i, (idx, msg)) in messages.iter().enumerate() {
let kind = match msg.role {
WorkflowRole::User => ChatNodeKind::User,
WorkflowRole::Assistant => ChatNodeKind::Assistant,
WorkflowRole::System => ChatNodeKind::System,
WorkflowRole::Tool => ChatNodeKind::ToolCall,
};
let label = if msg.content.chars().count() > 30 {
let truncated: String = msg.content.chars().take(27).collect();
format!("{}...", truncated)
} else {
msg.content.clone()
};
let node = DagNodeData::new(&msg.id, kind, i as u32)
.with_label(&label)
.with_state(ChatNodeState::Complete);
self.dag_nodes.push(node);
for dep_idx in self.workflow.get_dependencies(*idx) {
if let Some(dep_msg) = self.workflow.get_message_by_index(dep_idx) {
self.dag_edges.push(DagEdgeData::new(&dep_msg.id, &msg.id));
}
}
}
}
pub fn add_task_to_queue(&mut self, id: &str, verb: ChatTaskVerb) {
let item = ChatTaskQueueItem::new(id, verb);
self.task_queue.push(item);
}
pub fn update_task_state(
&mut self,
id: &str,
state: ChatTaskState,
elapsed: Option<std::time::Duration>,
) {
if let Some(task) = self.task_queue.iter_mut().find(|t| t.id() == id) {
task.set_state(state);
if let Some(e) = elapsed {
task.set_elapsed(e);
}
}
}
pub(super) fn complete_last_running_task(&mut self, verb: ChatTaskVerb, elapsed_ms: u64) {
if let Some(task) = self
.task_queue
.iter_mut()
.rev()
.find(|t| t.verb() == verb && t.state() == ChatTaskState::Running)
{
task.set_state(ChatTaskState::Complete);
task.set_elapsed(std::time::Duration::from_millis(elapsed_ms));
task.set_progress(1.0); }
}
pub(super) fn fail_last_running_task(&mut self, verb: ChatTaskVerb, elapsed_ms: u64) {
if let Some(task) = self
.task_queue
.iter_mut()
.rev()
.find(|t| t.verb() == verb && t.state() == ChatTaskState::Running)
{
task.set_state(ChatTaskState::Failed);
task.set_elapsed(std::time::Duration::from_millis(elapsed_ms));
task.set_progress(1.0); }
}
pub fn toggle_dag_panel(&mut self) {
self.show_dag_panel = !self.show_dag_panel;
if self.show_dag_panel {
self.sync_dag_from_messages();
}
}
pub fn cycle_task_box_render_mode(&mut self) {
self.task_box_render_mode = self.task_box_render_mode.cycle();
}
pub(super) fn render_hints(&self, frame: &mut Frame, area: Rect, theme: &Theme) {
let hints = Line::from(vec![
Span::styled(
" ⌘K ",
Style::default().fg(Color::Black).bg(theme.highlight),
),
Span::raw(" commands "),
Span::styled(
" ⌘P ",
Style::default().fg(Color::Black).bg(theme.highlight),
),
Span::raw(" model "),
Span::styled(
" ⇧P ",
Style::default().fg(Color::Black).bg(theme.highlight),
),
Span::raw(" providers "),
Span::styled(
" 1234 ",
Style::default().fg(Color::Black).bg(theme.highlight),
),
Span::raw(" views "),
Span::styled(
" Esc ",
Style::default().fg(Color::Black).bg(theme.highlight),
),
Span::raw(" close"),
]);
let paragraph = Paragraph::new(hints);
frame.render_widget(paragraph, area);
}
}