refactor(cli): polish secondary commands with icons, number formatting, and section dividers
Phase 6 of the UX overhaul. Applies consistent visual treatment across
the remaining command outputs: stats, doctor, timeline, who, count,
and drift.
Stats (stats.rs):
- Apply render::format_number() to all numeric values (documents,
FTS indexed, embedding counts, chunks) for thousand-separator
formatting in large databases
Doctor (doctor.rs):
- Replace Unicode check/warning/cross symbols with Icons::success(),
Icons::warning(), Icons::error() for glyph-mode awareness
- Add summary line after checks showing "Ready/Not ready" with counts
of passed, warnings, and failed checks separated by middle dots
- Remove "lore doctor" title header for cleaner output
Count (count.rs):
- Right-align numeric values with {:>10} format for columnar output
in count and state breakdown displays
Timeline (timeline.rs):
- Add entity icons (issue/MR) before entity references in event rows
- Refactor format_event_tag to pad plain text before applying style,
preventing ANSI codes from breaking column alignment
- Extract style_padded() helper for width-then-style pattern
Who (who.rs):
- Add Icons::user() before usernames in expert, workload, reviews,
and overlap displays
- Replace manual bold section headers with render::section_divider()
in workload view (Assigned Issues, Authored MRs, Reviewing MRs,
Unresolved Discussions)
Drift (drift.rs):
- Add Icons::error()/success() before drift detection status line
- Replace '#' bar character with Unicode full block for similarity
curve visualization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
committed by
teernisse
parent
d0744039ef
commit
8572f6cc04
@@ -1,4 +1,4 @@
|
||||
use crate::cli::render::{self, Theme};
|
||||
use crate::cli::render::{self, Icons, Theme};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::Config;
|
||||
@@ -202,6 +202,11 @@ pub fn print_timeline(result: &TimelineResult) {
|
||||
fn print_timeline_event(event: &TimelineEvent) {
|
||||
let date = render::format_date(event.timestamp);
|
||||
let tag = format_event_tag(&event.event_type);
|
||||
let entity_icon = match event.entity_type.as_str() {
|
||||
"issue" => Icons::issue_opened(),
|
||||
"merge_request" => Icons::mr_opened(),
|
||||
_ => "",
|
||||
};
|
||||
let entity_ref = format_entity_ref(&event.entity_type, event.entity_iid);
|
||||
let actor = event
|
||||
.actor
|
||||
@@ -211,8 +216,7 @@ fn print_timeline_event(event: &TimelineEvent) {
|
||||
let expanded_marker = if event.is_seed { "" } else { " [expanded]" };
|
||||
|
||||
let summary = render::truncate(&event.summary, 50);
|
||||
let tag_padded = format!("{:<12}", tag);
|
||||
println!("{date} {tag_padded} {entity_ref:7} {summary:50} {actor}{expanded_marker}");
|
||||
println!("{date} {tag} {entity_icon}{entity_ref:7} {summary:50} {actor}{expanded_marker}");
|
||||
|
||||
// Show snippet for evidence notes
|
||||
if let TimelineEventType::NoteEvidence { snippet, .. } = &event.event_type
|
||||
@@ -276,23 +280,33 @@ fn print_timeline_footer(result: &TimelineResult) {
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Format event tag: pad plain text to TAG_WIDTH, then apply style.
|
||||
const TAG_WIDTH: usize = 10;
|
||||
|
||||
fn format_event_tag(event_type: &TimelineEventType) -> String {
|
||||
match event_type {
|
||||
TimelineEventType::Created => Theme::success().render("CREATED"),
|
||||
let (label, style) = match event_type {
|
||||
TimelineEventType::Created => ("CREATED", Theme::success()),
|
||||
TimelineEventType::StateChanged { state } => match state.as_str() {
|
||||
"closed" => Theme::error().render("CLOSED"),
|
||||
"reopened" => Theme::warning().render("REOPENED"),
|
||||
_ => Theme::dim().render(&state.to_uppercase()),
|
||||
"closed" => ("CLOSED", Theme::error()),
|
||||
"reopened" => ("REOPENED", Theme::warning()),
|
||||
_ => return style_padded(&state.to_uppercase(), TAG_WIDTH, Theme::dim()),
|
||||
},
|
||||
TimelineEventType::LabelAdded { .. } => Theme::info().render("LABEL+"),
|
||||
TimelineEventType::LabelRemoved { .. } => Theme::info().render("LABEL-"),
|
||||
TimelineEventType::MilestoneSet { .. } => Theme::accent().render("MILESTONE+"),
|
||||
TimelineEventType::MilestoneRemoved { .. } => Theme::accent().render("MILESTONE-"),
|
||||
TimelineEventType::Merged => Theme::info().render("MERGED"),
|
||||
TimelineEventType::NoteEvidence { .. } => Theme::dim().render("NOTE"),
|
||||
TimelineEventType::DiscussionThread { .. } => Theme::warning().render("THREAD"),
|
||||
TimelineEventType::CrossReferenced { .. } => Theme::dim().render("REF"),
|
||||
}
|
||||
TimelineEventType::LabelAdded { .. } => ("LABEL+", Theme::info()),
|
||||
TimelineEventType::LabelRemoved { .. } => ("LABEL-", Theme::info()),
|
||||
TimelineEventType::MilestoneSet { .. } => ("MILESTONE+", Theme::accent()),
|
||||
TimelineEventType::MilestoneRemoved { .. } => ("MILESTONE-", Theme::accent()),
|
||||
TimelineEventType::Merged => ("MERGED", Theme::info()),
|
||||
TimelineEventType::NoteEvidence { .. } => ("NOTE", Theme::dim()),
|
||||
TimelineEventType::DiscussionThread { .. } => ("THREAD", Theme::warning()),
|
||||
TimelineEventType::CrossReferenced { .. } => ("REF", Theme::dim()),
|
||||
};
|
||||
style_padded(label, TAG_WIDTH, style)
|
||||
}
|
||||
|
||||
/// Pad text to width, then apply lipgloss style (so ANSI codes don't break alignment).
|
||||
fn style_padded(text: &str, width: usize, style: lipgloss::Style) -> String {
|
||||
let padded = format!("{:<width$}", text);
|
||||
style.render(&padded)
|
||||
}
|
||||
|
||||
fn format_entity_ref(entity_type: &str, iid: i64) -> String {
|
||||
|
||||
Reference in New Issue
Block a user