@setoelkahfi / sigit / commits / 172a214

fix(tui): show checkmark once model download completes

The in-chat model-switch line kept spinning at "switching model … (100%)" while the weights loaded into memory, which looked stuck. Once the download has fully landed on disk, swap the spinner for a green checkmark and switch the label to "model downloaded — loading into memory…". Also cap the displayed downloaded size at the expected size so it no longer reads e.g. "9.36 GB / 4.78 GB". Fixes #4 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

keypair34 committed Jun 24, 2026 at 15:45 UTC 172a214c7a7a5217509951c6af7f3b457581dec4
1 file changed +38 -17
src/chat.rs
+38 -17
@@ -872,25 +872,46 @@ mod tui {
872 Style::default().fg(Color::DarkGray),
873 )));
874 } else if app.switching_model {
875 - let frame_str = app.switching_frame();
876 - let progress_str = if let Some((downloaded, expected)) = app.download_progress {
877 - if expected > 0 {
878 - let pct = (downloaded as f64 / expected as f64 * 100.0).min(100.0) as u8;
879 - let dl_str = format_size_human(downloaded);
880 - let ex_str = format_size_human(expected);
881 - format!(" — {dl_str} / {ex_str} ({pct}%)")
882 - } else if downloaded > 0 {
883 - format!(" — {} downloaded", format_size_human(downloaded))
875 + // Once the weights have fully landed on disk, swap the spinner for a
876 + // checkmark so it's clear the download finished and we're now loading
877 + // the model into memory (which can still take a while).
878 + let download_complete = matches!(
879 + app.download_progress,
880 + Some((downloaded, expected)) if expected > 0 && downloaded >= expected
881 + );
882 +
883 + if download_complete {
884 + let size_str = app
885 + .download_progress
886 + .map(|(_, expected)| format!(" ({})", format_size_human(expected)))
887 + .unwrap_or_default();
888 + lines.push(Line::from(vec![
889 + Span::styled(" ✓ ", Style::default().fg(Color::Green)),
890 + Span::styled(
891 + format!("model downloaded{size_str} — loading into memory…"),
892 + Style::default().fg(Color::DarkGray),
893 + ),
894 + ]));
895 + } else {
896 + let progress_str = if let Some((downloaded, expected)) = app.download_progress {
897 + if expected > 0 {
898 + let pct = (downloaded as f64 / expected as f64 * 100.0).min(100.0) as u8;
899 + let dl_str = format_size_human(downloaded.min(expected));
900 + let ex_str = format_size_human(expected);
901 + format!(" — {dl_str} / {ex_str} ({pct}%)")
902 + } else if downloaded > 0 {
903 + format!(" — {} downloaded", format_size_human(downloaded))
904 + } else {
905 + String::new()
906 + }
907 } else {
908 String::new()
886 - }
887 - } else {
888 - String::new()
889 - };
890 - lines.push(Line::from(Span::styled(
891 - format!(" {frame_str} switching model{progress_str}…"),
892 - Style::default().fg(Color::DarkGray),
893 - )));
909 + };
910 + lines.push(Line::from(Span::styled(
911 + format!(" {} switching model{progress_str}…", app.switching_frame()),
912 + Style::default().fg(Color::DarkGray),
913 + )));
914 + }
915 }
916
917 let total_lines = lines.len() as u16;