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
@@ -333,7 +333,7 @@ pub fn print_count(result: &CountResult) {
|
|||||||
|
|
||||||
if let Some(system_count) = result.system_count {
|
if let Some(system_count) = result.system_count {
|
||||||
println!(
|
println!(
|
||||||
"{}: {} {}",
|
"{}: {:>10} {}",
|
||||||
Theme::info().render(&result.entity),
|
Theme::info().render(&result.entity),
|
||||||
Theme::bold().render(&count_str),
|
Theme::bold().render(&count_str),
|
||||||
Theme::dim().render(&format!(
|
Theme::dim().render(&format!(
|
||||||
@@ -343,22 +343,22 @@ pub fn print_count(result: &CountResult) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"{}: {}",
|
"{}: {:>10}",
|
||||||
Theme::info().render(&result.entity),
|
Theme::info().render(&result.entity),
|
||||||
Theme::bold().render(&count_str)
|
Theme::bold().render(&count_str)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(breakdown) = &result.state_breakdown {
|
if let Some(breakdown) = &result.state_breakdown {
|
||||||
println!(" opened: {}", render::format_number(breakdown.opened));
|
println!(" opened: {:>10}", render::format_number(breakdown.opened));
|
||||||
if let Some(merged) = breakdown.merged {
|
if let Some(merged) = breakdown.merged {
|
||||||
println!(" merged: {}", render::format_number(merged));
|
println!(" merged: {:>10}", render::format_number(merged));
|
||||||
}
|
}
|
||||||
println!(" closed: {}", render::format_number(breakdown.closed));
|
println!(" closed: {:>10}", render::format_number(breakdown.closed));
|
||||||
if let Some(locked) = breakdown.locked
|
if let Some(locked) = breakdown.locked
|
||||||
&& locked > 0
|
&& locked > 0
|
||||||
{
|
{
|
||||||
println!(" locked: {}", render::format_number(locked));
|
println!(" locked: {:>10}", render::format_number(locked));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::cli::render::Theme;
|
use crate::cli::render::{Icons, Theme};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::core::config::Config;
|
use crate::core::config::Config;
|
||||||
@@ -530,7 +530,7 @@ fn check_logging(config: Option<&Config>) -> LoggingCheck {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_doctor_results(result: &DoctorResult) {
|
pub fn print_doctor_results(result: &DoctorResult) {
|
||||||
println!("\nlore doctor\n");
|
println!();
|
||||||
|
|
||||||
print_check("Config", &result.checks.config.result);
|
print_check("Config", &result.checks.config.result);
|
||||||
print_check("Database", &result.checks.database.result);
|
print_check("Database", &result.checks.database.result);
|
||||||
@@ -539,31 +539,53 @@ pub fn print_doctor_results(result: &DoctorResult) {
|
|||||||
print_check("Ollama", &result.checks.ollama.result);
|
print_check("Ollama", &result.checks.ollama.result);
|
||||||
print_check("Logging", &result.checks.logging.result);
|
print_check("Logging", &result.checks.logging.result);
|
||||||
|
|
||||||
|
// Count statuses
|
||||||
|
let checks = [
|
||||||
|
&result.checks.config.result,
|
||||||
|
&result.checks.database.result,
|
||||||
|
&result.checks.gitlab.result,
|
||||||
|
&result.checks.projects.result,
|
||||||
|
&result.checks.ollama.result,
|
||||||
|
&result.checks.logging.result,
|
||||||
|
];
|
||||||
|
let passed = checks
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.status == CheckStatus::Ok)
|
||||||
|
.count();
|
||||||
|
let warnings = checks
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.status == CheckStatus::Warning)
|
||||||
|
.count();
|
||||||
|
let failed = checks
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.status == CheckStatus::Error)
|
||||||
|
.count();
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
|
let mut summary_parts = Vec::new();
|
||||||
if result.success {
|
if result.success {
|
||||||
let ollama_ok = result.checks.ollama.result.status == CheckStatus::Ok;
|
summary_parts.push(Theme::success().render("Ready"));
|
||||||
if ollama_ok {
|
|
||||||
println!("{}", Theme::success().render("Status: Ready"));
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"{} {}",
|
|
||||||
Theme::success().render("Status: Ready"),
|
|
||||||
Theme::warning()
|
|
||||||
.render("(lexical search available, semantic search requires Ollama)")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
println!("{}", Theme::error().render("Status: Not ready"));
|
summary_parts.push(Theme::error().render("Not ready"));
|
||||||
}
|
}
|
||||||
|
summary_parts.push(format!("{passed} passed"));
|
||||||
|
if warnings > 0 {
|
||||||
|
summary_parts.push(Theme::warning().render(&format!("{warnings} warning")));
|
||||||
|
}
|
||||||
|
if failed > 0 {
|
||||||
|
summary_parts.push(Theme::error().render(&format!("{failed} failed")));
|
||||||
|
}
|
||||||
|
println!(" {}", summary_parts.join(" \u{b7} "));
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_check(name: &str, result: &CheckResult) {
|
fn print_check(name: &str, result: &CheckResult) {
|
||||||
let symbol = match result.status {
|
let icon = match result.status {
|
||||||
CheckStatus::Ok => Theme::success().render("\u{2713}"),
|
CheckStatus::Ok => Theme::success().render(Icons::success()),
|
||||||
CheckStatus::Warning => Theme::warning().render("\u{26a0}"),
|
CheckStatus::Warning => Theme::warning().render(Icons::warning()),
|
||||||
CheckStatus::Error => Theme::error().render("\u{2717}"),
|
CheckStatus::Error => Theme::error().render(Icons::error()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let message = result.message.as_deref().unwrap_or("");
|
let message = result.message.as_deref().unwrap_or("");
|
||||||
@@ -573,5 +595,5 @@ fn print_check(name: &str, result: &CheckResult) {
|
|||||||
CheckStatus::Error => Theme::error().render(message),
|
CheckStatus::Error => Theme::error().render(message),
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(" {symbol} {:<10} {message_styled}", name);
|
println!(" {icon} {:<10} {message_styled}", name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::sync::LazyLock;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::cli::render::Theme;
|
use crate::cli::render::{Icons, Theme};
|
||||||
use crate::cli::robot::RobotMeta;
|
use crate::cli::robot::RobotMeta;
|
||||||
use crate::core::config::Config;
|
use crate::core::config::Config;
|
||||||
use crate::core::db::create_connection;
|
use crate::core::db::create_connection;
|
||||||
@@ -428,7 +428,11 @@ pub fn print_drift_human(response: &DriftResponse) {
|
|||||||
println!();
|
println!();
|
||||||
|
|
||||||
if response.drift_detected {
|
if response.drift_detected {
|
||||||
println!("{}", Theme::error().bold().render("DRIFT DETECTED"));
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
Theme::error().render(Icons::error()),
|
||||||
|
Theme::error().bold().render("DRIFT DETECTED")
|
||||||
|
);
|
||||||
if let Some(dp) = &response.drift_point {
|
if let Some(dp) = &response.drift_point {
|
||||||
println!(
|
println!(
|
||||||
" At note #{} by @{} ({}) - similarity {:.2}",
|
" At note #{} by @{} ({}) - similarity {:.2}",
|
||||||
@@ -439,7 +443,11 @@ pub fn print_drift_human(response: &DriftResponse) {
|
|||||||
println!(" Topics: {}", response.drift_topics.join(", "));
|
println!(" Topics: {}", response.drift_topics.join(", "));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("{}", Theme::success().render("No drift detected"));
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
Theme::success().render(Icons::success()),
|
||||||
|
Theme::success().render("No drift detected")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
@@ -450,7 +458,7 @@ pub fn print_drift_human(response: &DriftResponse) {
|
|||||||
println!("{}", Theme::bold().render("Similarity Curve:"));
|
println!("{}", Theme::bold().render("Similarity Curve:"));
|
||||||
for pt in &response.similarity_curve {
|
for pt in &response.similarity_curve {
|
||||||
let bar_len = ((pt.similarity.max(0.0)) * 30.0) as usize;
|
let bar_len = ((pt.similarity.max(0.0)) * 30.0) as usize;
|
||||||
let bar: String = "#".repeat(bar_len);
|
let bar: String = "\u{2588}".repeat(bar_len);
|
||||||
println!(
|
println!(
|
||||||
" {:>3} {:.2} {} @{}",
|
" {:>3} {:.2} {} @{}",
|
||||||
pt.note_index, pt.similarity, bar, pt.author
|
pt.note_index, pt.similarity, bar, pt.author
|
||||||
|
|||||||
@@ -328,26 +328,44 @@ fn section(title: &str) {
|
|||||||
|
|
||||||
pub fn print_stats(result: &StatsResult) {
|
pub fn print_stats(result: &StatsResult) {
|
||||||
section("Documents");
|
section("Documents");
|
||||||
let mut parts = vec![format!("{} total", result.documents.total)];
|
let mut parts = vec![format!(
|
||||||
|
"{} total",
|
||||||
|
render::format_number(result.documents.total)
|
||||||
|
)];
|
||||||
if result.documents.issues > 0 {
|
if result.documents.issues > 0 {
|
||||||
parts.push(format!("{} issues", result.documents.issues));
|
parts.push(format!(
|
||||||
|
"{} issues",
|
||||||
|
render::format_number(result.documents.issues)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if result.documents.merge_requests > 0 {
|
if result.documents.merge_requests > 0 {
|
||||||
parts.push(format!("{} MRs", result.documents.merge_requests));
|
parts.push(format!(
|
||||||
|
"{} MRs",
|
||||||
|
render::format_number(result.documents.merge_requests)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if result.documents.discussions > 0 {
|
if result.documents.discussions > 0 {
|
||||||
parts.push(format!("{} discussions", result.documents.discussions));
|
parts.push(format!(
|
||||||
|
"{} discussions",
|
||||||
|
render::format_number(result.documents.discussions)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
println!(" {}", parts.join(" \u{b7} "));
|
println!(" {}", parts.join(" \u{b7} "));
|
||||||
if result.documents.truncated > 0 {
|
if result.documents.truncated > 0 {
|
||||||
println!(
|
println!(
|
||||||
" {}",
|
" {}",
|
||||||
Theme::warning().render(&format!("{} truncated", result.documents.truncated))
|
Theme::warning().render(&format!(
|
||||||
|
"{} truncated",
|
||||||
|
render::format_number(result.documents.truncated)
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
section("Search Index");
|
section("Search Index");
|
||||||
println!(" {} FTS indexed", result.fts.indexed);
|
println!(
|
||||||
|
" {} FTS indexed",
|
||||||
|
render::format_number(result.fts.indexed)
|
||||||
|
);
|
||||||
let coverage_color = if result.embeddings.coverage_pct >= 95.0 {
|
let coverage_color = if result.embeddings.coverage_pct >= 95.0 {
|
||||||
Theme::success().render(&format!("{:.0}%", result.embeddings.coverage_pct))
|
Theme::success().render(&format!("{:.0}%", result.embeddings.coverage_pct))
|
||||||
} else if result.embeddings.coverage_pct >= 50.0 {
|
} else if result.embeddings.coverage_pct >= 50.0 {
|
||||||
@@ -357,12 +375,17 @@ pub fn print_stats(result: &StatsResult) {
|
|||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
" {} embedding coverage ({}/{})",
|
" {} embedding coverage ({}/{})",
|
||||||
coverage_color, result.embeddings.embedded_documents, result.documents.total,
|
coverage_color,
|
||||||
|
render::format_number(result.embeddings.embedded_documents),
|
||||||
|
render::format_number(result.documents.total),
|
||||||
);
|
);
|
||||||
if result.embeddings.total_chunks > 0 {
|
if result.embeddings.total_chunks > 0 {
|
||||||
println!(
|
println!(
|
||||||
" {}",
|
" {}",
|
||||||
Theme::dim().render(&format!("{} chunks", result.embeddings.total_chunks))
|
Theme::dim().render(&format!(
|
||||||
|
"{} chunks",
|
||||||
|
render::format_number(result.embeddings.total_chunks)
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::cli::render::{self, Theme};
|
use crate::cli::render::{self, Icons, Theme};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
@@ -202,6 +202,11 @@ pub fn print_timeline(result: &TimelineResult) {
|
|||||||
fn print_timeline_event(event: &TimelineEvent) {
|
fn print_timeline_event(event: &TimelineEvent) {
|
||||||
let date = render::format_date(event.timestamp);
|
let date = render::format_date(event.timestamp);
|
||||||
let tag = format_event_tag(&event.event_type);
|
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 entity_ref = format_entity_ref(&event.entity_type, event.entity_iid);
|
||||||
let actor = event
|
let actor = event
|
||||||
.actor
|
.actor
|
||||||
@@ -211,8 +216,7 @@ fn print_timeline_event(event: &TimelineEvent) {
|
|||||||
let expanded_marker = if event.is_seed { "" } else { " [expanded]" };
|
let expanded_marker = if event.is_seed { "" } else { " [expanded]" };
|
||||||
|
|
||||||
let summary = render::truncate(&event.summary, 50);
|
let summary = render::truncate(&event.summary, 50);
|
||||||
let tag_padded = format!("{:<12}", tag);
|
println!("{date} {tag} {entity_icon}{entity_ref:7} {summary:50} {actor}{expanded_marker}");
|
||||||
println!("{date} {tag_padded} {entity_ref:7} {summary:50} {actor}{expanded_marker}");
|
|
||||||
|
|
||||||
// Show snippet for evidence notes
|
// Show snippet for evidence notes
|
||||||
if let TimelineEventType::NoteEvidence { snippet, .. } = &event.event_type
|
if let TimelineEventType::NoteEvidence { snippet, .. } = &event.event_type
|
||||||
@@ -276,23 +280,33 @@ fn print_timeline_footer(result: &TimelineResult) {
|
|||||||
println!();
|
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 {
|
fn format_event_tag(event_type: &TimelineEventType) -> String {
|
||||||
match event_type {
|
let (label, style) = match event_type {
|
||||||
TimelineEventType::Created => Theme::success().render("CREATED"),
|
TimelineEventType::Created => ("CREATED", Theme::success()),
|
||||||
TimelineEventType::StateChanged { state } => match state.as_str() {
|
TimelineEventType::StateChanged { state } => match state.as_str() {
|
||||||
"closed" => Theme::error().render("CLOSED"),
|
"closed" => ("CLOSED", Theme::error()),
|
||||||
"reopened" => Theme::warning().render("REOPENED"),
|
"reopened" => ("REOPENED", Theme::warning()),
|
||||||
_ => Theme::dim().render(&state.to_uppercase()),
|
_ => return style_padded(&state.to_uppercase(), TAG_WIDTH, Theme::dim()),
|
||||||
},
|
},
|
||||||
TimelineEventType::LabelAdded { .. } => Theme::info().render("LABEL+"),
|
TimelineEventType::LabelAdded { .. } => ("LABEL+", Theme::info()),
|
||||||
TimelineEventType::LabelRemoved { .. } => Theme::info().render("LABEL-"),
|
TimelineEventType::LabelRemoved { .. } => ("LABEL-", Theme::info()),
|
||||||
TimelineEventType::MilestoneSet { .. } => Theme::accent().render("MILESTONE+"),
|
TimelineEventType::MilestoneSet { .. } => ("MILESTONE+", Theme::accent()),
|
||||||
TimelineEventType::MilestoneRemoved { .. } => Theme::accent().render("MILESTONE-"),
|
TimelineEventType::MilestoneRemoved { .. } => ("MILESTONE-", Theme::accent()),
|
||||||
TimelineEventType::Merged => Theme::info().render("MERGED"),
|
TimelineEventType::Merged => ("MERGED", Theme::info()),
|
||||||
TimelineEventType::NoteEvidence { .. } => Theme::dim().render("NOTE"),
|
TimelineEventType::NoteEvidence { .. } => ("NOTE", Theme::dim()),
|
||||||
TimelineEventType::DiscussionThread { .. } => Theme::warning().render("THREAD"),
|
TimelineEventType::DiscussionThread { .. } => ("THREAD", Theme::warning()),
|
||||||
TimelineEventType::CrossReferenced { .. } => Theme::dim().render("REF"),
|
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 {
|
fn format_entity_ref(entity_type: &str, iid: i64) -> String {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::cli::render::{self, Theme};
|
use crate::cli::render::{self, Icons, Theme};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@@ -1951,7 +1951,7 @@ fn print_expert_human(r: &ExpertResult, project_path: Option<&str>) {
|
|||||||
};
|
};
|
||||||
println!(
|
println!(
|
||||||
" {:<16} {:>6} {:>12} {:>6} {:>12} {:<12}{}{}",
|
" {:<16} {:>6} {:>12} {:>6} {:>12} {:<12}{}{}",
|
||||||
Theme::info().render(&format!("@{}", expert.username)),
|
Theme::info().render(&format!("{} {}", Icons::user(), expert.username)),
|
||||||
expert.score,
|
expert.score,
|
||||||
reviews,
|
reviews,
|
||||||
notes,
|
notes,
|
||||||
@@ -2004,16 +2004,18 @@ fn print_workload_human(r: &WorkloadResult) {
|
|||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
Theme::bold().render(&format!("@{} -- Workload Summary", r.username))
|
Theme::bold().render(&format!(
|
||||||
|
"{} {} -- Workload Summary",
|
||||||
|
Icons::user(),
|
||||||
|
r.username
|
||||||
|
))
|
||||||
);
|
);
|
||||||
println!("{}", "\u{2500}".repeat(60));
|
println!("{}", "\u{2500}".repeat(60));
|
||||||
|
|
||||||
if !r.assigned_issues.is_empty() {
|
if !r.assigned_issues.is_empty() {
|
||||||
println!();
|
|
||||||
println!(
|
println!(
|
||||||
" {} ({})",
|
"{}",
|
||||||
Theme::bold().render("Assigned Issues"),
|
render::section_divider(&format!("Assigned Issues ({})", r.assigned_issues.len()))
|
||||||
r.assigned_issues.len()
|
|
||||||
);
|
);
|
||||||
for item in &r.assigned_issues {
|
for item in &r.assigned_issues {
|
||||||
println!(
|
println!(
|
||||||
@@ -2032,11 +2034,9 @@ fn print_workload_human(r: &WorkloadResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !r.authored_mrs.is_empty() {
|
if !r.authored_mrs.is_empty() {
|
||||||
println!();
|
|
||||||
println!(
|
println!(
|
||||||
" {} ({})",
|
"{}",
|
||||||
Theme::bold().render("Authored MRs"),
|
render::section_divider(&format!("Authored MRs ({})", r.authored_mrs.len()))
|
||||||
r.authored_mrs.len()
|
|
||||||
);
|
);
|
||||||
for mr in &r.authored_mrs {
|
for mr in &r.authored_mrs {
|
||||||
let draft = if mr.draft { " [draft]" } else { "" };
|
let draft = if mr.draft { " [draft]" } else { "" };
|
||||||
@@ -2057,11 +2057,9 @@ fn print_workload_human(r: &WorkloadResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !r.reviewing_mrs.is_empty() {
|
if !r.reviewing_mrs.is_empty() {
|
||||||
println!();
|
|
||||||
println!(
|
println!(
|
||||||
" {} ({})",
|
"{}",
|
||||||
Theme::bold().render("Reviewing MRs"),
|
render::section_divider(&format!("Reviewing MRs ({})", r.reviewing_mrs.len()))
|
||||||
r.reviewing_mrs.len()
|
|
||||||
);
|
);
|
||||||
for mr in &r.reviewing_mrs {
|
for mr in &r.reviewing_mrs {
|
||||||
let author = mr
|
let author = mr
|
||||||
@@ -2086,11 +2084,12 @@ fn print_workload_human(r: &WorkloadResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !r.unresolved_discussions.is_empty() {
|
if !r.unresolved_discussions.is_empty() {
|
||||||
println!();
|
|
||||||
println!(
|
println!(
|
||||||
" {} ({})",
|
"{}",
|
||||||
Theme::bold().render("Unresolved Discussions"),
|
render::section_divider(&format!(
|
||||||
r.unresolved_discussions.len()
|
"Unresolved Discussions ({})",
|
||||||
|
r.unresolved_discussions.len()
|
||||||
|
))
|
||||||
);
|
);
|
||||||
for disc in &r.unresolved_discussions {
|
for disc in &r.unresolved_discussions {
|
||||||
println!(
|
println!(
|
||||||
@@ -2128,7 +2127,11 @@ fn print_reviews_human(r: &ReviewsResult) {
|
|||||||
println!();
|
println!();
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
Theme::bold().render(&format!("@{} -- Review Patterns", r.username))
|
Theme::bold().render(&format!(
|
||||||
|
"{} {} -- Review Patterns",
|
||||||
|
Icons::user(),
|
||||||
|
r.username
|
||||||
|
))
|
||||||
);
|
);
|
||||||
println!("{}", "\u{2500}".repeat(60));
|
println!("{}", "\u{2500}".repeat(60));
|
||||||
println!();
|
println!();
|
||||||
@@ -2289,7 +2292,7 @@ fn print_overlap_human(r: &OverlapResult, project_path: Option<&str>) {
|
|||||||
|
|
||||||
println!(
|
println!(
|
||||||
" {:<16} {:<6} {:>7} {:<12} {}{}",
|
" {:<16} {:<6} {:>7} {:<12} {}{}",
|
||||||
Theme::info().render(&format!("@{}", user.username)),
|
Theme::info().render(&format!("{} {}", Icons::user(), user.username)),
|
||||||
format_overlap_role(user),
|
format_overlap_role(user),
|
||||||
user.touch_count,
|
user.touch_count,
|
||||||
render::format_relative_time(user.last_seen_at),
|
render::format_relative_time(user.last_seen_at),
|
||||||
|
|||||||
Reference in New Issue
Block a user