ci / rust (push) Failing after 4m44s
Adds the full visual layer: - `state.rs` — `AppState` (tabs/focus/mode/sidebar visibility/overlay). v1 keeps each tab to one optional session; the split tree is v1.5. - `terminal.rs` — `TerminalGuard` (raw mode + alt screen + mouse capture with RAII restore) and an idempotent panic hook that restores the terminal before re-panicking. - `sidebar.rs` — daemon → session tree with ●/○/✗ glyphs. Pulls a label out of `metadata.label` (the protocol's shape), falls back to the command. - `tabs.rs` — top-row TabBar with active highlight. - `pane.rs` — `vt100::Screen` → ratatui `Buffer` cell-by-cell, with full fg/bg/bold/italic/underline/reverse translation. `PaneStatus` round-trips from `SessionState`. - `overlays.rs` — SpawnModal (5-field form with rotation), KillConfirm, HelpOverlay, Toast. All centered popups with `Clear` underlay. - `render.rs` — `draw(f, RenderInputs)` composes everything; returns a `RegionMap` whose `region_at(col, row)` resolves a mouse coordinate to a tagged Region (modal eclipses the rest). 22 new unit tests bringing the crate total to 80 passing. Tests use content-based assertions over `ratatui::backend::TestBackend` output rather than file-based goldens — less brittle to whitespace shifts while still covering the visible content.
62 lines
1.7 KiB
Rust
62 lines
1.7 KiB
Rust
//! Tab-bar widget — top row of the main area.
|
|
|
|
use ratatui::layout::Rect;
|
|
use ratatui::style::{Modifier, Style};
|
|
use ratatui::text::Line;
|
|
use ratatui::widgets::Tabs;
|
|
use ratatui::Frame;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TabBar {
|
|
pub tab_names: Vec<String>,
|
|
pub active_idx: usize,
|
|
}
|
|
|
|
impl TabBar {
|
|
pub fn render(&self, f: &mut Frame, area: Rect) {
|
|
let titles: Vec<Line> = self
|
|
.tab_names
|
|
.iter()
|
|
.map(|n| Line::from(n.as_str()))
|
|
.collect();
|
|
let tabs = Tabs::new(titles)
|
|
.select(self.active_idx)
|
|
.highlight_style(Style::default().add_modifier(Modifier::REVERSED));
|
|
f.render_widget(tabs, area);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use ratatui::backend::TestBackend;
|
|
use ratatui::Terminal;
|
|
|
|
fn render_to_string(bar: &TabBar, w: u16, h: u16) -> String {
|
|
let backend = TestBackend::new(w, h);
|
|
let mut terminal = Terminal::new(backend).unwrap();
|
|
terminal.draw(|f| bar.render(f, f.area())).unwrap();
|
|
let buf = terminal.backend().buffer().clone();
|
|
let mut out = String::new();
|
|
for y in 0..buf.area.height {
|
|
for x in 0..buf.area.width {
|
|
out.push_str(buf[(x, y)].symbol());
|
|
}
|
|
out.push('\n');
|
|
}
|
|
out
|
|
}
|
|
|
|
#[test]
|
|
fn renders_three_tab_names() {
|
|
let bar = TabBar {
|
|
tab_names: vec!["1".into(), "2".into(), "3".into()],
|
|
active_idx: 1,
|
|
};
|
|
let rendered = render_to_string(&bar, 40, 1);
|
|
assert!(rendered.contains("1"));
|
|
assert!(rendered.contains("2"));
|
|
assert!(rendered.contains("3"));
|
|
}
|
|
}
|