rush_sync_server/ui/
terminal.rs1use 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 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 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}