@matthesketh/ink-fuzzy-select
v0.1.0
Published
Filterable select input with fuzzy matching for Ink 5
Maintainers
Readme
@matthesketh/ink-fuzzy-select
Filterable select input with fuzzy matching for Ink 5. Type to filter, arrow keys to navigate, enter to select.
All existing fuzzy-select packages for Ink target Ink 0.x or 2.x and are no longer maintained. This package is built for Ink 5 with React 18.
Install
npm install @matthesketh/ink-fuzzy-selectUsage
import React from "react";
import { render } from "ink";
import { FuzzySelect } from "@matthesketh/ink-fuzzy-select";
const items = [
{ label: "JavaScript", value: "js" },
{ label: "TypeScript", value: "ts" },
{ label: "Python", value: "py" },
{ label: "Rust", value: "rs" },
{ label: "Go", value: "go" },
];
function App() {
return (
<FuzzySelect
items={items}
onSelect={(item) => {
console.log("Selected:", item.value);
process.exit(0);
}}
onCancel={() => {
console.log("Cancelled");
process.exit(0);
}}
placeholder="Search languages..."
maxVisible={5}
/>
);
}
render(<App />);Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| items | FuzzySelectItem[] | (required) | Array of items to select from |
| onSelect | (item: FuzzySelectItem) => void | (required) | Called when an item is selected with Enter |
| onCancel | () => void | undefined | Called when Escape is pressed |
| placeholder | string | "Type to filter..." | Shown in the input when the query is empty |
| maxVisible | number | 10 | Maximum number of items visible in the list |
| renderItem | (item, selected, highlighted) => ReactNode | undefined | Custom item renderer |
FuzzySelectItem
interface FuzzySelectItem {
label: string;
value: string;
}Keyboard
| Key | Action |
|-----|--------|
| Any character | Appends to the filter query |
| Backspace | Removes the last character from the query |
| Up / Down | Navigate the filtered list |
| Enter | Select the highlighted item |
| Escape | Cancel (calls onCancel) |
Fuzzy matching
The built-in fuzzy matcher checks whether all query characters appear in order in the label (case-insensitive). Results are scored by:
- Consecutive matches -- +2 points for each character that immediately follows the previous match
- Start-of-string -- +3 points if the first query character matches the first character of the label
Higher-scoring matches appear first in the filtered list.
You can also import the matcher directly:
import { fuzzyMatch } from "@matthesketh/ink-fuzzy-select";
const result = fuzzyMatch("ts", "TypeScript");
// { matches: true, score: 3, indices: [0, 5] }Custom rendering
Use renderItem to control how each item is displayed. The highlighted parameter is a string with matched characters wrapped in brackets (e.g. "[T]ype[S]cript").
<FuzzySelect
items={items}
onSelect={handleSelect}
renderItem={(item, selected, highlighted) => (
<Text color={selected ? "green" : "white"}>
{selected ? "> " : " "}
{highlighted} ({item.value})
</Text>
)}
/>Requirements
- Ink >= 5.0.0
- React >= 18.0.0
