rush_sync_server/output/
message.rs1use 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, pub scroll_state: ScrollState,
20}
21
22impl MessageManager {
23 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(), scroll_state,
30 }
31 }
32
33 pub fn update_config(&mut self, new_config: &Config) {
35 self.config = new_config.clone();
36
37 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 if self.scroll_state.offset > 0 {
44 self.scroll_state.offset = self.scroll_state.offset.saturating_sub(excess);
45 }
46 } else {
47 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 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 let initial_length = if self.config.typewriter_delay.as_millis() == 0 {
94 content.graphemes(true).count() } else {
96 1 };
98
99 self.messages.push(Message {
100 content,
101 current_length: initial_length,
102 timestamp: Instant::now(),
103 });
104
105 self.scroll_state.force_auto_scroll();
107
108 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 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 pub fn update_typewriter(&mut self) {
143 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 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}