Lines
100 %
Functions
50 %
Branches
//! Inherent draw + intent handling for `ConsoleState`.
//!
//! The draw logic (scrollback + prompt + hint) lives here; the pure state
//! stays in `tabs::nms`. The console consumes no tab-layer intents (input is
//! routed via `handle_console` while focused), so `handle` always ignores.
use crate::event::Intent;
use crate::pane::PaneId;
use crate::tabs::nms::ConsoleState;
use crate::view::{DrawCtx, Handled};
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
fn draw_scrollback(state: &ConsoleState, frame: &mut Frame, area: Rect) {
let visible = usize::from(area.height);
let lines: Vec<Line> = state
.visible_scrollback(visible)
.iter()
.map(|l| Line::from(l.as_str()))
.collect();
frame.render_widget(Paragraph::new(lines).wrap(Wrap { trim: false }), area);
}
fn draw_prompt(state: &ConsoleState, frame: &mut Frame, area: Rect, focused: bool) {
let marker = if state.pending.is_empty() {
"nms> "
} else {
"...> "
};
let content = format!("{marker}{}", state.input.buffer());
let style = if focused {
Style::default()
.fg(Color::White)
.add_modifier(Modifier::BOLD)
Style::default().fg(Color::Gray)
frame.render_widget(Paragraph::new(Span::styled(content, style)), area);
fn draw_hint(frame: &mut Frame, area: Rect, active: bool, pane: PaneId) {
let hint = if active {
match pane {
PaneId::Scrollback => "Tab prompt Up/Down scroll PgUp/PgDn page Esc blur",
PaneId::Prompt => "Enter run Esc blur C-c interrupt Up/Down history",
"i/Enter focus 1-6 tabs Tab next q quit"
let line = Span::styled(hint, Style::default().fg(Color::DarkGray));
frame.render_widget(Paragraph::new(line), area);
impl ConsoleState {
pub fn draw(&self, frame: &mut Frame, area: Rect, ctx: &DrawCtx) {
let title = if ctx.is_active && self.panes.is_focused(PaneId::Scrollback) {
"Console — Scrollback ↕"
"Console"
let block = Block::default().borders(Borders::ALL).title(title);
let inner = block.inner(area);
frame.render_widget(block, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Min(1),
Constraint::Length(1),
])
.split(inner);
draw_scrollback(self, frame, chunks[0]);
draw_prompt(
self,
frame,
chunks[1],
ctx.is_active && self.panes.is_focused(PaneId::Prompt),
);
draw_hint(frame, chunks[2], ctx.is_active, self.panes.focused());
pub fn handle(&mut self, _intent: Intent, _ctx: &DrawCtx) -> Handled {
Handled::Ignored