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<'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 let initial_length = if self.config.typewriter_delay.as_millis() == 0 {
66 content.graphemes(true).count() } else {
68 1 };
70
71 self.messages.push(Message {
72 content,
73 current_length: initial_length,
74 timestamp: Instant::now(),
75 });
76
77 self.scroll_state.force_auto_scroll();
79
80 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 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 pub fn update_typewriter(&mut self) {
115 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 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}