@@ -7,34 +7,86 @@ use ratatui::{
77} ;
88use crate :: app:: App ;
99
10- // Helper function to wrap text to fit within a given width
10+ // Enhanced helper function to wrap text to fit within a given width
1111fn wrap_text ( text : & str , max_width : usize ) -> Vec < String > {
12- let words: Vec < & str > = text. split_whitespace ( ) . collect ( ) ;
12+ if max_width == 0 {
13+ return vec ! [ text. to_string( ) ] ;
14+ }
15+
1316 let mut lines = Vec :: new ( ) ;
1417 let mut current_line = String :: new ( ) ;
15-
16- for word in words {
17- if current_line. len ( ) + word. len ( ) + 1 <= max_width {
18- if !current_line. is_empty ( ) {
19- current_line. push ( ' ' ) ;
20- }
21- current_line. push_str ( word) ;
22- } else {
18+
19+ // Handle empty text
20+ if text. trim ( ) . is_empty ( ) {
21+ return vec ! [ text. to_string( ) ] ;
22+ }
23+
24+ for line in text. lines ( ) {
25+ if line. trim ( ) . is_empty ( ) {
26+ // Handle empty lines by preserving them
2327 if !current_line. is_empty ( ) {
2428 lines. push ( current_line. clone ( ) ) ;
29+ current_line. clear ( ) ;
30+ }
31+ lines. push ( String :: new ( ) ) ;
32+ continue ;
33+ }
34+
35+ let words: Vec < & str > = line. split_whitespace ( ) . collect ( ) ;
36+
37+ for word in words {
38+ // Handle words longer than max_width by breaking them
39+ if word. len ( ) > max_width {
40+ if !current_line. is_empty ( ) {
41+ lines. push ( current_line. clone ( ) ) ;
42+ current_line. clear ( ) ;
43+ }
44+ // Break long word into chunks
45+ let mut remaining_word = word;
46+ while !remaining_word. is_empty ( ) {
47+ let chunk_size = std:: cmp:: min ( max_width, remaining_word. len ( ) ) ;
48+ let chunk = & remaining_word[ ..chunk_size] ;
49+ lines. push ( chunk. to_string ( ) ) ;
50+ remaining_word = & remaining_word[ chunk_size..] ;
51+ }
52+ continue ;
53+ }
54+
55+ // Check if word fits on current line
56+ let space_needed = if current_line. is_empty ( ) { word. len ( ) } else { current_line. len ( ) + 1 + word. len ( ) } ;
57+
58+ if space_needed <= max_width {
59+ if !current_line. is_empty ( ) {
60+ current_line. push ( ' ' ) ;
61+ }
62+ current_line. push_str ( word) ;
63+ } else {
64+ // Word doesn't fit, start new line
65+ if !current_line. is_empty ( ) {
66+ lines. push ( current_line. clone ( ) ) ;
67+ current_line. clear ( ) ;
68+ }
69+ current_line. push_str ( word) ;
2570 }
26- current_line = word. to_string ( ) ;
71+ }
72+
73+ // Add the current line if it's not empty
74+ if !current_line. is_empty ( ) {
75+ lines. push ( current_line. clone ( ) ) ;
76+ current_line. clear ( ) ;
2777 }
2878 }
29-
79+
80+ // Handle case where we have pending content
3081 if !current_line. is_empty ( ) {
3182 lines. push ( current_line) ;
3283 }
33-
84+
85+ // Ensure we always return at least the original text if no wrapping occurred
3486 if lines. is_empty ( ) {
3587 lines. push ( text. to_string ( ) ) ;
3688 }
37-
89+
3890 lines
3991}
4092
@@ -152,8 +204,19 @@ pub fn ui(f: &mut Frame, app: &App) {
152204 . position ( tool_scroll_position) ;
153205 f. render_stateful_widget ( tool_scrollbar, chunks[ 1 ] , & mut tool_scrollbar_state) ;
154206
207+ // Enhanced input field with text wrapping
155208 let input_block = Block :: default ( ) . title ( "Input" ) . borders ( Borders :: ALL ) ;
156- let input = Paragraph :: new ( app. user_input . as_str ( ) )
209+ let input_max_width = chunks[ 2 ] . width . saturating_sub ( 4 ) as usize ; // Account for borders and padding
210+
211+ // Wrap the input text for better display
212+ let wrapped_input = if app. user_input . is_empty ( ) {
213+ vec ! [ String :: new( ) ]
214+ } else {
215+ wrap_text ( & app. user_input , input_max_width)
216+ } ;
217+
218+ let input_text = wrapped_input. join ( "\n " ) ;
219+ let input = Paragraph :: new ( input_text)
157220 . block ( input_block) ;
158221 f. render_widget ( input, chunks[ 2 ] ) ;
159222
0 commit comments