1use serde::{Deserialize, Serialize};
2
3pub type SessionId = String;
5
6pub type WindowId = u32;
8
9pub type PaneId = String;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct SessionInfo {
14 pub name: SessionId,
15 pub windows: Vec<WindowInfo>,
16 pub created_at: u64, }
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct WindowInfo {
21 pub id: WindowId,
22 pub panes: Vec<PaneInfo>,
23 pub active_pane: PaneId,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct PaneInfo {
28 pub id: PaneId,
29 pub pid: u32,
30 pub running: bool,
31 pub exit_code: Option<i32>,
32 pub size: (u16, u16), pub title: String,
34 #[serde(default)]
35 pub marks: Vec<String>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct MarkInfo {
40 pub mark: String,
41 pub pane_id: PaneId,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct CursorPos {
46 pub row: u16,
47 pub col: u16,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct ProcessInfo {
52 pub pid: u32,
53 pub running: bool,
54 pub exit_code: Option<i32>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub enum ScrollbackRange {
59 Last(usize),
60 All,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct CaptureResult {
65 pub pane_id: PaneId,
66 pub lines: Vec<String>,
67 pub scrollback_lines: Vec<String>,
68 pub cursor: CursorPos,
69 pub size: (u16, u16), pub process: ProcessInfo,
71 pub title: String,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
76pub struct ScreenCell {
77 pub ch: char,
78 pub attrs: ScreenCellAttrs,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
82pub struct ScreenCellAttrs {
83 pub bold: bool,
84 pub dim: bool,
85 pub italic: bool,
86 pub underline: bool,
87 pub fg: ScreenColor,
88 pub bg: ScreenColor,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
92pub enum ScreenColor {
93 #[default]
94 Default,
95 Indexed(u8),
96 Rgb(u8, u8, u8),
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
101pub struct PaneLayout {
102 pub pane_id: PaneId,
103 pub x: u16,
104 pub y: u16,
105 pub width: u16,
106 pub height: u16,
107 pub is_active: bool,
108 pub is_zoomed: bool,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
113pub struct WindowBarInfo {
114 pub id: WindowId,
115 pub title: String,
116 pub is_active: bool,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
121pub struct LayoutInfo {
122 pub session: SessionId,
123 pub window_id: WindowId,
124 pub panes: Vec<PaneLayout>,
125 pub terminal_cols: u16,
126 pub terminal_rows: u16,
127 pub windows: Vec<WindowBarInfo>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(tag = "event")]
133pub enum PaneEvent {
134 #[serde(rename = "output")]
135 Output { pane: PaneId, data: String },
136 #[serde(rename = "exit")]
137 Exit { pane: PaneId, code: i32 },
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ServerStatusInfo {
142 pub version: String,
143 pub uptime_secs: u64,
144 pub sessions: u32,
145 pub windows: u32,
146 pub panes: u32,
147 pub pid: u32,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct LayoutListEntry {
152 pub name: String,
153 pub source: LayoutSource,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub enum CaptureFilter {
158 LastCommand { strip_prompt: bool },
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
162pub enum LayoutSource {
163 Config,
164 Saved,
165 Preset,
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn pane_event_output_json_format() {
174 let event = PaneEvent::Output {
175 pane: "%0".into(),
176 data: "hello world".into(),
177 };
178 let json = serde_json::to_string(&event).unwrap();
179 assert!(json.contains("\"event\":\"output\""));
180 assert!(json.contains("\"pane\":\"%0\""));
181 assert!(json.contains("\"data\":\"hello world\""));
182
183 let parsed: PaneEvent = serde_json::from_str(&json).unwrap();
185 match parsed {
186 PaneEvent::Output { pane, data } => {
187 assert_eq!(pane, "%0");
188 assert_eq!(data, "hello world");
189 }
190 _ => panic!("wrong variant"),
191 }
192 }
193
194 #[test]
195 fn pane_event_exit_json_format() {
196 let event = PaneEvent::Exit {
197 pane: "%1".into(),
198 code: 0,
199 };
200 let json = serde_json::to_string(&event).unwrap();
201 assert!(json.contains("\"event\":\"exit\""));
202 assert!(json.contains("\"pane\":\"%1\""));
203 assert!(json.contains("\"code\":0"));
204
205 let parsed: PaneEvent = serde_json::from_str(&json).unwrap();
206 match parsed {
207 PaneEvent::Exit { pane, code } => {
208 assert_eq!(pane, "%1");
209 assert_eq!(code, 0);
210 }
211 _ => panic!("wrong variant"),
212 }
213 }
214
215 #[test]
216 fn pane_event_msgpack_roundtrip() {
217 let events = vec![
218 PaneEvent::Output {
219 pane: "%0".into(),
220 data: "test data".into(),
221 },
222 PaneEvent::Exit {
223 pane: "%2".into(),
224 code: 1,
225 },
226 ];
227 for event in events {
228 let bytes = rmp_serde::to_vec(&event).unwrap();
229 let decoded: PaneEvent = rmp_serde::from_slice(&bytes).unwrap();
230 assert_eq!(format!("{:?}", event), format!("{:?}", decoded));
231 }
232 }
233}