findbar

package
v0.34.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 25, 2026 License: MIT Imports: 6 Imported by: 0

README

Find Bar

In-buffer search overlay: a narrow query input with a match counter, navigation messages, and a stateless FindMatches helper.

find-bar preview

The bar is purely an input. It owns the query, the cursor, and the active match index; the consumer owns the buffer and is the one that scrolls to a hit. Pair with editor for Ctrl-F search inside a buffer, or with any row-oriented component (log-stream, markdown-viewer) that wants a search affordance.

Install

glyph add find-bar

Hello, world

package main

import (
	"fmt"

	tea "github.com/charmbracelet/bubbletea"

	findbar "github.com/truffle-dev/glyph/components/find-bar"
	"github.com/truffle-dev/glyph/components/theme"
)

var lines = []string{"hello world", "world hello", "WORLD"}

type model struct{ bar findbar.Bar }

func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case findbar.QueryMsg:
		matches := findbar.FindMatches(lines, msg.Value, m.bar.CaseSensitive())
		m.bar = m.bar.WithMatches(matches, 0)
		return m, nil
	case findbar.CloseMsg:
		return m, tea.Quit
	}
	updated, cmd := m.bar.Update(msg)
	m.bar = updated
	return m, cmd
}
func (m model) View() string { return m.bar.View() }

func main() {
	m := model{bar: findbar.New(theme.Default).WithWidth(56)}
	if _, err := tea.NewProgram(m).Run(); err != nil {
		fmt.Println(err)
	}
}

Bindings

typing                       insert into query (emits QueryMsg)
←   →                        cursor by one rune
Home / End                   query edges
Backspace                    delete-back (emits QueryMsg)
Delete                       delete-forward (emits QueryMsg)
Ctrl-U                       clear query (emits QueryMsg "")
Enter                        emit NextMsg
Alt-Enter / F3 with Alt      emit PrevMsg
F3                           emit NextMsg
Esc                          emit CloseMsg

API surface

Package: findbar

Types

  • Bar — Bubble Tea model
  • Match{Row, ColStart, ColEnd} — one buffer hit (rune indices)
  • QueryMsg{Value}, NextMsg, PrevMsg, CloseMsg

Functions

  • New
  • FindMatches(lines, query, caseSensitive) []Match — stateless helper

Model methods

  • Init, Update, View
  • WithWidth, WithQuery, WithCaseSensitive, WithMatches
  • Focus, Blur, Focused
  • Query, CaseSensitive, Current, MatchCount, CurrentMatch

Dependencies

  • glyph component theme (installed automatically)
  • github.com/charmbracelet/bubbletea@v1.3.10
  • github.com/charmbracelet/lipgloss@v1.1.0

Notes

The bar emits messages via Cmd rather than mutating any parent state. The consumer subscribes by intercepting the four message types in its own Update: re-run FindMatches on QueryMsg, advance/retreat the active match index on Next/Prev, dismiss the overlay on Close. This is what every editor does at the boundary between an input widget and the buffer it searches.

FindMatches walks lines for plain substring hits and returns matches in document order. Empty queries return nil. Non-overlapping by construction: a 2-char query in aaaa produces 2 matches at columns 0..2 and 2..4, not 3. If you want fuzzy or regex behavior, wire your own search and pass the resulting []Match to WithMatches.

See also

License

MIT, same as the rest of glyph.

Documentation

Overview

Package findbar renders an in-buffer search bar — the narrow query input that overlays an editor when the user hits Ctrl-F. It owns nothing but the query, the active match index, and the navigation buttons; the actual buffer-side search is performed by the consumer (FindMatches walks any "lines" string for plain or case-insensitive matches; if the consumer wants fuzzy or regex they wire their own).

Composition shape:

bar := findbar.New(theme.Default)
bar = bar.WithQuery(q).WithMatches(matches, current)
// In Update: pass tea.KeyMsg to bar.Update; act on its messages.

The bar emits four messages: QueryMsg{Value} when the input changes, NextMsg / PrevMsg when the user hits Enter/F3/Shift-F3, CloseMsg when the user hits Esc. The consumer is the one that scrolls the editor to the next match — the bar is purely an input.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bar

type Bar struct {
	// contains filtered or unexported fields
}

Bar is a Bubble Tea model for the find bar.

func New

func New(t theme.Theme) Bar

New constructs a Bar focused, 48 cells wide, with no matches yet.

func (Bar) Blur

func (b Bar) Blur() Bar

func (Bar) CaseSensitive

func (b Bar) CaseSensitive() bool

CaseSensitive reports the bar's current match mode.

func (Bar) Current

func (b Bar) Current() int

Current returns the active match index, or -1 if none.

func (Bar) CurrentMatch

func (b Bar) CurrentMatch() (Match, bool)

CurrentMatch returns the active match and true when one exists.

func (Bar) Focus

func (b Bar) Focus() Bar

Focus / Blur control whether the bar consumes key events.

func (Bar) Focused

func (b Bar) Focused() bool

Focused reports whether the bar is accepting input.

func (Bar) Init

func (b Bar) Init() tea.Cmd

Init implements tea.Model.

func (Bar) MatchCount

func (b Bar) MatchCount() int

MatchCount returns the size of the bar's match set.

func (Bar) Query

func (b Bar) Query() string

Query returns the current query string.

func (Bar) Update

func (b Bar) Update(msg tea.Msg) (Bar, tea.Cmd)

Update implements tea.Model. It returns its own messages via Cmd rather than mutating any parent state; the consumer is expected to re-issue queries and navigate the editor on each message.

func (Bar) View

func (b Bar) View() string

View renders the bar: query input on the left, match counter on the right ("3 / 12" or "no matches"), wrapped in a single-line bordered frame the same height as a status-bar row.

func (Bar) WithCaseSensitive

func (b Bar) WithCaseSensitive(on bool) Bar

WithCaseSensitive toggles case-sensitive matching for FindMatches.

func (Bar) WithMatches

func (b Bar) WithMatches(matches []Match, current int) Bar

WithMatches replaces the match set; current is clamped to a valid index or -1 when empty.

func (Bar) WithQuery

func (b Bar) WithQuery(s string) Bar

WithQuery presets the query string and parks the cursor at end.

func (Bar) WithWidth

func (b Bar) WithWidth(w int) Bar

WithWidth sets the bar width in cells. Clamped to >= 24.

type CloseMsg

type CloseMsg struct{}

CloseMsg is emitted when the user hits Esc.

type Match

type Match struct {
	Row      int
	ColStart int
	ColEnd   int
}

Match is a single hit inside the buffer. Row is 0-based; ColStart and ColEnd are rune indices (start-inclusive, end-exclusive).

func FindMatches

func FindMatches(lines []string, query string, caseSensitive bool) []Match

FindMatches is a stateless helper: walk lines for every occurrence of query and return the match set in document order. Empty queries return nil. The bar passes b.CaseSensitive() through; consumers can call FindMatches directly with any boolean.

type NextMsg

type NextMsg struct{}

NextMsg / PrevMsg are emitted when the user requests cursor motion.

type PrevMsg

type PrevMsg struct{}

NextMsg / PrevMsg are emitted when the user requests cursor motion.

type QueryMsg

type QueryMsg struct{ Value string }

QueryMsg is emitted whenever the query value changes. The consumer should re-run FindMatches and reseat the bar via WithMatches.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL