Fuzzy finder overlay for terminal UIs
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.
composer require sugarcraft/candy-hermit
use SugarCraft\Hermit\Hermit;
$hermit = Hermit::new(
items: ['Ruby', 'PHP', 'Go', 'Rust', 'Python'],
)->setPrompt('Choose language: ');
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();
| Class | Method | Description |
|---|---|---|
| Hermit | new(array $items, ?Closure $itemFormatter) | Create fuzzy finder (items: string[] or Item[]) |
| Hermit | withItems(array $items) | Replace items and reset filter state |
| Hermit | setPrompt(string) | Set filter prompt string |
| Hermit | setFilterFn(Closure) | Set custom filter predicate: Closure(Item): bool |
| Hermit | setItemFormatter(Closure) | Set item formatter: Closure(item, isSelected): string |
| Hermit | setMatchStyle(string) | Set ANSI SGR codes for match highlighting |
| Hermit | setWindowHeight(int) | Set visible item window height |
| Hermit | setWindowWidth(int) | Set overlay width (0 = auto) |
| Hermit | setOffset(int $x, int $y) | Position overlay and show |
| Hermit | show() / hide() | Show or hide the overlay |
| Hermit | type(string) / backspace() / clear() | Modify filter text |
| Hermit | cursorUp/Down/Top/Bottom() | Navigate item cursor |
| Hermit | selected() | Get currently selected Item |
| Hermit | items() / itemCount() / allCount() | Query filtered/all item state |
| Hermit | withBorder(?Border) | Apply candy-sprinkles Border to overlay window |
| Hermit | withStyle(?Style) | Apply candy-sprinkles Style to overlay window |
| Hermit | withHelpBar(?HelpBar) | Attach keyboard-shortcut summary below filter list |
| Hermit | withStatusBar(?StatusBar) | Attach status message line at overlay bottom |
| Hermit | withOnResize(Closure) | Register callback invoked on SIGWINCH (cols, rows) |
| Hermit | attachSigwinch() | Install SIGWINCH handler via SignalForwarder (requires ext-pcntl) |
| Hermit | border() / style() / helpBar() / statusBar() | Query attached border, style, help bar, status bar |
| Hermit | View(string $backgroundView) | Render composited overlay over background |
| HelpBar | new(array $shortcuts, bool $visible) | Create help bar with key→description pairs |
| HelpBar | withShortcut(key, desc) / withoutShortcut(key) | Add/remove shortcut entry |
| HelpBar | show() / hide() / isVisible() | Control visibility |
| HelpBar | render() | Render as "key: desc │ key: desc ..." string |
| StatusBar | new(string $message, bool $visible) | Create status bar with message |
| StatusBar | withMessage(string) / withNoMessage() | Set or clear primary message |
| StatusBar | withSegment(name, value) / withoutSegment(name) | Add/remove named segment |
| StatusBar | show() / hide() / isVisible() | Control visibility |
| StatusBar | render() | Render as "[name: value] message" string |
| Item | interface number(), value() | Contract for filterable items |
| FilteredItem | new(int $number, string $value) | Numbered Item implementation |
| FileHistory | new(string $path) | Open/create JSONL history file |
| FileHistory | append(Item) | Append item as JSON line |
| FileHistory | all() | Read all items from history |
| FileHistory | clear() / path() | Clear history or get file path |
| Model | interface update(Hermit, string), view(Hermit) | Bubble-Tea embedding contract |
VHS-recorded GIFs of every example shipped with the library. Regenerated automatically on every push that touches the source.