cd /news/ai-tools/peektea-past-the-roadmap-sorting-and… · home topics ai-tools article
[ARTICLE · art-22283] src=dev.to pub= topic=ai-tools verified=true sentiment=↑ positive

peektea past the roadmap 👀 sorting and scrollable previews

Maneshwar added sorting and scrollable previews to peektea, the terminal-based file browser. The `s` key now cycles through sort modes by name, size, or modification time, while `[` and `]` keys enable scrolling through file previews. The update also integrates bat for syntax-highlighted previews and adds scrollbars to both panels.

read4 min publishedJun 5, 2026

Hello, I'm Maneshwar. I'm building git-lrc, a Micro AI code reviewer that runs on every commit. It is free and source-available on Github. Star git-lrc to help devs discover the project. Do give it a try and share your feedback.

Last time I said the list was empty.

That was true.

Then I kept going anyway.

Press s

and the list sorts by size. Press it again, newest-modified first.

Previwing something? Press [

and ]

and scroll through the preview without leaving the peektea cli.

The s

key cycles through three modes: name → size → modified → name.

The current mode is always visible in the hint bar: s:sorted by name

, s:sorted by size

, s:sorted by modified

.

Under the hood it's a single sort.SliceStable

call inside withFilters()

.

The sort runs on the filtered slice, so it composes with the text filter and the hidden file toggle, same composable approach as before:

sort.SliceStable(filtered, func(i, j int) bool {
    switch m.sortMode {
    case sortSize:
        ii, _ := filtered[i].Info()
        jj, _ := filtered[j].Info()
        return ii.Size() > jj.Size() // largest first
    case sortMod:
        ii, _ := filtered[i].Info()
        jj, _ := filtered[j].Info()
        return ii.ModTime().After(jj.ModTime()) // newest first
    default:
        return strings.ToLower(filtered[i].Name()) < strings.ToLower(filtered[j].Name())
    }
})

SliceStable

preserves the relative order of entries that compare equal, which keeps directories grouped sensibly when names or sizes match.

The preview panel used to show the first N lines of a file and stop there.

Now you can scroll through it with [

(up) and ]

(down). Each press moves a quarter of the panel height.

The key insight: instead of limiting what gets loaded to the visible height, the preview now loads up to 500 lines and stores the whole thing.

The rendering step slices out the visible window:

lines := strings.Split(m.previewContent, "\n")
scroll := m.previewScroll
maxScroll := len(lines) - ph
if maxScroll < 0 {
    maxScroll = 0
}
if scroll > maxScroll {
    scroll = maxScroll
}
visible := lines[scroll:]
if len(visible) > ph {
    visible = visible[:ph]
}
body = strings.Join(visible, "\n")

previewScroll

resets to zero whenever you move to a new file, so you always start at the top.

While reworking the preview , I added a bat check.

bat is a cat

clone with syntax highlighting. If it's installed, peektea uses it for text previews:

if _, err := exec.LookPath("bat"); err == nil {
    out, err := exec.Command("bat",
        "--color=always",
        "--style=plain",
        "--line-range", ":500",
        "--terminal-width", fmt.Sprintf("%d", width),
        path,
    ).Output()
    if err == nil {
        return strings.TrimRight(string(out), "\n")
    }
}
// fallback to plain text reader

Same graceful-fallback pattern as chafa. Not installed? Plain text. Installed but fails on a particular file? Plain text. The preview never breaks.

Both panels now have scrollbars.

When the preview has more content than fits on screen, a thin

track with a

thumb appears on the right edge of the preview.

Same thing in the file list when there are more entries than the terminal can show.

A single function handles both:

func buildScrollbar(total, visible, scroll int) []string {
    chars := make([]string, visible)
    track := scrollTrackStyle.Render("│")
    thumb := scrollThumbStyle.Render("┃")
    if total <= visible {
        for i := range chars {
            chars[i] = track
        }
        return chars
    }
    thumbSize := visible * visible / total
    if thumbSize < 1 {
        thumbSize = 1
    }
    thumbPos := scroll * (visible - thumbSize) / (total - visible)
    for i := range chars {
        if i >= thumbPos && i < thumbPos+thumbSize {
            chars[i] = thumb
        } else {
            chars[i] = track
        }
    }
    return chars
}

It returns a slice of styled characters, one per visible row that get appended to each rendered line.

The thumb size scales proportionally: if half the content is visible, the thumb fills half the track.

AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs — without telling you. You often find out in production.

git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.

Any feedback or contributors are welcome! It's online, source-available, and ready for anyone to use.

⭐ Star it on GitHub:

| 🇩🇰 Dansk | 🇪🇸 Español | 🇮🇷 Farsi | 🇫🇮 Suomi | 🇯🇵 日本語 | 🇳🇴 Norsk | 🇵🇹 Português | 🇷🇺 Русский | 🇦🇱 Shqip | 🇨🇳 中文 |

AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs -- without telling you. You often find out in production.

** git-lrc fixes this.** It hooks into

git commit

and reviews every diff git-lrc-intro-60s.mp4See git-lrc catch serious security issues such as leaked credentials, expensive cloud operations, and sensitive material in log statements

── more in #ai-tools 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/peektea-past-the-roa…] indexed:0 read:4min 2026-06-05 ·