@@ -13,6 +13,8 @@ use vtcode_core::config::loader::VTCodeConfig;
1313use vtcode_core:: config:: types:: AgentConfig as CoreAgentConfig ;
1414use vtcode_core:: project_doc;
1515use vtcode_core:: ui:: slash:: SLASH_COMMANDS ;
16+ use vtcode_core:: ui:: styled:: Styles ;
17+ use vtcode_core:: ui:: theme;
1618use vtcode_core:: utils:: utils:: {
1719 ProjectOverview , build_project_overview, summarize_workspace_languages,
1820} ;
@@ -177,9 +179,9 @@ fn render_welcome_text(
177179 . collect ( ) ;
178180 add_section (
179181 & mut sections,
180- "Key Guidelines" ,
182+ style_section_title ( "Key Guidelines" ) ,
181183 details,
182- SectionSpacing :: Normal ,
184+ SectionSpacing :: Flush ,
183185 ) ;
184186 }
185187
@@ -206,9 +208,7 @@ fn render_welcome_text(
206208
207209 let mut previous_spacing: Option < SectionSpacing > = None ;
208210 for section in sections {
209- let compact_pair = previous_spacing == Some ( SectionSpacing :: Compact )
210- && section. spacing == SectionSpacing :: Compact ;
211- if !lines. is_empty ( ) && !compact_pair {
211+ if !lines. is_empty ( ) && section. spacing . needs_blank_line ( previous_spacing) {
212212 lines. push ( String :: new ( ) ) ;
213213 }
214214 lines. extend ( section. lines ) ;
@@ -323,16 +323,24 @@ fn collect_non_empty_entries(items: &[String]) -> Vec<&str> {
323323 . collect ( )
324324}
325325
326+ fn style_section_title ( title : & str ) -> String {
327+ let primary = theme:: active_styles ( ) . primary . bold ( ) ;
328+ let prefix = Styles :: render ( & primary) ;
329+ let reset = Styles :: render_reset ( ) ;
330+ format ! ( "{prefix}{title}{reset}" )
331+ }
332+
326333fn add_section (
327334 sections : & mut Vec < SectionBlock > ,
328- title : & str ,
335+ title : impl Into < String > ,
329336 body : Vec < String > ,
330337 spacing : SectionSpacing ,
331338) {
332339 if body. is_empty ( ) {
333340 return ;
334341 }
335342
343+ let title = title. into ( ) ;
336344 let mut section = Vec :: with_capacity ( body. len ( ) + 1 ) ;
337345 section. push ( title. to_string ( ) ) ;
338346 section. extend ( body) ;
@@ -386,9 +394,9 @@ fn add_keyboard_shortcut_section(sections: &mut Vec<SectionBlock>) {
386394
387395 add_section (
388396 sections,
389- ui_constants:: WELCOME_SHORTCUT_SECTION_TITLE ,
397+ style_section_title ( ui_constants:: WELCOME_SHORTCUT_SECTION_TITLE ) ,
390398 entries,
391- SectionSpacing :: Compact ,
399+ SectionSpacing :: Flush ,
392400 ) ;
393401}
394402
@@ -443,6 +451,7 @@ fn add_slash_command_section(sections: &mut Vec<SectionBlock>) {
443451enum SectionSpacing {
444452 Normal ,
445453 Compact ,
454+ Flush ,
446455}
447456
448457struct SectionBlock {
@@ -456,6 +465,16 @@ impl SectionBlock {
456465 }
457466}
458467
468+ impl SectionSpacing {
469+ fn needs_blank_line ( self , previous : Option < Self > ) -> bool {
470+ match self {
471+ SectionSpacing :: Normal => true ,
472+ SectionSpacing :: Compact => previous != Some ( SectionSpacing :: Compact ) ,
473+ SectionSpacing :: Flush => false ,
474+ }
475+ }
476+ }
477+
459478fn compute_update_notice ( ) -> Option < String > {
460479 if !should_check_for_updates ( ) {
461480 return None ;
@@ -505,6 +524,28 @@ mod tests {
505524 use vtcode_core:: config:: types:: {
506525 ModelSelectionSource , ReasoningEffortLevel , UiSurfacePreference ,
507526 } ;
527+ use vtcode_core:: ui:: { styled:: Styles , theme} ;
528+
529+ fn strip_ansi_codes ( input : & str ) -> String {
530+ let mut output = String :: with_capacity ( input. len ( ) ) ;
531+ let mut chars = input. chars ( ) ;
532+ while let Some ( ch) = chars. next ( ) {
533+ if ch == '\u{1b}' {
534+ if let Some ( next) = chars. next ( ) {
535+ if next == '[' {
536+ while let Some ( terminator) = chars. next ( ) {
537+ if ( '@' ..='~' ) . contains ( & terminator) {
538+ break ;
539+ }
540+ }
541+ continue ;
542+ }
543+ }
544+ }
545+ output. push ( ch) ;
546+ }
547+ output
548+ }
508549
509550 #[ test]
510551 fn test_prepare_session_bootstrap_builds_sections ( ) {
@@ -560,11 +601,24 @@ mod tests {
560601 let bootstrap = prepare_session_bootstrap ( & runtime_cfg, Some ( & vt_cfg) , None ) ;
561602
562603 let welcome = bootstrap. welcome_text . expect ( "welcome text" ) ;
604+ let plain = strip_ansi_codes ( & welcome) ;
605+ let styled_title = theme:: active_styles ( ) . primary . bold ( ) ;
606+ let prefix = Styles :: render ( & styled_title) ;
607+ let reset = Styles :: render_reset ( ) ;
608+ let styled_guidelines = format ! ( "{prefix}Key Guidelines{reset}" ) ;
609+ let styled_shortcuts = format ! (
610+ "{prefix}{}{reset}" ,
611+ ui_constants:: WELCOME_SHORTCUT_SECTION_TITLE
612+ ) ;
613+
563614 assert ! ( welcome. contains( "**Project Overview**" ) ) ;
564615 assert ! ( welcome. contains( "**Project:" ) ) ;
565616 assert ! ( welcome. contains( "Tip one" ) ) ;
566617 assert ! ( welcome. contains( "Follow workspace guidelines" ) ) ;
567- assert ! ( welcome. contains( "Keyboard Shortcuts" ) ) ;
618+ assert ! ( welcome. contains( & styled_guidelines) ) ;
619+ assert ! ( welcome. contains( & styled_shortcuts) ) ;
620+ assert ! ( plain. contains( "Keyboard Shortcuts" ) ) ;
621+ assert ! ( plain. contains( "Key Guidelines" ) ) ;
568622 assert ! ( welcome. contains( "Slash Commands" ) ) ;
569623 assert ! ( welcome. contains( ui_constants:: WELCOME_SLASH_COMMAND_INTRO ) ) ;
570624 assert ! ( welcome. contains( & format!(
@@ -576,6 +630,8 @@ mod tests {
576630 "{}Ctrl+Enter" ,
577631 ui_constants:: WELCOME_SHORTCUT_INDENT
578632 ) ) ) ;
633+ assert ! ( !plain. contains( "\n \n Key Guidelines" ) ) ;
634+ assert ! ( !plain. contains( "\n \n Keyboard Shortcuts" ) ) ;
579635
580636 let prompt = bootstrap. prompt_addendum . expect ( "prompt addendum" ) ;
581637 assert ! ( prompt. contains( "## SESSION CONTEXT" ) ) ;
@@ -628,10 +684,20 @@ mod tests {
628684 let vt_cfg = VTCodeConfig :: default ( ) ;
629685 let bootstrap = prepare_session_bootstrap ( & runtime_cfg, Some ( & vt_cfg) , None ) ;
630686 let welcome = bootstrap. welcome_text . expect ( "welcome text" ) ;
687+ let plain = strip_ansi_codes ( & welcome) ;
688+ let styled_title = theme:: active_styles ( ) . primary . bold ( ) ;
689+ let prefix = Styles :: render ( & styled_title) ;
690+ let reset = Styles :: render_reset ( ) ;
691+ let styled_shortcuts = format ! (
692+ "{prefix}{}{reset}" ,
693+ ui_constants:: WELCOME_SHORTCUT_SECTION_TITLE
694+ ) ;
631695
632696 assert ! ( !welcome. contains( "Usage Tips" ) ) ;
633697 assert ! ( !welcome. contains( "Suggested Next Actions" ) ) ;
634- assert ! ( welcome. contains( "Keyboard Shortcuts" ) ) ;
698+ assert ! ( welcome. contains( & styled_shortcuts) ) ;
699+ assert ! ( plain. contains( "Keyboard Shortcuts" ) ) ;
700+ assert ! ( !plain. contains( "\n \n Keyboard Shortcuts" ) ) ;
635701 assert ! ( welcome. contains( "Slash Commands" ) ) ;
636702 assert ! ( welcome. contains( ui_constants:: WELCOME_SLASH_COMMAND_INTRO ) ) ;
637703 assert ! ( welcome. contains( & format!(
0 commit comments