Skip to content

Commit d1b0049

Browse files
authored
feat: add search filter to model picker (#26)
1 parent a7b9b2b commit d1b0049

1 file changed

Lines changed: 62 additions & 6 deletions

File tree

apps/tui/src/components/app.tsx

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,25 @@ function Dashboard({
118118
const [modelPicker, setModelPicker] = useState(false);
119119
const [modelOptions, setModelOptions] = useState<SelectOption[]>([]);
120120
const [fetchingModels, setFetchingModels] = useState(false);
121+
const [query, setQuery] = useState("");
122+
const [cursorOn, setCursorOn] = useState(true);
123+
124+
useEffect(() => {
125+
if (!modelPicker) return;
126+
const id = setInterval(() => setCursorOn((v) => !v), 500);
127+
return () => clearInterval(id);
128+
}, [modelPicker]);
129+
130+
const q = query.toLowerCase();
131+
const visibleOptions = q
132+
? modelOptions.filter(
133+
(o) =>
134+
o.value !== SEPARATOR_VALUE &&
135+
(o.name.toLowerCase().includes(q) ||
136+
(typeof o.description === "string" &&
137+
o.description.toLowerCase().includes(q))),
138+
)
139+
: modelOptions;
121140

122141
const refresh = useCallback(
123142
async (nextIndex = selectedIndex) => {
@@ -160,8 +179,24 @@ function Dashboard({
160179

161180
useKeyboard((key) => {
162181
if (modelPicker) {
163-
if (key.name === "escape" || key.name === "q") {
182+
if (key.name === "escape") {
164183
setModelPicker(false);
184+
setQuery("");
185+
return;
186+
}
187+
if (key.name === "backspace") {
188+
setQuery((prev) => prev.slice(0, -1));
189+
return;
190+
}
191+
if (
192+
!key.ctrl &&
193+
!key.meta &&
194+
typeof key.sequence === "string" &&
195+
key.sequence.length === 1 &&
196+
key.sequence >= " " &&
197+
key.sequence <= "~"
198+
) {
199+
setQuery((prev) => prev + key.sequence);
165200
}
166201
return;
167202
}
@@ -177,6 +212,7 @@ function Dashboard({
177212
}
178213

179214
if (key.name === "m" && !fetchingModels) {
215+
setQuery("");
180216
setFetchingModels(true);
181217
void fetchModelOptions()
182218
.then((options) => {
@@ -222,16 +258,36 @@ function Dashboard({
222258
if (modelPicker) {
223259
return (
224260
<box flexDirection="column" flexGrow={1} padding={1}>
225-
<box flexDirection="column" marginBottom={1}>
261+
<box
262+
flexDirection="row"
263+
justifyContent="space-between"
264+
marginBottom={1}
265+
>
226266
<text attributes={TextAttributes.BOLD}>Select Model</text>
227-
<text attributes={TextAttributes.DIM}>
228-
{`${modelOptions.length} models available — esc: cancel`}
229-
</text>
267+
<text attributes={TextAttributes.DIM}>esc</text>
268+
</box>
269+
<box
270+
border
271+
borderStyle="single"
272+
borderColor="#666"
273+
paddingLeft={1}
274+
paddingRight={1}
275+
marginBottom={1}
276+
height={3}
277+
>
278+
{query ? (
279+
<text>{`${query}${cursorOn ? "\u2588" : " "}`}</text>
280+
) : (
281+
<text attributes={TextAttributes.DIM}>
282+
{`${cursorOn ? "\u2588" : "S"}earch`}
283+
</text>
284+
)}
230285
</box>
231286
<select
287+
key={query}
232288
focused
233289
flexGrow={1}
234-
options={modelOptions}
290+
options={visibleOptions}
235291
showDescription
236292
showScrollIndicator
237293
wrapSelection

0 commit comments

Comments
 (0)