DEV Community

Cover image for peektea narrows its gaze 👀 filter-as-you-type and hidden files
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on • Edited on

peektea narrows its gaze 👀 filter-as-you-type and hidden files

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 ended with two items left on the list.

Filter as you type — Bubble Tea's textinput component from bubbles is sitting there waiting

Hidden file toggle — show/hide dotfiles

Both ship today.

Press / and start typing, the list narrows instantly.

Press . and dotfiles appear.

Press it again and they vanish.

peektea filter and hidden files demo

GitHub logo lovestaco / peektea

Peek First. Open Later. A TUI file browser built with bubble tea

cover

A minimal terminal file browser built with Bubble Tea.

Peek through your filesystem with arrow keys (or vim keys), then pour each file straight into the app you've configured for it.

Demo

A quick peek before you steep:

demo

Install

One-liner:

curl -fsSL https://raw.githubusercontent.com/lovestaco/peektea/master/scripts/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Download a binary (no Go required) — grab the latest release for your platform from the releases page:

Platform File
Linux x86-64 peektea_*_linux_amd64.tar.gz
Linux arm64 peektea_*_linux_arm64.tar.gz
macOS x86-64 peektea_*_darwin_amd64.tar.gz
macOS Apple Silicon peektea_*_darwin_arm64.tar.gz

Extract and put the peektea binary anywhere on your $PATH.

Install with Go:

go install github.com/lovestaco/peektea@latest
Enter fullscreen mode Exit fullscreen mode

Build from source:

git clone https://github.com/lovestaco/peektea
cd peektea
make install
Enter fullscreen mode Exit fullscreen mode

make install puts the binary in ~/go/bin and figures out $PATH for you:

  1. Already reachable — done, nothing to do.
  2. ~/.local/bin is on your PATH — symlinks the binary there, works immediately in the current shell.
  3. Neither — appends ~/go/bin to your .bashrc

Filter as you type

The / key enters filter mode.

A text input appears at the bottom of the panel right above the hint bar, like vim's command line.

Type anything and only matching entries stay visible.

Press esc to clear it.

The component doing the work is textinput from charmbracelet/bubbles, a library of ready-made Bubble Tea components.

Wiring it into the model is straightforward:

type model struct {
    // ...
    filterInput textinput.Model
    filtering   bool
}
Enter fullscreen mode Exit fullscreen mode

When / is pressed, focus the input and return textinput.Blink, that's the command that starts the cursor blinking animation:

case "/":
    m.filtering = true
    m.filterInput.Focus()
    return m, textinput.Blink
Enter fullscreen mode Exit fullscreen mode

While filtering, most keystrokes go straight to the textinput's own Update.

Navigation keys (↑↓) are intercepted first so you can still move the cursor while typing:

if m.filtering {
    switch msg.String() {
    case "up", "k":
        if m.cursor > 0 { m.cursor-- }
    case "down", "j":
        if m.cursor < len(m.entries)-1 { m.cursor++ }
    case "esc":
        m.filtering = false
        m.filterInput.Blur()
        m.filterInput.SetValue("")
        m = m.withFilters()
    default:
        var tiCmd tea.Cmd
        m.filterInput, tiCmd = m.filterInput.Update(msg)
        m = m.withFilters()
        return m, tiCmd
    }
}
Enter fullscreen mode Exit fullscreen mode

The tiCmd returned by m.filterInput.Update carries the next blink tick.

You have to pass it back out of Update, drop it and the cursor freezes.

Composable filters with withFilters()

The model now stores two entry slices: allEntries (everything os.ReadDir returned) and entries (what's visible after filters).

Every filter change calls withFilters() to recompute:

func (m model) withFilters() model {
    q := strings.ToLower(m.filterInput.Value())
    var filtered []os.DirEntry
    for _, e := range m.allEntries {
        if !m.showHidden && strings.HasPrefix(e.Name(), ".") {
            continue
        }
        if q != "" && !strings.Contains(strings.ToLower(e.Name()), q) {
            continue
        }
        filtered = append(filtered, e)
    }
    m.entries = filtered
    if m.cursor >= len(m.entries) {
        m.cursor = max(0, len(m.entries)-1)
    }
    return m
}
Enter fullscreen mode Exit fullscreen mode

Hidden toggle and text filter compose naturally, you can filter by name with dotfiles visible or hidden simultaneously.

Adding a third filter later means touching one function.

withFilters returns a new model value rather than mutating in place, which fits cleanly with Bubble Tea's immutable-update pattern.

Hidden file toggle

The . key flips showHidden and calls withFilters():

case ".":
    m.showHidden = !m.showHidden
    m = m.withFilters()
    needPreview = true
Enter fullscreen mode Exit fullscreen mode

The hint bar reflects the current state so you always know where you are:

. show hidden   ← dotfiles hidden
. hide hidden   ← dotfiles visible
Enter fullscreen mode Exit fullscreen mode

Navigating into a directory clears the filter, the search you ran in one folder doesn't carry over to its children.

Makes the behaviour predictable without any extra state to manage.

The vim-style bottom bar

The filter input sits at the bottom of the panel, one line above the hint bar.

That's the slot vim uses for its command line, /pattern, :w, :q so it feels immediately familiar.

When filtering is active the text input renders there instead of the hint.

When you press enter to confirm and leave filter mode, the active filter stays visible:

/main  esc to clear
↑/↓  →/enter  o open  / filter  . hide hidden  p preview  ←/h  q
Enter fullscreen mode Exit fullscreen mode

The hint never disappears, even in filter mode it's always one line below.

The layout calculates how many rows the file list consumed and pads blank lines to push both the filter bar and the hint bar to the very bottom of the terminal.


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:

GitHub logo HexmosTech / git-lrc

Free, Micro AI Code Reviews That Run on Commit




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.

See It In Action

See git-lrc catch serious security issues such as leaked credentials, expensive cloud operations, and sensitive material in log statements

git-lrc-intro-60s.mp4

Why

  • 🤖 AI agents silently break things. Code removed. Logic changed. Edge cases gone. You won't notice until production.
  • 🔍 Catch it before it ships. AI-powered inline comments show you exactly what changed and what looks wrong.

Top comments (4)

Collapse
 
shricodev profile image
Shrijal Acharya

I see it replacing my yazi setup 👀 Great work so far.

Collapse
 
lovestaco profile image
Athreya aka Maneshwar

Hehe, thanks bro.
Send your yazi, lemme peek it xD

Collapse
 
shricodev profile image
Shrijal Acharya

Just tried it. Works great so far.
it took me quite a while to understand the Elm architecture in Bubble Tea, but I loved working with it when building gophercast.

I guess I’m currently sticking to the defaults with yazi. You can peek it here if I make some custom config for it: github.com/shricodev/dotfiles/tree...

Thread Thread
 
lovestaco profile image
Athreya aka Maneshwar • Edited

Awesome, will try gophercast