rush_sync_server/ui/
terminal.rs1use crate::core::constants::APP_TITLE;
6use crate::core::prelude::*;
7use crate::i18n::get_translation;
8use crossterm::{
9 cursor, execute,
10 style::ResetColor,
11 terminal::{
12 self, disable_raw_mode, enable_raw_mode, ClearType, EnterAlternateScreen,
13 LeaveAlternateScreen,
14 },
15};
16use std::io::Stdout;
17
18pub struct TerminalManager {
19 stdout: Stdout,
20 raw_mode_enabled: bool,
21}
22
23impl TerminalManager {
24 pub async fn new() -> Result<Self> {
25 let stdout = io::stdout();
26 Ok(Self {
27 stdout,
28 raw_mode_enabled: false,
29 })
30 }
31
32 pub async fn setup(&mut self) -> Result<()> {
33 self.enable_full_raw_mode().await?;
35
36 execute!(
38 self.stdout,
39 terminal::Clear(ClearType::All),
40 EnterAlternateScreen,
41 terminal::DisableLineWrap,
42 terminal::SetTitle(APP_TITLE),
43 crossterm::style::Print("\x1B]112\x07"), crossterm::style::Print("\x1B[0 q"), cursor::Hide
47 )?;
48
49 Ok(())
50 }
51
52 async fn enable_full_raw_mode(&mut self) -> Result<()> {
54 enable_raw_mode()?;
56 self.raw_mode_enabled = true;
57
58 execute!(
60 self.stdout,
61 crossterm::style::Print("\x1B[?1000h"), crossterm::style::Print("\x1B[?1002h"), crossterm::style::Print("\x1B[?1015h"), crossterm::style::Print("\x1B[?1006h"), crossterm::style::Print("\x1B[?1049h"), )?;
69 Ok(())
70 }
71
72 pub async fn cleanup(&mut self) -> Result<()> {
73 log::info!("🔄 Starting terminal cleanup...");
74
75 if self.raw_mode_enabled {
77 self.disable_full_raw_mode().await?;
78 }
79
80 execute!(
82 self.stdout,
83 crossterm::style::Print("\x1B]12;\x07"), crossterm::style::Print("\x1B]Pl\x1B\\"), crossterm::style::Print("\x1B]112\x07"), crossterm::style::Print("\x1B[0 q"), ResetColor, cursor::Show, )?;
91
92 execute!(
94 self.stdout,
95 terminal::Clear(ClearType::All),
96 LeaveAlternateScreen,
97 terminal::EnableLineWrap,
98 cursor::MoveTo(0, 0)
99 )?;
100
101 execute!(
103 self.stdout,
104 crossterm::style::Print("\x1B[!p"), crossterm::style::Print("\x1B]12;white\x07"), crossterm::style::Print("\x1B[0 q"), crossterm::style::Print("\x1B[?25h"), ResetColor, )?;
111
112 self.stdout.flush()?;
114
115 log::info!("{}", get_translation("terminal.cleanup.done", &[]));
116 Ok(())
117 }
118
119 async fn disable_full_raw_mode(&mut self) -> Result<()> {
121 if !self.raw_mode_enabled {
122 return Ok(());
123 }
124
125 execute!(
127 self.stdout,
128 crossterm::style::Print("\x1B[?1000l"), crossterm::style::Print("\x1B[?1002l"), crossterm::style::Print("\x1B[?1015l"), crossterm::style::Print("\x1B[?1006l"), crossterm::style::Print("\x1B[?1049l"), )?;
136
137 disable_raw_mode()?;
139 self.raw_mode_enabled = false;
140 Ok(())
141 }
142
143 pub fn is_raw_mode_enabled(&self) -> bool {
145 self.raw_mode_enabled
146 }
147
148 pub async fn force_raw_mode(&mut self) -> Result<()> {
150 if !self.raw_mode_enabled {
151 log::warn!("🚨 Raw mode was lost, re-enabling...");
152 self.enable_full_raw_mode().await?;
153 }
154 Ok(())
155 }
156}
157
158impl Drop for TerminalManager {
160 fn drop(&mut self) {
161 if self.raw_mode_enabled {
162 let _ = disable_raw_mode();
164 let _ = execute!(
165 std::io::stdout(),
166 terminal::LeaveAlternateScreen,
167 cursor::Show,
168 ResetColor
169 );
170 log::warn!("🚨 Emergency terminal cleanup in destructor");
171 }
172 }
173}