rush_sync_server/output/
message.rs

1// =====================================================
2// FILE: src/output/message.rs - OWNED CONFIG SUPPORT
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 {
17    pub messages: Vec<Message>,
18    config: Config, // ✅ OWNED statt &'a Config
19    pub scroll_state: ScrollState,
20}
21
22impl MessageManager {
23    /// ✅ NEUER CONSTRUCTOR mit owned config
24    pub fn new(config: &Config) -> Self {
25        let scroll_state = ScrollState::new();
26        Self {
27            messages: Vec::with_capacity(config.max_messages),
28            config: config.clone(), // ✅ CLONE die Config
29            scroll_state,
30        }
31    }
32
33    /// ✅ NEU: UPDATE METHOD für Live-Changes
34    pub fn update_config(&mut self, new_config: &Config) {
35        self.config = new_config.clone();
36
37        // ✅ RESIZE messages buffer falls max_messages geändert wurde
38        if self.messages.len() > self.config.max_messages {
39            let excess = self.messages.len() - self.config.max_messages;
40            self.messages.drain(0..excess);
41
42            // ✅ UPDATE scroll state nach message removal
43            if self.scroll_state.offset > 0 {
44                self.scroll_state.offset = self.scroll_state.offset.saturating_sub(excess);
45            }
46        } else {
47            // ✅ RESERVE mehr Platz falls max_messages erhöht wurde
48            self.messages
49                .reserve(self.config.max_messages.saturating_sub(self.messages.len()));
50        }
51
52        log::debug!(
53            "MessageManager config updated: max_messages = {}, typewriter_delay = {}ms",
54            self.config.max_messages,
55            self.config.typewriter_delay.as_millis()
56        );
57    }
58
59    pub fn clear_messages(&mut self) {
60        self.messages.clear();
61        self.scroll_state.force_auto_scroll();
62    }
63
64    pub fn get_content_height(&self) -> usize {
65        self.messages.len()
66    }
67
68    pub fn get_messages(&self) -> Vec<(&String, usize)> {
69        let (start, end) = self.scroll_state.get_visible_range();
70        let start = start.min(self.messages.len());
71        let end = end.min(self.messages.len());
72
73        if start >= end {
74            return Vec::new();
75        }
76
77        self.messages[start..end]
78            .iter()
79            .map(|msg| (&msg.content, msg.current_length))
80            .collect()
81    }
82
83    pub fn add_message(&mut self, content: String) {
84        // ✅ BUFFER-MANAGEMENT: Entferne alte Messages wenn Buffer voll
85        if self.messages.len() >= self.config.max_messages {
86            self.messages.remove(0);
87            if self.scroll_state.offset > 0 {
88                self.scroll_state.offset = self.scroll_state.offset.saturating_sub(1);
89            }
90        }
91
92        // ✅ TYPEWRITER FIX: Wenn delay = 0, sofort alles anzeigen
93        let initial_length = if self.config.typewriter_delay.as_millis() == 0 {
94            content.graphemes(true).count() // ✅ Komplette Nachricht sofort
95        } else {
96            1 // ✅ Typewriter-Effekt: nur erstes Zeichen
97        };
98
99        self.messages.push(Message {
100            content,
101            current_length: initial_length,
102            timestamp: Instant::now(),
103        });
104
105        // ✅ ERZWINGE Auto-Scroll bei neuer Nachricht
106        self.scroll_state.force_auto_scroll();
107
108        // ✅ UPDATE dimensions mit aktueller window height
109        self.scroll_state
110            .update_dimensions(self.scroll_state.window_height, self.messages.len());
111    }
112
113    pub fn handle_scroll(&mut self, action: KeyAction, window_height: usize) {
114        // ✅ UPDATE dimensions vor dem Scrollen
115        self.scroll_state
116            .update_dimensions(window_height, self.messages.len());
117
118        match action {
119            KeyAction::ScrollUp => {
120                self.scroll_state.scroll_up(1);
121            }
122            KeyAction::ScrollDown => {
123                self.scroll_state.scroll_down(1);
124            }
125            KeyAction::PageUp => {
126                let scroll_amount = window_height.saturating_sub(1);
127                self.scroll_state.scroll_up(scroll_amount);
128            }
129            KeyAction::PageDown => {
130                let scroll_amount = window_height.saturating_sub(1);
131                self.scroll_state.scroll_down(scroll_amount);
132            }
133            _ => {}
134        }
135    }
136
137    pub fn get_visible_messages(&self) -> Vec<(&String, usize)> {
138        self.get_messages()
139    }
140
141    /// ✅ HAUPTFIX: Typewriter Update nur wenn delay > 0
142    pub fn update_typewriter(&mut self) {
143        // ✅ EARLY RETURN: Wenn typewriter_delay = 0, mache nichts
144        if self.config.typewriter_delay.as_millis() == 0 {
145            return;
146        }
147
148        if let Some(last_message) = self.messages.last_mut() {
149            let total_length = last_message.content.graphemes(true).count();
150
151            if last_message.current_length < total_length {
152                let elapsed = last_message.timestamp.elapsed();
153
154                // ✅ ULTRASCHNELL: Mehrere Zeichen pro Update bei sehr niedrigen Delays
155                if elapsed >= self.config.typewriter_delay {
156                    let chars_to_add = if self.config.typewriter_delay.as_millis() <= 5 {
157                        let ratio = elapsed.as_millis() as f64
158                            / self.config.typewriter_delay.as_millis() as f64;
159                        ratio.floor().max(1.0) as usize
160                    } else {
161                        1
162                    };
163
164                    let new_length = (last_message.current_length + chars_to_add).min(total_length);
165                    last_message.current_length = new_length;
166                    last_message.timestamp = Instant::now();
167                }
168            }
169        }
170    }
171
172    pub fn log(&mut self, level: &str, message: &str) {
173        let log_message = format!("[{}] {}", level, message);
174        self.add_message(log_message);
175    }
176}