agent_tui/daemon/
terminal_state.rs

1use crate::core::{Component, Element, component_to_element, find_element_by_ref};
2use crate::terminal::{CursorPosition, ScreenBuffer, VirtualTerminal};
3
4/// Manages terminal emulation state and element detection.
5///
6/// Wraps VirtualTerminal and maintains a cache of detected UI elements,
7/// separate from PTY lifecycle concerns.
8pub struct TerminalState {
9    terminal: VirtualTerminal,
10    cached_elements: Vec<Element>,
11}
12
13impl TerminalState {
14    pub fn new(cols: u16, rows: u16) -> Self {
15        Self {
16            terminal: VirtualTerminal::new(cols, rows),
17            cached_elements: Vec::new(),
18        }
19    }
20
21    pub fn process(&self, data: &[u8]) {
22        self.terminal.process(data);
23    }
24
25    pub fn screen_text(&self) -> String {
26        self.terminal.screen_text()
27    }
28
29    pub fn screen_buffer(&self) -> ScreenBuffer {
30        self.terminal.screen_buffer()
31    }
32
33    pub fn cursor(&self) -> CursorPosition {
34        self.terminal.cursor()
35    }
36
37    pub fn size(&self) -> (u16, u16) {
38        self.terminal.size()
39    }
40
41    pub fn resize(&mut self, cols: u16, rows: u16) {
42        self.terminal.resize(cols, rows);
43    }
44
45    pub fn clear(&mut self) {
46        self.terminal.clear();
47    }
48
49    pub fn detect_elements(&mut self, cursor: &CursorPosition) -> &[Element] {
50        let buffer = self.terminal.screen_buffer();
51        let components = crate::core::analyze(&buffer, cursor);
52
53        self.cached_elements = components
54            .iter()
55            .filter(|c| c.role.is_interactive())
56            .enumerate()
57            .map(|(i, c)| component_to_element(c, i, cursor.row, cursor.col))
58            .collect();
59
60        &self.cached_elements
61    }
62
63    pub fn cached_elements(&self) -> &[Element] {
64        &self.cached_elements
65    }
66
67    pub fn find_element(&self, element_ref: &str) -> Option<&Element> {
68        find_element_by_ref(&self.cached_elements, element_ref)
69    }
70
71    pub fn analyze_screen(&self, cursor: &CursorPosition) -> Vec<Component> {
72        let buffer = self.terminal.screen_buffer();
73        crate::core::analyze(&buffer, cursor)
74    }
75}