use crate::app::TuiApp;
use crate::types::Activity;
use crate::workflow::{DagWidget, render_progress_view};
use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Paragraph},
};
fn status_to_color(status_color: &str) -> Color {
match status_color {
"gray" => Color::Gray,
"yellow" => Color::Yellow,
"green" => Color::Green,
"red" => Color::Red,
"blue" => Color::Blue,
_ => Color::Reset,
}
}
impl TuiApp {
pub(crate) fn draw_workflow_panel(&self, f: &mut ratatui::Frame) {
if !self.workflow_state.visible {
return;
}
let area = f.area();
let panel_width = 40u16.min(area.width / 3);
let status_height: u16 = 1;
let hint_height: u16 = if matches!(
self.approve_mode,
crate::types::ApproveMode::Ask | crate::types::ApproveMode::Auto
) {
1
} else {
0
};
let gap_height: u16 = 1;
let queue_height: u16 = if self.pending_messages.is_empty() {
0
} else {
1
};
let activity_height: u16 = if matches!(self.activity, Activity::Thinking)
|| (self.is_tool_activity() && self.streaming.is_empty() && self.thinking.is_empty())
{
1
} else {
0
};
let input_height: u16 = self.calculate_input_content_height() + 2;
let bottom_reserved = status_height
+ input_height
+ hint_height
+ gap_height
+ queue_height
+ activity_height;
let panel_height = area.height.saturating_sub(bottom_reserved);
let panel_area = Rect::new(
area.width.saturating_sub(panel_width),
0,
panel_width,
panel_height,
);
let block = Block::default()
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::Cyan))
.title(Span::styled(
" ⚙ Workflow (Alt+W) ",
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
));
let inner_area = block.inner(panel_area);
f.render_widget(block, panel_area);
match self.workflow_state.view_mode {
crate::workflow::WorkflowViewMode::Dag => {
let dag = DagWidget::new(&self.workflow_state);
f.render_widget(dag, inner_area);
}
crate::workflow::WorkflowViewMode::Progress => {
let mut buf = ratatui::buffer::Buffer::empty(inner_area);
render_progress_view(&self.workflow_state, inner_area, &mut buf);
for y in inner_area.top()..inner_area.bottom() {
for x in inner_area.left()..inner_area.right() {
if let Some(cell) = buf.cell((x, y))
&& let Some(c) = f.buffer_mut().cell_mut((x, y))
{
*c = cell.clone();
}
}
}
}
crate::workflow::WorkflowViewMode::Detail => {
self.draw_workflow_detail(f, inner_area);
}
}
}
fn draw_workflow_detail(&self, f: &mut ratatui::Frame, area: Rect) {
if self.workflow_state.selected_node.is_none() {
let text = Paragraph::new("No node selected\n\nUse ↑↓ to select node");
f.render_widget(text, area);
return;
}
let node_id = self.workflow_state.selected_node.as_ref().unwrap();
let def = self.workflow_state.workflow_def.as_ref();
if let Some(def) = def {
let node = def.nodes.iter().find(|n| n.id == *node_id);
if let Some(node) = node {
let status = self.workflow_state.get_node_status(node_id);
let lines = vec![
Line::from(Span::styled(
format!("Node: {}", node.name),
Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD),
)),
Line::from(Span::styled(
format!("ID: {}", node.id),
Style::default().fg(Color::Gray),
)),
Line::from(Span::styled(
format!("Type: {:?}", node.node_type),
Style::default().fg(Color::Gray),
)),
Line::from(Span::styled(
format!("Status: {}", status.icon()),
Style::default().fg(status_to_color(status.color())),
)),
];
let paragraph = Paragraph::new(lines);
f.render_widget(paragraph, area);
}
}
}
}