Skip to main content

rab/tui/
component.rs

1use crossterm::event::KeyEvent;
2
3/// Key for render caching — components return this to indicate when cache is valid.
4/// Two renders with the same cache key produce identical output.
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct RenderCacheKey {
7    /// Viewport width.
8    pub width: usize,
9    /// Whether expanded (for collapsible components).
10    pub expanded: bool,
11    /// Additional state hash (tool name, args hash, etc.).
12    pub state_hash: u64,
13}
14
15/// Cached render output.
16#[derive(Debug, Clone)]
17pub struct RenderCache {
18    /// The cache key used to generate this output.
19    pub key: RenderCacheKey,
20    /// Rendered lines.
21    pub lines: Vec<String>,
22}
23
24/// Every renderable UI element.
25pub trait Component {
26    /// Render to lines for the given viewport width.
27    /// Each returned string MUST NOT exceed `width` in visible width.
28    fn render(&self, width: usize) -> Vec<String>;
29
30    /// Handle keyboard input. Return `true` if consumed.
31    fn handle_input(&mut self, _key: &KeyEvent) -> bool {
32        false
33    }
34
35    /// Handle a paste event (text from bracketed paste mode).
36    /// Default no-op; override to process pasted content.
37    fn handle_paste(&mut self, _text: &str) {}
38
39    /// Mark this component as needing re-render.
40    /// Called when internal state changes (output received, expanded toggled, etc.).
41    fn invalidate(&mut self) {}
42
43    /// Check if this component needs re-render.
44    /// Default: always re-render (conservative).
45    fn is_dirty(&self) -> bool {
46        true
47    }
48
49    /// Clear dirty flag after successful render.
50    fn clear_dirty(&mut self) {}
51
52    /// Get the cache key for this component's current state.
53    /// Return None to disable caching (always re-render).
54    fn cache_key(&self, _width: usize) -> Option<RenderCacheKey> {
55        None
56    }
57
58    /// Get cached render output, if available and valid.
59    fn get_cached_render(&self) -> Option<&RenderCache> {
60        None
61    }
62
63    /// Store render output in cache.
64    fn set_cached_render(&mut self, _cache: RenderCache) {}
65
66    /// Whether this component wants focus (for IME cursor positioning).
67    fn is_focusable(&self) -> bool {
68        false
69    }
70
71    /// Toggle expanded/collapsed state. No-op by default.
72    /// Override for components that support expand/collapse (tool results, messages, etc.).
73    fn set_expanded(&mut self, _expanded: bool) {}
74
75    /// Toggle thinking block visibility. No-op by default.
76    /// Override for components that display thinking content (AssistantMessageComponent).
77    fn set_hide_thinking(&mut self, _hide: bool) {}
78}