@@ -341,25 +341,62 @@ var (
341341 styleLabel = lipgloss .NewStyle ().Foreground (lipgloss .Color ("12" )) // blue
342342)
343343
344+ const maxVisibleCompleted = 10 // show only the last N completed/errored files
345+
344346func (m scanModel ) View () string {
345347 var s string
348+
349+ // Count states for the progress header.
350+ var pending , inFlight , completed , errored int
346351 for _ , f := range m .files {
347- name := filepath .Base (f .filename )
348352 switch f .state {
349353 case statePending :
350- s += styleDim .Render (" " + name ) + "\n "
354+ pending ++
355+ case stateUploading , stateScanning :
356+ inFlight ++
357+ case stateDone :
358+ completed ++
359+ case stateError :
360+ errored ++
361+ }
362+ }
363+ total := len (m .files )
364+ finished := completed + errored
351365
366+ // Progress header.
367+ s += styleLabel .Render (fmt .Sprintf ("Progress: %d/%d" , finished , total ))
368+ if pending > 0 {
369+ s += styleDim .Render (fmt .Sprintf (" (%d pending)" , pending ))
370+ }
371+ if errored > 0 {
372+ s += " " + styleError .Render (fmt .Sprintf ("%d failed" , errored ))
373+ }
374+ s += "\n \n "
375+
376+ // Show in-flight files (uploading/scanning) — these always get a spinner line.
377+ for _ , f := range m .files {
378+ name := filepath .Base (f .filename )
379+ switch f .state {
352380 case stateUploading :
353381 label := " Uploading "
354382 if m .isRescan {
355383 label = " Rescanning "
356384 }
357385 s += f .spinner .View () + styleLabel .Render (label ) + name + " ...\n "
358-
359386 case stateScanning :
360387 sha := truncSha (f .sha256 )
361388 s += f .spinner .View () + styleLabel .Render (" Scanning " ) + name + " " + styleDim .Render (sha ) + "\n "
389+ }
390+ }
362391
392+ // Collect completed/errored rows, show only the last N.
393+ type doneRow struct {
394+ line string
395+ }
396+ var doneRows []doneRow
397+ for _ , f := range m .files {
398+ name := filepath .Base (f .filename )
399+ switch f .state {
363400 case stateDone :
364401 sha := truncSha (f .sha256 )
365402 line := styleSuccess .Render ("✓" ) + " " + name + " " + styleDim .Render (sha )
@@ -375,12 +412,22 @@ func (m scanModel) View() string {
375412 f .result .MultiAV .Positives , f .result .MultiAV .EnginesCount )
376413 }
377414 }
378- s += line + "\n "
379-
415+ doneRows = append (doneRows , doneRow {line })
380416 case stateError :
381- s += styleError .Render ("✗" ) + " " + name + " " + styleError .Render (f .err .Error ()) + "\n "
417+ line := styleError .Render ("✗" ) + " " + name + " " + styleError .Render (f .err .Error ())
418+ doneRows = append (doneRows , doneRow {line })
382419 }
383420 }
421+
422+ if len (doneRows ) > maxVisibleCompleted && ! m .done {
423+ hidden := len (doneRows ) - maxVisibleCompleted
424+ s += styleDim .Render (fmt .Sprintf (" ... %d more completed above ..." , hidden )) + "\n "
425+ doneRows = doneRows [hidden :]
426+ }
427+ for _ , r := range doneRows {
428+ s += r .line + "\n "
429+ }
430+
384431 return s
385432}
386433
0 commit comments