Skip to main content

oxicode_vtui/design/layout/
mod.rs

1//! Layout system — responsive mode + agent view geometry.
2//!
3//! ## Modules
4//!
5//! | Module | Purpose |
6//! |---|---|
7//! | [`agent`] | `AgentViewLayout`, `ActivePane`, `PaneAreas` — pure geometry |
8//! | [`config`] | `LayoutConfig`, `ScrollbarConfig` — appearance settings |
9//! | [`shortcuts_bar`] | `ShortcutsBar` + `HintItem` — bottom keyboard hints |
10//! | [`welcome`] | `WelcomeLayout` — welcome screen geometry |
11//!
12//! The existing [`LayoutMode`] (ported from `vtcode-ui`) provides responsive
13//! breakpoints.  The new `AgentViewLayout` provides the grok-build-style
14//! pure-compute layout engine for the agent conversation view.
15
16pub mod agent;
17pub mod config;
18pub mod shortcuts_bar;
19pub mod welcome;
20
21// ───────────────────────────────────────────────────────────────────────────
22// Re-exports
23// ───────────────────────────────────────────────────────────────────────────
24
25pub use agent::{
26    AUTO_COMPACT_MAX_ROWS, ActivePane, AgentViewLayout, LayoutInput, PaneAreas,
27    SHORT_TERMINAL_ROWS, effective_compact,
28};
29pub use config::{LayoutConfig, ScrollbarConfig};
30pub use shortcuts_bar::{
31    CompactConfig, HintItem, PendingHint, ShortcutBarStyling, ShortcutsBar, compute_effective_hints,
32};
33
34pub use welcome::{HERO_BOX_MIN_WIDTH, PROMPT_HEIGHT, WelcomeLayout, WelcomePromptFocus};
35
36// ───────────────────────────────────────────────────────────────────────────
37// LayoutMode (preserved from the original vtcode-ui port)
38// ───────────────────────────────────────────────────────────────────────────
39
40use ratatui::layout::Rect;
41
42use crate::design::constants::{COMPACT_MAX_COLS, COMPACT_MAX_ROWS, WIDE_MIN_COLS, WIDE_MIN_ROWS};
43
44/// Responsive layout mode based on terminal dimensions.
45///
46/// This enum provides a single source of truth for layout decisions
47/// across the UI, enabling consistent responsive behavior.
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub enum LayoutMode {
50    /// Minimal chrome for tiny terminals (< 80 cols or < 20 rows)
51    Compact,
52    /// Default layout for standard terminals
53    Standard,
54    /// Enhanced layout with sidebar for wide terminals (>= 120 cols, >= 24 rows)
55    Wide,
56}
57
58impl LayoutMode {
59    /// Determine layout mode from viewport dimensions.
60    pub(crate) fn from_area(area: Rect) -> Self {
61        if area.width <= COMPACT_MAX_COLS || area.height <= COMPACT_MAX_ROWS {
62            LayoutMode::Compact
63        } else if area.width >= WIDE_MIN_COLS && area.height >= WIDE_MIN_ROWS {
64            LayoutMode::Wide
65        } else {
66            LayoutMode::Standard
67        }
68    }
69
70    /// Check if borders should be shown.
71    pub(crate) fn show_borders(self) -> bool {
72        !matches!(self, LayoutMode::Compact)
73    }
74
75    /// Check if panel titles should be shown.
76    pub(crate) fn show_titles(self) -> bool {
77        !matches!(self, LayoutMode::Compact)
78    }
79
80    /// Check if sidebar can be shown.
81    pub(crate) fn allow_sidebar(self) -> bool {
82        matches!(self, LayoutMode::Wide)
83    }
84
85    /// Check if logs panel should be visible.
86    pub(crate) fn show_logs_panel(self) -> bool {
87        !matches!(self, LayoutMode::Compact)
88    }
89
90    /// Get the footer height for this mode.
91    pub(crate) fn footer_height(self) -> u16 {
92        0
93    }
94
95    /// Check if footer should be shown.
96    pub(crate) fn show_footer(self) -> bool {
97        false
98    }
99
100    /// Get the maximum header height as percentage of viewport.
101    pub(crate) fn max_header_percent(self) -> f32 {
102        match self {
103            LayoutMode::Compact => 0.2,
104            _ => 0.3,
105        }
106    }
107
108    /// Get the sidebar width percentage (only meaningful in Wide mode).
109    pub(crate) fn sidebar_width_percent(self) -> u16 {
110        match self {
111            LayoutMode::Wide => 25,
112            _ => 0,
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn compact_mode_for_small_terminals() {
123        assert_eq!(
124            LayoutMode::from_area(Rect::new(0, 0, 60, 20)),
125            LayoutMode::Compact
126        );
127        assert_eq!(
128            LayoutMode::from_area(Rect::new(0, 0, 80, 15)),
129            LayoutMode::Compact
130        );
131    }
132
133    #[test]
134    fn standard_mode_for_normal_terminals() {
135        assert_eq!(
136            LayoutMode::from_area(Rect::new(0, 0, 100, 22)),
137            LayoutMode::Standard
138        );
139    }
140
141    #[test]
142    fn wide_mode_for_large_terminals() {
143        assert_eq!(
144            LayoutMode::from_area(Rect::new(0, 0, 140, 30)),
145            LayoutMode::Wide
146        );
147    }
148}