← All libraries

CandyHermit

CandyHermit

Fuzzy finder overlay for terminal UIs

port of Genekkion/theHermit fuzzyfinderoverlaysearch

Fuzzy-search overlay — type to filter a list, arrow keys to select, Enter to confirm. Ships as a model wrapper that composes into any bubbletea program. Supports structured Item objects, custom filter predicates, and persistent JSONL history.

Install

composer require sugarcraft/candy-hermit

Quickstart — plain strings

use SugarCraft\Hermit\Hermit;

$hermit = Hermit::new(
    items: ['Ruby', 'PHP', 'Go', 'Rust', 'Python'],
)->setPrompt('Choose language: ');

Quickstart — numbered items + history

use SugarCraft\Hermit\Hermit;
use SugarCraft\Hermit\FilteredItem;
use SugarCraft\Hermit\History\FileHistory;

$items = [
    new FilteredItem(1, 'apple'),
    new FilteredItem(2, 'banana'),
    new FilteredItem(3, 'cherry'),
];

$hermit = Hermit::new($items)
    ->setFilterFn(fn($item) => $item->number() > 0)
    ->setPrompt('> ');

// Persist selections
$history = new FileHistory('/tmp/hermit.jsonl');
$history->append($hermit->selected());
$allItems = $history->all();

What's in the box

Fuzzy matchingAnchor-aware substring filter on item text as you type.
Hermit modelFull bubbletea Model with init / update / view.
ConfigurableCustom formatter, prompt, match highlight style, window size.
Item interfaceStructured Item objects with number() + value() instead of raw strings.
Custom filter predicatessetFilterFn() applies arbitrary Closure(Item): bool logic.
Persistent historyFileHistory stores items as JSONL lines, one item per line.
Border & StylewithBorder() / withStyle() compose candy-sprinkles Border and Style objects.
HelpBar / StatusBarKeyboard-shortcut summary and status message rendered below the overlay.
SIGWINCH resizewithOnResize() + attachSigwinch() forward terminal resize events via SignalForwarder.

Source & demos

Try the quickstart →

API

ClassMethodDescription
Hermitnew(array $items, ?Closure $itemFormatter)Create fuzzy finder (items: string[] or Item[])
HermitwithItems(array $items)Replace items and reset filter state
HermitsetPrompt(string)Set filter prompt string
HermitsetFilterFn(Closure)Set custom filter predicate: Closure(Item): bool
HermitsetItemFormatter(Closure)Set item formatter: Closure(item, isSelected): string
HermitsetMatchStyle(string)Set ANSI SGR codes for match highlighting
HermitsetWindowHeight(int)Set visible item window height
HermitsetWindowWidth(int)Set overlay width (0 = auto)
HermitsetOffset(int $x, int $y)Position overlay and show
Hermitshow() / hide()Show or hide the overlay
Hermittype(string) / backspace() / clear()Modify filter text
HermitcursorUp/Down/Top/Bottom()Navigate item cursor
Hermitselected()Get currently selected Item
Hermititems() / itemCount() / allCount()Query filtered/all item state
HermitwithBorder(?Border)Apply candy-sprinkles Border to overlay window
HermitwithStyle(?Style)Apply candy-sprinkles Style to overlay window
HermitwithHelpBar(?HelpBar)Attach keyboard-shortcut summary below filter list
HermitwithStatusBar(?StatusBar)Attach status message line at overlay bottom
HermitwithOnResize(Closure)Register callback invoked on SIGWINCH (cols, rows)
HermitattachSigwinch()Install SIGWINCH handler via SignalForwarder (requires ext-pcntl)
Hermitborder() / style() / helpBar() / statusBar()Query attached border, style, help bar, status bar
HermitView(string $backgroundView)Render composited overlay over background
HelpBarnew(array $shortcuts, bool $visible)Create help bar with key→description pairs
HelpBarwithShortcut(key, desc) / withoutShortcut(key)Add/remove shortcut entry
HelpBarshow() / hide() / isVisible()Control visibility
HelpBarrender()Render as "key: desc │ key: desc ..." string
StatusBarnew(string $message, bool $visible)Create status bar with message
StatusBarwithMessage(string) / withNoMessage()Set or clear primary message
StatusBarwithSegment(name, value) / withoutSegment(name)Add/remove named segment
StatusBarshow() / hide() / isVisible()Control visibility
StatusBarrender()Render as "[name: value] message" string
Iteminterface number(), value()Contract for filterable items
FilteredItemnew(int $number, string $value)Numbered Item implementation
FileHistorynew(string $path)Open/create JSONL history file
FileHistoryappend(Item)Append item as JSON line
FileHistoryall()Read all items from history
FileHistoryclear() / path()Clear history or get file path
Modelinterface update(Hermit, string), view(Hermit)Bubble-Tea embedding contract

Demos.

VHS-recorded GIFs of every example shipped with the library. Regenerated automatically on every push that touches the source.

Background + overlay

Background + overlay

Hermit overlay over a sample background, four filter states.
Filter interaction

Filter interaction

Filter narrowing the candidate list as you type.