use oxicode_vtui::design::layout::{
AgentViewLayout, LayoutConfig, LayoutInput, ScrollbarConfig, effective_compact,
};
use ratatui::layout::Rect;
const COMPOSER_HEIGHT: u16 = 3;
const SHORTCUTS_HEIGHT: u16 = 0;
const CHAT_LAYOUT: LayoutConfig = LayoutConfig {
hpad_left: 1,
hpad_right: 1,
hpad_left_compact: 1,
hpad_right_compact: 1,
outer_vpad: 0,
outer_vpad_compact: 0,
};
const BREATH_ROW: u16 = 1;
pub(super) fn compute_chrome(area: Rect) -> AgentViewLayout {
let compact = effective_compact(false, area.height);
let mut layout = AgentViewLayout::compute(
area,
&CHAT_LAYOUT,
&ScrollbarConfig {
enabled: false,
..Default::default()
},
LayoutInput {
prompt_height: COMPOSER_HEIGHT,
shortcuts_height: SHORTCUTS_HEIGHT,
compact,
..LayoutInput::default()
},
);
layout.scrollback.height = layout.scrollback.height.saturating_sub(BREATH_ROW);
layout
}
pub(super) fn scrollback_height(area: Rect) -> u16 {
let compact = effective_compact(false, area.height);
AgentViewLayout::compute(
area,
&CHAT_LAYOUT,
&ScrollbarConfig {
enabled: false,
..Default::default()
},
LayoutInput {
prompt_height: COMPOSER_HEIGHT,
shortcuts_height: SHORTCUTS_HEIGHT,
compact,
..LayoutInput::default()
},
)
.scrollback
.height
.saturating_sub(BREATH_ROW)
}
pub(super) fn scrollback_geometry(area: Rect) -> (u16, u16) {
let compact = effective_compact(false, area.height);
let layout = AgentViewLayout::compute(
area,
&CHAT_LAYOUT,
&ScrollbarConfig {
enabled: false,
..Default::default()
},
LayoutInput {
prompt_height: COMPOSER_HEIGHT,
shortcuts_height: SHORTCUTS_HEIGHT,
compact,
..LayoutInput::default()
},
);
(layout.scrollback.x, layout.scrollback.width)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chrome_has_no_top_status_row() {
let layout = compute_chrome(Rect {
x: 0,
y: 0,
width: 80,
height: 24,
});
assert_eq!(layout.scrollback.y, 0, "scrollback starts at row 0");
assert_eq!(
layout.scrollback.height,
24 - COMPOSER_HEIGHT - SHORTCUTS_HEIGHT - BREATH_ROW,
"scrollback owns every row the composer does not, minus the breath row"
);
assert_eq!(
layout.scrollback.bottom() + 1,
layout.prompt.y,
"the breath row separates transcript from composer"
);
assert_eq!(SHORTCUTS_HEIGHT, 0, "the static shortcuts bar is gone");
}
#[test]
fn chrome_respects_short_terminal_without_panic() {
let layout = compute_chrome(Rect {
x: 0,
y: 0,
width: 60,
height: 12,
});
assert!(layout.scrollback.height > 0, "transcript keeps space");
assert!(layout.prompt.height > 0, "composer keeps space");
}
}