trail list: show shown/total counts when --limit truncates · Entire

trail list: show shown/total counts when --limit truncates

adfe5a0→main·

matthiaswenz·1mo ago·2 files·+125 added/-7 removed

A capped page rendered "Recent trails · 10", reading as the total number of matches rather than the page size. Header, per-status group counts, and the Other bucket now render "shown/total" when --limit dropped rows, and the header noun pluralizes by the total. Also fix the --json flag help, which claimed only --status is respected.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Sessions

72bbfd94df45View transcript

[?
Align Trail Status with Server and Refactor ListClaude Code·Fable 5·7 steps](/content/gh/entireio/cli/session/e489d20f-3db8-41fd-9446-9ab6aa550405#timeline-72bbfd94df45/index.html)

Changes

2

159 unmodified lines

160
161
162
163
163
164
165
166
60 unmodified lines

227
228
229
230
231
232
233
234
14 unmodified lines

249
250
251
252
253
254
255
256
113 unmodified lines

370
371
372
373
374
375
376
377
378
379
380
381
382
17 unmodified lines

400
401
402
392
403
404
405
406
10 unmodified lines

417
418
419
409
420
421
422
423
424
425
426
427
2 unmodified lines

430
431
432
433
434
435
436
437
438
439
440
441
420
442
443
444
423
445
446
447
448
5 unmodified lines

454
455
456
435
457
458
459
438
460
461
462
463
75 unmodified lines

539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562

159 unmodified lines

"Filter by author login (case-insensitive); use '"+trailListAuthorMe+"' for yourself (requires gh CLI); omit for any author")
    cmd.Flags().StringVar(&opts.Status, "status", defaultTrailListStatus,
        "Filter by comma-separated status(es): "+formatValidStatuses()+"; use '"+trailListStatusAny+"' for all statuses")
    cmd.Flags().BoolVar(&opts.JSON, "json", false, "Output as JSON (respects --status filter)")
    cmd.Flags().BoolVar(&opts.JSON, "json", false, "Output as JSON (respects --author, --status, and --limit)")
    cmd.Flags().IntVarP(&opts.Limit, "limit", "n", defaultTrailListLimit, "Maximum number of trails to show")

return cmd
60 unmodified lines

sort.Slice(trails, func(i, j int) bool {
        return trails[i].UpdatedAt.After(trails[j].UpdatedAt)
    })
    totalMatched := len(trails)
    statusTotals := trailStatusCounts(trails)
    trails = limitTrails(trails, opts.Limit)

if opts.JSON {
14 unmodified lines

RequestedAuthor: authorFilter,
        CurrentUser:     currentUserLogin,
        StatusFilters:   statusFilters,
        TotalMatched:    totalMatched,
        StatusTotals:    statusTotals,
    })

return nil
113 unmodified lines

RequestedAuthor string
    CurrentUser     string
    StatusFilters   []trail.Status
    // TotalMatched is the number of trails matching the filters before
    // --limit truncation. Counts render as "shown/total" when they differ so
    // a full page doesn't read as the total number of matches.
    TotalMatched int
    // StatusTotals are the pre-truncation per-status counts backing the
    // group headers in the grouped view.
    StatusTotals map[trail.Status]int
}

func printTrailList(w io.Writer, trails []*trail.Metadata, opts trailListDisplayOptions) {
17 unmodified lines

for _, t := range group {
        rendered[t] = true
    }
    fmt.Fprintf(w, "  %s · %d\n", trailStatusTitle(status), len(group))
    fmt.Fprintf(w, "  %s · %s\n", trailStatusTitle(status), trailCountDisplay(len(group), opts.StatusTotals[status]))
    fmt.Fprintln(w)
    printTrailRows(w, group, showAuthor)
    fmt.Fprintln(w)
10 unmodified lines

}
    }
    if len(other) > 0 {
        fmt.Fprintf(w, "  Other · %d\n", len(other))
        otherTotal := opts.TotalMatched
        for _, status := range trailListStatusOrder(nil) {
            otherTotal -= opts.StatusTotals[status]
        }
        fmt.Fprintf(w, "  Other · %s\n", trailCountDisplay(len(other), otherTotal))
        fmt.Fprintln(w)
        printTrailRows(w, other, showAuthor)
        fmt.Fprintln(w)
2 unmodified lines

}

func printTrailListHeader(w io.Writer, opts trailListDisplayOptions, count int) {
    countStr := trailCountDisplay(count, opts.TotalMatched)
    // The noun refers to the full match set, so pluralize by the total when
    // the page is truncated ("1/2 trails", not "1/2 trail").
    nounCount := count
    if opts.TotalMatched > count {
        nounCount = opts.TotalMatched
    }
    if opts.RequestedAuthor == "" {
        if len(opts.StatusFilters) == 0 {
            fmt.Fprintf(w, "  Recent %s · %d\n", pluralize("trail", count), count)
            fmt.Fprintf(w, "  Recent %s · %s\n", pluralize("trail", nounCount), countStr)
            return
        }
        fmt.Fprintf(w, "  %s · %d %s\n", trailStatusListTitle(opts.StatusFilters), count, pluralize("trail", count))
        fmt.Fprintf(w, "  %s · %s %s\n", trailStatusListTitle(opts.StatusFilters), countStr, pluralize("trail", nounCount))
        return
    }

5 unmodified lines

label = fmt.Sprintf("Your trails (%s)", opts.CurrentUser)
    }
    if len(opts.StatusFilters) == 0 {
        fmt.Fprintf(w, "  %s · %d\n", label, count)
        fmt.Fprintf(w, "  %s · %s\n", label, countStr)
        return
    }
    fmt.Fprintf(w, "  %s · %d %s\n", label, count, trailStatusListDisplay(opts.StatusFilters))
    fmt.Fprintf(w, "  %s · %s %s\n", label, countStr, trailStatusListDisplay(opts.StatusFilters))
}

func printTrailRows(w io.Writer, trails []*trail.Metadata, showAuthor bool) {
75 unmodified lines

return strings.ToUpper(display[:1]) + display[1:]
}

// trailCountDisplay renders a count as "shown/total" when --limit truncated
// the list, so a capped page doesn't read as the total number of matches.
func trailCountDisplay(shown, total int) string {
    if total > shown {
        return fmt.Sprintf("%d/%d", shown, total)
    }
    return strconv.Itoa(shown)
}

// trailStatusCounts tallies trails per status before --limit truncation.
func trailStatusCounts(trails []*trail.Metadata) map[trail.Status]int {
    counts := make(map[trail.Status]int, len(trails))
    for _, t := range trails {
        counts[t.Status]++
    }
    return counts
}

func pluralize(s string, count int) string {
    if count == 1 {
        return s