@@ -209,14 +209,12 @@ impl App {
209209 let prefix = if is_selected { "> " } else { " " } ;
210210 let number = format ! ( "{}. " , i + 1 ) ;
211211
212- // Clean up the proposal text - remove template artifacts
212+ // Clean up any remaining context artifacts for display
213213 let proposal_text = proposal
214- . replace ( "Context statement: " , "" )
215- . replace ( "Another context: " , "" )
216- . replace ( "Third context: " , "" )
217- . replace ( "Context statement - " , "" )
218- . replace ( "Another context - " , "" )
219- . replace ( "Third context - " , "" ) ;
214+ . replace ( "From a scientific perspective, " , "" )
215+ . replace ( "As we explore " , "Exploring " )
216+ . trim ( )
217+ . to_string ( ) ;
220218
221219 let style = if is_selected {
222220 self . theme . ratatui_style ( Element :: Accent )
@@ -460,20 +458,13 @@ impl App {
460458 self . render_coaching_tip_modal ( frame, modal_area) ;
461459 } else if self . mode == AppMode :: Complete {
462460 let content = if let Some ( note) = & self . cloud_response {
463- use ratatui:: text:: { Line , Span } ;
464- let title = note. header_tags . join ( " • " ) ;
465- let title_style = self . theme . ratatui_style ( Element :: Accent ) ;
466- let body_style = self . theme . ratatui_style ( Element :: Text ) ;
467-
468- let text = vec ! [
469- Line :: from( Span :: styled( title, title_style) ) ,
470- Line :: from( "" ) , // Spacer
471- Line :: from( Span :: styled( & note. body_text, body_style) ) ,
472- ] ;
473- Paragraph :: new ( text)
461+ // Clean display - only show the synthesis content, hide system metadata
462+ Paragraph :: new ( note. body_text . as_str ( ) )
463+ . style ( self . theme . ratatui_style ( Element :: Text ) )
474464 } else {
475465 // This case should ideally not be reached if mode is Complete
476466 Paragraph :: new ( "Waiting for synthesis..." )
467+ . style ( self . theme . ratatui_style ( Element :: Text ) )
477468 } ;
478469
479470 let block = Block :: default ( )
@@ -930,12 +921,33 @@ impl App {
930921 } ,
931922 AppMode :: Complete => match key. code {
932923 KeyCode :: Up => {
933- self . synthesis_scroll = self . synthesis_scroll . saturating_sub ( 1 ) ;
924+ // Save synthesis (positive action)
925+ self . save_synthesis ( ) ;
926+ self . mode = AppMode :: Normal ;
927+ self . final_prompt . clear ( ) ;
928+ self . proposals . clear ( ) ;
929+ self . current_proposal_index = 0 ;
930+ self . cloud_response = None ;
931+ self . synthesis_scroll = 0 ;
932+ self . agent_status = AgentStatus :: Ready ;
934933 }
935934 KeyCode :: Down => {
936- self . synthesis_scroll = self . synthesis_scroll . saturating_add ( 1 ) ;
935+ // Discard synthesis (negative action)
936+ self . mode = AppMode :: Chat ; // Start new query
937+ self . final_prompt . clear ( ) ;
938+ self . proposals . clear ( ) ;
939+ self . current_proposal_index = 0 ;
940+ self . cloud_response = None ;
941+ self . synthesis_scroll = 0 ;
942+ self . agent_status = AgentStatus :: Ready ;
943+ self . edit_buffer . clear ( ) ;
944+ }
945+ KeyCode :: Left | KeyCode :: Right => {
946+ // Keep horizontal scrolling for long content
947+ // Currently no horizontal scroll implemented
937948 }
938949 KeyCode :: Enter | KeyCode :: Esc => {
950+ // Fallback: return to normal without saving
939951 self . mode = AppMode :: Normal ;
940952 self . final_prompt . clear ( ) ;
941953 self . proposals . clear ( ) ;
@@ -1006,6 +1018,53 @@ impl App {
10061018 self . edit_buffer . clear ( ) ;
10071019 }
10081020
1021+ fn save_synthesis ( & self ) {
1022+ if let Some ( note) = & self . cloud_response {
1023+ // Generate markdown content with v0.1.0 metadata structure
1024+ let timestamp = chrono:: Utc :: now ( ) . format ( "%Y-%m-%d_%H-%M-%S" ) . to_string ( ) ;
1025+ let filename = format ! ( "synthesis_{}.md" , timestamp) ;
1026+
1027+ // Get the selected proposal text
1028+ let proposal_text = if !self . proposals . is_empty ( )
1029+ && self . current_proposal_index < self . proposals . len ( )
1030+ {
1031+ & self . proposals [ self . current_proposal_index ]
1032+ } else {
1033+ "No proposal available"
1034+ } ;
1035+
1036+ // Use proposal text directly since the new prompt ensures proper format
1037+ let clean_proposal = proposal_text;
1038+
1039+ let markdown_content = format ! (
1040+ "---\n date: {}\n provider: \" OPENROUTER\" \n model: \" {}\" \n query: \" {}\" \n proposal: \" {}\" \n tags: [{}]\n ---\n \n # {}\n \n {}\n " ,
1041+ chrono:: Utc :: now( ) . format( "%Y-%m-%d %H:%M:%S UTC" ) ,
1042+ if self . settings. cloud_model. is_empty( ) || self . settings. cloud_model == "[SELECT]" {
1043+ "anthropic/claude-3.5-sonnet"
1044+ } else {
1045+ & self . settings. cloud_model
1046+ } ,
1047+ self . final_prompt. replace( "\" " , "\\ \" " ) ,
1048+ clean_proposal. replace( "\" " , "\\ \" " ) ,
1049+ note. header_tags. iter( ) . map( |tag| format!( "\" {}\" " , tag) ) . collect:: <Vec <_>>( ) . join( ", " ) ,
1050+ note. header_tags. join( " • " ) ,
1051+ note. body_text
1052+ ) ;
1053+
1054+ // Create Documents/ruixen directory if it doesn't exist
1055+ let home_dir = std:: env:: var ( "HOME" ) . unwrap_or_else ( |_| "." . to_string ( ) ) ;
1056+ let save_dir = format ! ( "{}/Documents/ruixen" , home_dir) ;
1057+ if std:: fs:: create_dir_all ( & save_dir) . is_err ( ) {
1058+ // Silent fallback - don't crash the app
1059+ return ;
1060+ }
1061+
1062+ let filepath = format ! ( "{}/{}" , save_dir, filename) ;
1063+ // Silent save - don't print debug logs that crash the TUI
1064+ let _ = std:: fs:: write ( & filepath, markdown_content) ;
1065+ }
1066+ }
1067+
10091068 fn handle_cloud_synthesis ( & mut self ) {
10101069 // Set status to searching and trigger cloud API call
10111070 self . agent_status = AgentStatus :: Searching ;
0 commit comments