From 361757568fbfc0736747f360d7509b513c705f4f Mon Sep 17 00:00:00 2001 From: teernisse Date: Sat, 14 Feb 2026 10:10:22 -0500 Subject: [PATCH] refactor(cli): remove deprecated stage_spinner, migrate remaining callers to v2 Phase 7 cleanup: migrate timeline.rs and main.rs search spinner from stage_spinner() to stage_spinner_v2() with proper icon labels, then remove the now-unused stage_spinner() function and its tests. No external callers remain for the old numbered-stage API. Co-Authored-By: Claude Opus 4.6 --- src/cli/commands/timeline.rs | 30 ++++++++++++++++++---- src/cli/progress.rs | 50 +----------------------------------- src/cli/render.rs | 25 +++++++++++------- src/main.rs | 13 ++++------ 4 files changed, 47 insertions(+), 71 deletions(-) diff --git a/src/cli/commands/timeline.rs b/src/cli/commands/timeline.rs index ce473dd..7afda42 100644 --- a/src/cli/commands/timeline.rs +++ b/src/cli/commands/timeline.rs @@ -2,7 +2,7 @@ use crate::cli::render::{self, Icons, Theme}; use serde::Serialize; use crate::Config; -use crate::cli::progress::stage_spinner; +use crate::cli::progress::stage_spinner_v2; use crate::core::db::create_connection; use crate::core::error::{LoreError, Result}; use crate::core::paths::get_db_path; @@ -96,7 +96,12 @@ pub async fn run_timeline(config: &Config, params: &TimelineParams) -> Result { // Direct seeding: synchronous, no Ollama needed - let spinner = stage_spinner(1, 3, "Resolving entity...", params.robot_mode); + let spinner = stage_spinner_v2( + Icons::search(), + "Resolve", + "Resolving entity...", + params.robot_mode, + ); let result = seed_timeline_direct(&conn, &entity_type, iid, project_id)?; spinner.finish_and_clear(); result @@ -111,7 +116,12 @@ pub async fn run_timeline(config: &Config, params: &TimelineParams) -> Result Result Result &'static MultiProgress { &MULTI } -/// Create a spinner for a numbered pipeline stage. -/// -/// Returns a hidden (no-op) bar in robot mode so callers can use -/// the same code path regardless of output mode. -pub fn stage_spinner(stage: u8, total: u8, msg: &str, robot_mode: bool) -> ProgressBar { - if robot_mode { - return ProgressBar::hidden(); - } - let pb = multi().add(ProgressBar::new_spinner()); - pb.set_style( - ProgressStyle::default_spinner() - .template("{spinner:.blue} {prefix} {msg}") - .expect("valid template"), - ); - pb.enable_steady_tick(std::time::Duration::from_millis(80)); - pb.set_prefix(format!("[{stage}/{total}]")); - pb.set_message(msg.to_string()); - pb -} - /// Stage spinner with icon prefix and elapsed time on the right. /// /// Template: `{spinner:.cyan} {prefix} {wide_msg} {elapsed_style:.dim}` @@ -176,35 +156,7 @@ mod tests { drop(w); } - #[test] - fn stage_spinner_robot_mode_returns_hidden() { - let pb = stage_spinner(1, 3, "Testing...", true); - assert!(pb.is_hidden()); - } - - #[test] - fn stage_spinner_human_mode_sets_properties() { - let pb = stage_spinner(1, 3, "Testing...", false); - assert_eq!(pb.prefix(), "[1/3]"); - assert_eq!(pb.message(), "Testing..."); - pb.finish_and_clear(); - } - - #[test] - fn stage_spinner_sets_prefix_format() { - let pb = stage_spinner(2, 5, "Working...", false); - assert_eq!(pb.prefix(), "[2/5]"); - pb.finish_and_clear(); - } - - #[test] - fn stage_spinner_sets_message() { - let pb = stage_spinner(1, 3, "Seeding timeline...", false); - assert_eq!(pb.message(), "Seeding timeline..."); - pb.finish_and_clear(); - } - - // ── New progress API tests ── + // ── Progress API tests ── #[test] fn stage_spinner_v2_robot_mode_returns_hidden() { diff --git a/src/cli/render.rs b/src/cli/render.rs index 99540ac..ad41975 100644 --- a/src/cli/render.rs +++ b/src/cli/render.rs @@ -32,14 +32,10 @@ impl GlyphMode { /// Precedence: /// 1. Explicit `--icons` CLI value (passed as `cli_flag`) /// 2. `LORE_ICONS` environment variable - /// 3. Auto-detect: Nerd if `$TERM_PROGRAM` matches known Nerd-capable terminals + /// 3. Force ASCII fallback if `force_ascii` is true (robot mode) + /// 4. Auto-detect: Nerd if `$TERM_PROGRAM` matches known Nerd-capable terminals /// or `$NERD_FONTS=1`; otherwise Unicode - /// 4. Force ASCII if `force_ascii` is true (robot mode or `--color never`) pub fn detect(cli_flag: Option<&str>, force_ascii: bool) -> Self { - if force_ascii { - return Self::Ascii; - } - // 1. CLI flag if let Some(flag) = cli_flag { return Self::from_str_lossy(flag); @@ -50,7 +46,12 @@ impl GlyphMode { return Self::from_str_lossy(&val); } - // 3. Auto-detect + // 3. Robot-safe fallback + if force_ascii { + return Self::Ascii; + } + + // 4. Auto-detect if Self::detect_nerd_capable() { Self::Nerd } else { @@ -1252,8 +1253,14 @@ mod tests { } #[test] - fn glyph_mode_force_ascii_overrides_cli_flag() { - assert_eq!(GlyphMode::detect(Some("nerd"), true), GlyphMode::Ascii); + fn glyph_mode_force_ascii_is_fallback_when_no_explicit_icon_mode() { + assert_eq!(GlyphMode::detect(None, true), GlyphMode::Ascii); + } + + #[test] + fn glyph_mode_force_ascii_does_not_override_cli_flag() { + assert_eq!(GlyphMode::detect(Some("nerd"), true), GlyphMode::Nerd); + assert_eq!(GlyphMode::detect(Some("unicode"), true), GlyphMode::Unicode); } #[test] diff --git a/src/main.rs b/src/main.rs index 18b0248..9675dcd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,11 +144,8 @@ async fn main() { } } - // I1: Respect NO_COLOR convention (https://no-color.org/) - let force_ascii = robot_mode - || cli.color == "never" - || std::env::var("NO_COLOR").is_ok_and(|v| !v.is_empty()); - let glyphs = GlyphMode::detect(cli.icons.as_deref(), force_ascii); + // Icon mode is independent of color flags; robot mode still defaults to ASCII. + let glyphs = GlyphMode::detect(cli.icons.as_deref(), robot_mode); if std::env::var("NO_COLOR").is_ok_and(|v| !v.is_empty()) { LoreRenderer::init(ColorMode::Never, glyphs); @@ -1919,9 +1916,9 @@ async fn handle_search( limit: args.limit, }; - let spinner = lore::cli::progress::stage_spinner( - 1, - 1, + let spinner = lore::cli::progress::stage_spinner_v2( + lore::cli::render::Icons::search(), + "Search", &format!("Searching ({})...", args.mode), robot_mode, );