1use crate::layout::AppLayout;
5use crate::theme::Theme;
6use crate::widgets;
7
8use super::{App, Panel};
9
10impl App {
11 pub fn draw(&mut self, frame: &mut ratatui::Frame) {
12 let layout = AppLayout::compute(
13 frame.area(),
14 self.show_side_panels,
15 self.desired_input_height(),
16 );
17
18 self.draw_header(frame, layout.header);
19 if self.sessions.current().show_splash {
20 widgets::splash::render(frame, layout.chat);
21 } else {
22 let mut cache = std::mem::take(&mut self.sessions.current_mut().render_cache);
23 let max_scroll = widgets::chat::render(self, frame, layout.chat, &mut cache);
24 self.sessions.current_mut().render_cache = cache;
25 self.sessions.current_mut().scroll_offset =
26 self.sessions.current().scroll_offset.min(max_scroll);
27 }
28 self.draw_side_panel(frame, &layout);
29 widgets::chat::render_activity(self, frame, layout.activity);
30 widgets::input::render(self, frame, layout.input);
31 widgets::status::render(self, &self.metrics, frame, layout.status);
32
33 if let Some(state) = &self.file_picker_state {
34 widgets::file_picker::render(state, frame, layout.input);
35 }
36
37 if let Some(state) = &self.slash_autocomplete {
38 widgets::slash_autocomplete::render(state, frame, layout.input);
39 }
40
41 if let Some(state) = &self.confirm_state {
42 widgets::confirm::render(&state.prompt, frame, frame.area());
43 }
44
45 if let Some(state) = &self.elicitation_state {
46 widgets::elicitation::render(&state.dialog, frame, frame.area());
47 }
48
49 if let Some(palette) = &self.command_palette {
50 widgets::command_palette::render(palette, frame, frame.area());
51 }
52
53 if self.show_help {
54 widgets::help::render(frame, frame.area());
55 }
56 }
57
58 pub(super) fn draw_header(&self, frame: &mut ratatui::Frame, area: ratatui::layout::Rect) {
59 use ratatui::text::{Line, Span};
60 use ratatui::widgets::Paragraph;
61
62 let theme = Theme::default();
63
64 let provider = if self.metrics.provider_name.is_empty() {
65 "---"
66 } else {
67 &self.metrics.provider_name
68 };
69 let model = if self.metrics.model_name.is_empty() {
70 "---"
71 } else {
72 &self.metrics.model_name
73 };
74
75 let ctx_badge = if self.metrics.extended_context {
76 " [1M CTX]"
77 } else {
78 ""
79 };
80 let text = format!(
81 " Zeph v{} | Provider: {provider} | Model: {model}{ctx_badge}",
82 env!("CARGO_PKG_VERSION")
83 );
84
85 let line = Line::from(Span::styled(text, theme.header));
86 let paragraph = Paragraph::new(line).style(theme.header);
87 frame.render_widget(paragraph, area);
88 }
89
90 fn draw_side_panel(&mut self, frame: &mut ratatui::Frame, layout: &AppLayout) {
91 widgets::skills::render(&self.metrics, frame, layout.skills);
92 widgets::memory::render(&self.metrics, frame, layout.memory);
93
94 {
97 use ratatui::layout::{Constraint, Direction, Layout};
98 let context_split = Layout::default()
99 .direction(Direction::Vertical)
100 .constraints([
101 Constraint::Length(3),
102 Constraint::Length(3),
103 Constraint::Min(0),
104 ])
105 .split(layout.resources);
106 widgets::context_gauge::render(&self.metrics, frame, context_split[0]);
107 widgets::compaction_badge::render(&self.metrics, frame, context_split[1]);
108 widgets::resources::render(&self.metrics, frame, context_split[2]);
109 }
110
111 let tick = self.throbber_state.index().cast_unsigned();
112 let has_graph = self.metrics.orchestration_graph.as_ref().is_some_and(|s| {
113 !s.is_stale()
115 });
116 let panel_focused = self.active_panel == Panel::SubAgents;
117
118 if panel_focused {
121 widgets::subagents::render_interactive(
122 &self.metrics,
123 &mut self.subagent_sidebar,
124 frame,
125 layout.subagents,
126 tick,
127 );
128 } else if has_graph && !self.sessions.current().plan_view_active {
129 widgets::plan_view::render(&self.metrics, frame, layout.subagents, tick);
130 } else if self.has_recent_security_events() {
131 widgets::security::render(&self.metrics, frame, layout.subagents);
132 } else {
133 widgets::subagents::render(&self.metrics, frame, layout.subagents);
134 }
135
136 if self.show_task_panel {
138 if self.task_supervisor.is_some() {
139 widgets::task_registry::render(
140 &self.cached_task_snapshots,
141 tick,
142 layout.subagents,
143 frame,
144 );
145 } else {
146 use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
147 let theme = Theme::default();
148 let block = Block::default()
149 .borders(Borders::ALL)
150 .border_style(theme.panel_border)
151 .title(" Tasks ");
152 let paragraph = Paragraph::new(" Task supervisor not available.")
153 .block(block)
154 .wrap(Wrap { trim: true });
155 frame.render_widget(paragraph, layout.subagents);
156 }
157 }
158 }
159}