From 96b288ccdd23fae2a82d5ac2ffd929e25f464e16 Mon Sep 17 00:00:00 2001 From: Taylor Eernisse Date: Sat, 14 Feb 2026 09:59:09 -0500 Subject: [PATCH] refactor(search): polish search results rendering with semantic Theme styles Phase 5 of the UX overhaul. Migrates search result display from raw console styling to the centralized Theme system with semantic methods, improving visual consistency and readability. Search result changes: - Type badges now use semantic styles (issue_ref, mr_ref) with fixed-width alignment for clean columnar layout - Snippet rendering uses Theme::highlight() for matched terms and Theme::muted() for surrounding context, replacing bold+underline - Metadata line uses Theme::username() for authors and per-part styling with middle-dot separators instead of a single dim line - Result numbering uses muted style with right-aligned width - Consistent 8-space indent for metadata, snippets, and explain lines - Header line uses muted style for search mode instead of dim+parens - Trailing blank line moved after the result loop instead of per-result Co-Authored-By: Claude Opus 4.6 --- src/cli/commands/search.rs | 51 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/cli/commands/search.rs b/src/cli/commands/search.rs index e7dc8b5..c612d8f 100644 --- a/src/cli/commands/search.rs +++ b/src/cli/commands/search.rs @@ -309,20 +309,20 @@ fn parse_json_array(json: &str) -> Vec { .collect() } -/// Render FTS snippet with `` tags as terminal bold+underline. +/// Render FTS snippet with `` tags as terminal highlight style. fn render_snippet(snippet: &str) -> String { let mut result = String::new(); let mut remaining = snippet; while let Some(start) = remaining.find("") { - result.push_str(&remaining[..start]); + result.push_str(&Theme::muted().render(&remaining[..start])); remaining = &remaining[start + 6..]; if let Some(end) = remaining.find("") { let highlighted = &remaining[..end]; - result.push_str(&Theme::bold().underline().render(highlighted)); + result.push_str(&Theme::highlight().render(highlighted)); remaining = &remaining[end + 7..]; } } - result.push_str(remaining); + result.push_str(&Theme::muted().render(remaining)); result } @@ -342,35 +342,37 @@ pub fn print_search_results(response: &SearchResponse) { } println!( - "\n {} results for '{}' {}", + "\n {} results for '{}' {}", Theme::bold().render(&response.total_results.to_string()), Theme::bold().render(&response.query), - Theme::dim().render(&format!("({})", response.mode)) + Theme::muted().render(&response.mode) ); - println!(); for (i, result) in response.results.iter().enumerate() { + println!(); + let type_badge = match result.source_type.as_str() { - "issue" => Theme::info().render("issue"), - "merge_request" => Theme::accent().render("mr"), - "discussion" => Theme::info().render("disc"), - "note" => Theme::info().render("note"), - _ => Theme::dim().render(&result.source_type), + "issue" => Theme::issue_ref().render("issue"), + "merge_request" => Theme::mr_ref().render(" mr "), + "discussion" => Theme::info().render(" disc"), + "note" => Theme::muted().render(" note"), + _ => Theme::muted().render(&format!("{:>5}", &result.source_type)), }; // Title line: rank, type badge, title println!( - " {} {} {}", - Theme::dim().render(&format!("{:>2}.", i + 1)), + " {:>3}. {} {}", + Theme::muted().render(&(i + 1).to_string()), type_badge, Theme::bold().render(&result.title) ); // Metadata: project, author, labels — compact middle-dot line + let sep = Theme::muted().render(" \u{b7} "); let mut meta_parts: Vec = Vec::new(); - meta_parts.push(result.project_path.clone()); + meta_parts.push(Theme::muted().render(&result.project_path)); if let Some(ref author) = result.author { - meta_parts.push(format!("@{author}")); + meta_parts.push(Theme::username().render(&format!("@{author}"))); } if !result.labels.is_empty() { let label_str = if result.labels.len() <= 3 { @@ -382,20 +384,17 @@ pub fn print_search_results(response: &SearchResponse) { result.labels.len() - 2 ) }; - meta_parts.push(label_str); + meta_parts.push(Theme::muted().render(&label_str)); } - println!( - " {}", - Theme::dim().render(&meta_parts.join(" \u{b7} ")) - ); + println!(" {}", meta_parts.join(&sep)); - // Snippet with proper highlighting + // Snippet with highlight styling let rendered = render_snippet(&result.snippet); - println!(" {}", Theme::dim().render(&rendered)); + println!(" {rendered}"); if let Some(ref explain) = result.explain { println!( - " {} vec={} fts={} rrf={:.4}", + " {} vec={} fts={} rrf={:.4}", Theme::accent().render("explain"), explain .vector_rank @@ -408,9 +407,9 @@ pub fn print_search_results(response: &SearchResponse) { explain.rrf_score ); } - - println!(); } + + println!(); } #[derive(Serialize)]