use super::render_scroll_view_persistent_with_theme;
#[cfg(any(test, debug_assertions))]
use crate::tui::PADDED_INLINE_SEPARATOR;
use crate::tui::{input, state::MissionControlState};
use ratatui::{
Frame,
layout::Rect,
text::{Line, Span},
widgets::{Paragraph, Wrap},
};
pub(super) fn branch_text(state: &MissionControlState) -> String {
state
.footer_git_branch
.as_deref()
.filter(|branch| !branch.is_empty())
.map(|branch| format!("Branch: {branch}"))
.unwrap_or_default()
}
pub(super) fn version_title(state: &MissionControlState) -> Line<'static> {
let accent = state.theme.accent(false);
let mut spans = Vec::new();
#[cfg(any(test, debug_assertions))]
if let Some(perf) = state.perf.footer_text() {
spans.push(Span::styled(
format!("{perf}{PADDED_INLINE_SEPARATOR}"),
accent,
));
}
if state.available_update_version.is_some() {
spans.push(Span::styled(
"(/update ready) ",
accent.fg(state.theme.text_command()),
));
}
spans.push(Span::styled(
format!("MAGI-CODE v{}", env!("CARGO_PKG_VERSION")),
accent,
));
Line::from(spans)
}
pub(super) fn draw_help(frame: &mut Frame<'_>, area: Rect, state: &MissionControlState) {
let theme = state.theme;
let inner = crate::tui::modal_container::draw_modal_container(
frame,
area,
"Help",
"[PgUp]/[PgDn] scroll",
theme,
Some("[Esc] close"),
);
let text_area = super::overlay_text_area(inner);
let help_text = input::help_text();
let wrap_width = super::scroll_view_content_width(text_area).max(1);
let scroll = state.help_render_offset(text_area.height, wrap_width, help_text);
render_scroll_view_persistent_with_theme(
frame,
text_area,
super::usize_to_u16_saturating(scroll),
super::usize_to_u16_saturating(crate::tui::transcript::ratatui_wrapped_visual_rows(
help_text, wrap_width,
)),
Paragraph::new(help_text)
.style(theme.app())
.wrap(Wrap { trim: false }),
&state.scroll_views.help,
theme,
);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn help_shell_keeps_chrome_out_of_scroll_content() {
use ratatui::{Terminal, backend::TestBackend};
let state = MissionControlState::default();
let mut terminal = Terminal::new(TestBackend::new(100, 30)).unwrap();
let area = crate::tui::modal_container::modal_area(Rect::new(0, 0, 100, 30), 60, 60);
terminal
.draw(|frame| draw_help(frame, area, &state))
.unwrap();
let buffer = terminal.backend().buffer();
let row = |y| {
(area.x..area.right())
.map(|x| buffer[(x, y)].symbol())
.collect::<String>()
};
assert!(row(area.y).starts_with("┌Help"));
assert!(row(area.y).contains("[Esc] close"));
assert!(row(area.bottom() - 1).contains("[PgUp]/[PgDn] scroll"));
assert_eq!(
buffer[(area.x + 1, area.y + 1)].bg,
state.theme.app().bg.unwrap()
);
}
#[test]
fn branch_header_omits_directory() {
let state = MissionControlState {
footer_cwd: "/repo".to_string(),
footer_git_branch: Some("main".to_string()),
..Default::default()
};
assert_eq!(branch_text(&state), "Branch: main");
}
#[test]
fn branch_header_is_empty_for_missing_or_empty_branch() {
for branch in [None, Some(String::new())] {
let state = MissionControlState {
footer_cwd: "/repo".to_string(),
footer_git_branch: branch,
..Default::default()
};
assert_eq!(branch_text(&state), "");
}
}
#[test]
fn version_title_uses_accent_and_package_version() {
let state = MissionControlState::default();
let title = version_title(&state);
assert_eq!(
title.to_string(),
format!("MAGI-CODE v{}", env!("CARGO_PKG_VERSION"))
);
assert_eq!(title.spans[0].style, state.theme.accent(false));
}
#[test]
fn version_title_includes_perf_text_only_after_snapshot_exists() {
let state = MissionControlState {
#[cfg(any(test, debug_assertions))]
perf: crate::tui::perf::TuiPerfCounters::with_enabled(true),
..Default::default()
};
assert!(!version_title(&state).to_string().contains("perf drain"));
#[cfg(any(test, debug_assertions))]
{
state.perf.record_drain_us(400);
state.perf.increment_projected_cards();
state.perf.set_backlog_before_drain(3);
state.perf.set_backlog_after_drain(2);
state.perf.set_backlog_before_draw(1);
state.perf.record_draw_us(2100);
state.perf.record_prompt_edit_to_draw_us(17_300);
}
let text = version_title(&state).to_string();
assert!(text.contains("perf drain 0.4/0.4ms draw 2.1/2.1ms cards 1 backlog 3/2/1"));
assert!(text.contains("edit->draw 17.3ms"));
assert!(text.contains("MAGI-CODE v"));
}
}