Skip to content

Commit 6f9f29f

Browse files
committed
fix(welcome): style section headers
1 parent 8e92b1e commit 6f9f29f

File tree

1 file changed

+76
-10
lines changed

1 file changed

+76
-10
lines changed

src/agent/runloop/welcome.rs

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use vtcode_core::config::loader::VTCodeConfig;
1313
use vtcode_core::config::types::AgentConfig as CoreAgentConfig;
1414
use vtcode_core::project_doc;
1515
use vtcode_core::ui::slash::SLASH_COMMANDS;
16+
use vtcode_core::ui::styled::Styles;
17+
use vtcode_core::ui::theme;
1618
use 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+
326333
fn 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>) {
443451
enum SectionSpacing {
444452
Normal,
445453
Compact,
454+
Flush,
446455
}
447456

448457
struct 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+
459478
fn 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\nKey Guidelines"));
634+
assert!(!plain.contains("\n\nKeyboard 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\nKeyboard 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

Comments
 (0)