Skip to content

Commit eb75012

Browse files
fix(ModelPicker): show current model at the top of the select options
1 parent a62e890 commit eb75012

2 files changed

Lines changed: 72 additions & 3 deletions

File tree

src/components/ModelPicker.test.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import { ModelPicker } from './ModelPicker';
4343

4444
describe('ModelPicker', () => {
4545
beforeEach(() => {
46+
mockListModels.mockReset();
47+
mockOnChange.mockReset();
4648
mockListModels.mockResolvedValue(['gemma4', 'llama3', 'codellama']);
4749
});
4850

@@ -89,6 +91,67 @@ describe('ModelPicker', () => {
8991
expect(lastFrame()).toContain('llama3');
9092
});
9193

94+
it('renders current model first in the list', async () => {
95+
const { lastFrame } = render(
96+
<ModelPicker
97+
currentModel="llama3"
98+
onSelect={vi.fn()}
99+
onClose={vi.fn()}
100+
/>,
101+
);
102+
103+
await test.tick(10);
104+
105+
const frame = lastFrame() ?? '';
106+
expect(frame.indexOf('llama3')).toBeLessThan(frame.indexOf('gemma4'));
107+
expect(frame.indexOf('llama3')).toBeLessThan(frame.indexOf('codellama'));
108+
});
109+
110+
it('does not inject the current model when it is not in the fetched list', async () => {
111+
mockListModels.mockResolvedValue(['gemma4', 'codellama']);
112+
113+
const { lastFrame } = render(
114+
<ModelPicker
115+
currentModel="llama3"
116+
onSelect={vi.fn()}
117+
onClose={vi.fn()}
118+
/>,
119+
);
120+
121+
await test.tick(10);
122+
123+
const frame = lastFrame() ?? '';
124+
expect(frame).toContain('gemma4');
125+
expect(frame).toContain('codellama');
126+
expect(frame).not.toContain('llama3');
127+
});
128+
129+
it('reloads and reorders options when currentModel changes', async () => {
130+
const { lastFrame, rerender } = render(
131+
<ModelPicker
132+
currentModel="gemma4"
133+
onSelect={vi.fn()}
134+
onClose={vi.fn()}
135+
/>,
136+
);
137+
138+
await test.tick(10);
139+
140+
rerender(
141+
<ModelPicker
142+
currentModel="llama3"
143+
onSelect={vi.fn()}
144+
onClose={vi.fn()}
145+
/>,
146+
);
147+
148+
await test.tick(10);
149+
150+
const frame = lastFrame() ?? '';
151+
expect(mockListModels).toHaveBeenCalledTimes(2);
152+
expect(frame.indexOf('llama3')).toBeLessThan(frame.indexOf('gemma4'));
153+
});
154+
92155
it('calls onSelect when a model is chosen', async () => {
93156
const onSelect = vi.fn();
94157
render(

src/components/ModelPicker.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,21 @@ export function ModelPicker({ currentModel, onSelect, onClose }: Props) {
2727
useEffect(() => {
2828
async function load() {
2929
try {
30-
const list = await ollama.listModels();
31-
setOptions(list.map((name) => ({ label: name, value: name })));
30+
const models = await ollama.listModels();
31+
if (models.includes(currentModel)) {
32+
models.splice(models.indexOf(currentModel), 1);
33+
models.unshift(currentModel);
34+
}
35+
36+
const options = models.map((model) => ({ label: model, value: model }));
37+
setOptions(options);
3238
} catch (error: unknown) {
3339
setError(error instanceof Error ? error.message : String(error));
3440
}
3541
}
3642

3743
void load();
38-
}, []);
44+
}, [currentModel]);
3945

4046
if (error) {
4147
return <Text color="red">Error loading models: {error}</Text>;

0 commit comments

Comments
 (0)