rush_sync_server/ui/
terminal.rs

1use crate::core::constants::APP_TITLE;
2use crate::core::prelude::*;
3use crate::i18n::get_translation;
4use crossterm::{
5    cursor, execute,
6    style::ResetColor,
7    terminal::{
8        self, disable_raw_mode, enable_raw_mode, ClearType, EnterAlternateScreen,
9        LeaveAlternateScreen,
10    },
11};
12use std::io::Stdout;
13
14pub struct TerminalManager {
15    stdout: Stdout,
16    raw_mode_enabled: bool,
17}
18
19impl TerminalManager {
20    pub async fn new() -> Result<Self> {
21        Ok(Self {
22            stdout: io::stdout(),
23            raw_mode_enabled: false,
24        })
25    }
26
27    pub async fn setup(&mut self) -> Result<()> {
28        self.enable_full_raw_mode().await?;
29        execute!(
30            self.stdout,
31            terminal::Clear(ClearType::All),
32            EnterAlternateScreen,
33            terminal::DisableLineWrap,
34            terminal::SetTitle(APP_TITLE),
35            crossterm::style::Print("\x1B]112\x07"),
36            crossterm::style::Print("\x1B[0 q"),
37            cursor::Hide
38        )?;
39        Ok(())
40    }
41
42    async fn enable_full_raw_mode(&mut self) -> Result<()> {
43        enable_raw_mode()?;
44        self.raw_mode_enabled = true;
45        execute!(
46            self.stdout,
47            crossterm::style::Print("\x1B[?1000h"),
48            crossterm::style::Print("\x1B[?1002h"),
49            crossterm::style::Print("\x1B[?1015h"),
50            crossterm::style::Print("\x1B[?1006h"),
51            crossterm::style::Print("\x1B[?1049h")
52        )?;
53        Ok(())
54    }
55
56    pub async fn cleanup(&mut self) -> Result<()> {
57        log::info!("🔄 Starting terminal cleanup...");
58
59        if self.raw_mode_enabled {
60            self.disable_full_raw_mode().await?;
61        }
62
63        // Multi-standard cursor reset for maximum compatibility
64        execute!(
65            self.stdout,
66            crossterm::style::Print("\x1B]12;\x07"),
67            crossterm::style::Print("\x1B]Pl\x1B\\"),
68            crossterm::style::Print("\x1B]112\x07"),
69            crossterm::style::Print("\x1B[0 q"),
70            ResetColor,
71            cursor::Show
72        )?;
73
74        execute!(
75            self.stdout,
76            terminal::Clear(ClearType::All),
77            LeaveAlternateScreen,
78            terminal::EnableLineWrap,
79            cursor::MoveTo(0, 0)
80        )?;
81
82        // Final reset - guaranteed standard terminal
83        execute!(
84            self.stdout,
85            crossterm::style::Print("\x1B[!p"),
86            crossterm::style::Print("\x1B]12;white\x07"),
87            crossterm::style::Print("\x1B[0 q"),
88            crossterm::style::Print("\x1B[?25h"),
89            ResetColor
90        )?;
91
92        self.stdout.flush()?;
93        log::info!("{}", get_translation("terminal.cleanup.done", &[]));
94        Ok(())
95    }
96
97    async fn disable_full_raw_mode(&mut self) -> Result<()> {
98        if !self.raw_mode_enabled {
99            return Ok(());
100        }
101
102        execute!(
103            self.stdout,
104            crossterm::style::Print("\x1B[?1000l"),
105            crossterm::style::Print("\x1B[?1002l"),
106            crossterm::style::Print("\x1B[?1015l"),
107            crossterm::style::Print("\x1B[?1006l"),
108            crossterm::style::Print("\x1B[?1049l")
109        )?;
110
111        disable_raw_mode()?;
112        self.raw_mode_enabled = false;
113        Ok(())
114    }
115
116    pub fn is_raw_mode_enabled(&self) -> bool {
117        self.raw_mode_enabled
118    }
119
120    pub async fn force_raw_mode(&mut self) -> Result<()> {
121        if !self.raw_mode_enabled {
122            log::warn!("🚨 Raw mode was lost, re-enabling...");
123            self.enable_full_raw_mode().await?;
124        }
125        Ok(())
126    }
127}
128
129impl Drop for TerminalManager {
130    fn drop(&mut self) {
131        if self.raw_mode_enabled {
132            let _ = disable_raw_mode();
133            let _ = execute!(
134                std::io::stdout(),
135                terminal::LeaveAlternateScreen,
136                cursor::Show,
137                ResetColor
138            );
139            log::warn!("🚨 Emergency terminal cleanup in destructor");
140        }
141    }
142}