cd /news/ai-tools/peektea-narrows-its-gaze-filter-as-y… · home topics ai-tools article
[ARTICLE · art-21763] src=dev.to pub= topic=ai-tools verified=true sentiment=↑ positive

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

Maneshwar has added filter-as-you-type and hidden file toggle features to peektea, a terminal-based file browser built with Bubble Tea. Pressing `/` activates a text input that instantly narrows visible entries, while pressing `.` toggles the display of dotfiles, with both filters composable simultaneously. The implementation uses the `textinput` component from the charmbracelet/bubbles library and a `withFilters()` function that recomputes the visible entry list based on the current filter query and hidden file state.

read4 min publishedJun 4, 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 ended with two items left on the list.

Filter as you type— Bubble Tea'stextinput

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.

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
}

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

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
    }
}

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.

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
}

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.

The .

key flips showHidden

and calls withFilters()

:

case ".":
    m.showHidden = !m.showHidden
    m = m.withFilters()
    needPreview = true

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

. show hidden   ← dotfiles hidden
. hide hidden   ← dotfiles visible

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 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

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:

| 🇩🇰 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-narrows-its-…] indexed:0 read:4min 2026-06-04 ·