rush_sync_server/output/
message.rs

1// =====================================================
2// FILE: src/output/message.rs - TYPEWRITER FIX
3// =====================================================
4
5use crate::core::prelude::*;
6use crate::input::keyboard::KeyAction;
7use crate::output::scroll::ScrollState;
8use unicode_segmentation::UnicodeSegmentation;
9
10pub struct Message {
11    pub content: String,
12    pub current_length: usize,
13    pub timestamp: Instant,
14}
15
16pub struct MessageManager<'a> {
17    pub messages: Vec<Message>,
18    config: &'a Config,
19    pub scroll_state: ScrollState,
20}
21
22impl<'a> MessageManager<'a> {
23    pub fn new(config: &'a Config) -> Self {
24        let scroll_state = ScrollState::new();
25        Self {
26            messages: Vec::with_capacity(config.max_messages),
27            config,
28            scroll_state,
29        }
30    }
31
32    pub fn clear_messages(&mut self) {
33        self.messages.clear();
34        self.scroll_state.force_auto_scroll();
35    }
36
37    pub fn get_content_height(&self) -> usize {
38        self.messages.len()
39    }
40
41    pub fn get_messages(&self) -> Vec<(&String, usize)> {
42        let (start, end) = self.scroll_state.get_visible_range();
43        let start = start.min(self.messages.len());
44        let end = end.min(self.messages.len());
45
46        if start >= end {
47            return Vec::new();
48        }
49
50        self.messages[start..end]
51            .iter()
52            .map(|msg| (&msg.content, msg.current_length))
53            .collect()
54    }
55
56    pub fn add_message(&mut self, content: String) {
57        if self.messages.len() >= self.config.max_messages {
58            self.messages.remove(0);
59            if self.scroll_state.offset > 0 {
60                self.scroll_state.offset = self.scroll_state.offset.saturating_sub(1);
61            }
62        }
63
64        // ✅ TYPEWRITER FIX: Wenn delay = 0, sofort alles anzeigen
65        let initial_length = if self.config.typewriter_delay.as_millis() == 0 {
66            content.graphemes(true).count() // ✅ Komplette Nachricht sofort
67        } else {
68            1 // ✅ Typewriter-Effekt: nur erstes Zeichen
69        };
70
71        self.messages.push(Message {
72            content,
73            current_length: initial_length,
74            timestamp: Instant::now(),
75        });
76
77        // Erzwinge Auto-Scroll bei neuer Nachricht
78        self.scroll_state.force_auto_scroll();
79
80        // Update dimensions with current window height
81        self.scroll_state
82            .update_dimensions(self.scroll_state.window_height, self.messages.len());
83    }
84
85    pub fn handle_scroll(&mut self, action: KeyAction, window_height: usize) {
86        // Update dimensions vor dem Scrollen
87        self.scroll_state
88            .update_dimensions(window_height, self.messages.len());
89
90        match action {
91            KeyAction::ScrollUp => {
92                self.scroll_state.scroll_up(1);
93            }
94            KeyAction::ScrollDown => {
95                self.scroll_state.scroll_down(1);
96            }
97            KeyAction::PageUp => {
98                let scroll_amount = window_height.saturating_sub(1);
99                self.scroll_state.scroll_up(scroll_amount);
100            }
101            KeyAction::PageDown => {
102                let scroll_amount = window_height.saturating_sub(1);
103                self.scroll_state.scroll_down(scroll_amount);
104            }
105            _ => {}
106        }
107    }
108
109    pub fn get_visible_messages(&self) -> Vec<(&String, usize)> {
110        self.get_messages()
111    }
112
113    // ✅ HAUPTFIX: Typewriter Update nur wenn delay > 0
114    pub fn update_typewriter(&mut self) {
115        // ✅ EARLY RETURN: Wenn typewriter_delay = 0, mache nichts
116        if self.config.typewriter_delay.as_millis() == 0 {
117            return;
118        }
119
120        if let Some(last_message) = self.messages.last_mut() {
121            let total_length = last_message.content.graphemes(true).count();
122
123            if last_message.current_length < total_length {
124                let elapsed = last_message.timestamp.elapsed();
125
126                // ✅ ULTRASCHNELL: Mehrere Zeichen pro Update bei sehr niedrigen Delays
127                if elapsed >= self.config.typewriter_delay {
128                    let chars_to_add = if self.config.typewriter_delay.as_millis() <= 5 {
129                        let ratio = elapsed.as_millis() as f64
130                            / self.config.typewriter_delay.as_millis() as f64;
131                        ratio.floor().max(1.0) as usize
132                    } else {
133                        1
134                    };
135
136                    let new_length = (last_message.current_length + chars_to_add).min(total_length);
137                    last_message.current_length = new_length;
138                    last_message.timestamp = Instant::now();
139                }
140            }
141        }
142    }
143
144    pub fn log(&mut self, level: &str, message: &str) {
145        let log_message = format!("[{}] {}", level, message);
146        self.add_message(log_message);
147    }
148}