orchflow_terminal/
state.rs

1// Terminal State Management
2//
3// Tracks the state of each terminal including cursor position,
4// mode, and other terminal-specific information.
5
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8use tokio::sync::RwLock;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TerminalState {
12    pub id: String,
13    pub rows: u16,
14    pub cols: u16,
15    pub cursor: CursorPosition,
16    pub mode: TerminalMode,
17    pub title: String,
18    pub working_dir: Option<String>,
19    pub process_info: Option<ProcessInfo>,
20    pub active: bool,
21    pub last_activity: chrono::DateTime<chrono::Utc>,
22}
23
24#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
25pub struct CursorPosition {
26    pub x: u16,
27    pub y: u16,
28    pub visible: bool,
29    pub blinking: bool,
30}
31
32#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
33#[serde(rename_all = "snake_case")]
34pub enum TerminalMode {
35    Normal,
36    Insert,
37    Visual,
38    Command,
39    Search,
40    Raw,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct ProcessInfo {
45    pub pid: u32,
46    pub name: String,
47    pub command: String,
48    pub cpu_usage: f32,
49    pub memory_usage: u64,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct TerminalSelection {
54    pub start: CursorPosition,
55    pub end: CursorPosition,
56    pub mode: SelectionMode,
57}
58
59#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
60#[serde(rename_all = "snake_case")]
61pub enum SelectionMode {
62    Character,
63    Word,
64    Line,
65    Block,
66}
67
68impl TerminalState {
69    pub fn new(id: String, rows: u16, cols: u16) -> Self {
70        Self {
71            id,
72            rows,
73            cols,
74            cursor: CursorPosition::default(),
75            mode: TerminalMode::Normal,
76            title: "Terminal".to_string(),
77            working_dir: None,
78            process_info: None,
79            active: true,
80            last_activity: chrono::Utc::now(),
81        }
82    }
83
84    /// Update cursor position
85    pub fn set_cursor(&mut self, x: u16, y: u16) {
86        self.cursor.x = x.min(self.cols - 1);
87        self.cursor.y = y.min(self.rows - 1);
88        self.update_activity();
89    }
90
91    /// Move cursor relatively
92    pub fn move_cursor(&mut self, dx: i16, dy: i16) {
93        let new_x = (self.cursor.x as i16 + dx).max(0).min(self.cols as i16 - 1) as u16;
94        let new_y = (self.cursor.y as i16 + dy).max(0).min(self.rows as i16 - 1) as u16;
95        self.set_cursor(new_x, new_y);
96    }
97
98    /// Resize terminal
99    pub fn resize(&mut self, rows: u16, cols: u16) {
100        self.rows = rows;
101        self.cols = cols;
102        // Adjust cursor if needed
103        if self.cursor.x >= cols {
104            self.cursor.x = cols - 1;
105        }
106        if self.cursor.y >= rows {
107            self.cursor.y = rows - 1;
108        }
109        self.update_activity();
110    }
111
112    /// Change terminal mode
113    pub fn set_mode(&mut self, mode: TerminalMode) {
114        self.mode = mode;
115        self.update_activity();
116    }
117
118    /// Update last activity timestamp
119    pub fn update_activity(&mut self) {
120        self.last_activity = chrono::Utc::now();
121    }
122
123    /// Set terminal title
124    pub fn set_title(&mut self, title: String) {
125        self.title = title;
126    }
127
128    /// Update process info
129    pub fn update_process_info(&mut self, info: ProcessInfo) {
130        self.process_info = Some(info);
131        self.update_activity();
132    }
133}
134
135/// Terminal state manager for multiple terminals
136pub struct TerminalStateManager {
137    states: Arc<RwLock<std::collections::HashMap<String, TerminalState>>>,
138}
139
140impl Default for TerminalStateManager {
141    fn default() -> Self {
142        Self::new()
143    }
144}
145
146impl TerminalStateManager {
147    pub fn new() -> Self {
148        Self {
149            states: Arc::new(RwLock::new(std::collections::HashMap::new())),
150        }
151    }
152
153    /// Add a new terminal state
154    pub async fn add_terminal(&self, state: TerminalState) {
155        self.states.write().await.insert(state.id.clone(), state);
156    }
157
158    /// Get terminal state
159    pub async fn get_terminal(&self, id: &str) -> Option<TerminalState> {
160        self.states.read().await.get(id).cloned()
161    }
162
163    /// Update terminal state
164    pub async fn update_terminal<F>(&self, id: &str, updater: F) -> Result<(), String>
165    where
166        F: FnOnce(&mut TerminalState),
167    {
168        let mut states = self.states.write().await;
169        if let Some(state) = states.get_mut(id) {
170            updater(state);
171            Ok(())
172        } else {
173            Err(format!("Terminal {id} not found"))
174        }
175    }
176
177    /// Remove terminal state
178    pub async fn remove_terminal(&self, id: &str) -> Option<TerminalState> {
179        self.states.write().await.remove(id)
180    }
181
182    /// Get all terminal states
183    pub async fn get_all_terminals(&self) -> Vec<TerminalState> {
184        self.states.read().await.values().cloned().collect()
185    }
186
187    /// Get active terminal count
188    pub async fn active_count(&self) -> usize {
189        self.states
190            .read()
191            .await
192            .values()
193            .filter(|s| s.active)
194            .count()
195    }
196}
197
198impl Default for TerminalMode {
199    fn default() -> Self {
200        Self::Normal
201    }
202}