Skip to content

Commit c91a7ff

Browse files
committed
Merge branch 'RAHUL-art-create/main'
2 parents de9865d + f6b5619 commit c91a7ff

File tree

9 files changed

+272
-468
lines changed

9 files changed

+272
-468
lines changed

src/components/ModelSelect.tsx

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
import { ChevronDown } from "lucide-react";
2-
import { useEffect, useRef, useState } from "react";
2+
import { useCallback, useEffect, useRef, useState } from "react";
33
import { useAppSettings } from "../contexts/AppSettingsContext";
44
import { useModels } from "../hooks/useModels";
55
import type { LLMProvider } from "../types";
66
import { PROVIDERS } from "../utils/providers";
77
import Portal from "./Portal";
88

99
interface ModelSelectProps {
10-
provider: LLMProvider;
11-
apiKey?: string;
1210
value: string;
1311
onChange: (value: string) => void;
1412
}
1513

16-
export function ModelSelect({
17-
provider: _provider,
18-
apiKey: _apiKey,
19-
value,
20-
onChange,
21-
}: ModelSelectProps) {
14+
export function ModelSelect({ value, onChange }: ModelSelectProps) {
2215
const { settings, setSettings } = useAppSettings();
23-
const { models, isLoading } = useModels(settings.selectedProvider, settings);
16+
const { models, isLoading } = useModels(
17+
settings.selectedProvider,
18+
settings,
19+
setSettings,
20+
);
2421
const [isOpen, setIsOpen] = useState(false);
2522
const [showProviders, setShowProviders] = useState(false);
2623
const [buttonRect, setButtonRect] = useState<DOMRect | null>(null);
@@ -33,7 +30,7 @@ export function ModelSelect({
3330
const target = event.target as Node;
3431

3532
// Check if click is on the button
36-
if (buttonRef.current && buttonRef.current.contains(target)) {
33+
if (buttonRef.current?.contains(target)) {
3734
return;
3835
}
3936

@@ -56,37 +53,54 @@ export function ModelSelect({
5653
return () => document.removeEventListener("mousedown", handleClickOutside);
5754
}, []);
5855

59-
const handleProviderChange = (newProvider: LLMProvider) => {
60-
setSettings({
61-
selectedProvider: newProvider,
62-
// Model will be auto-set to default for the new provider
63-
});
64-
setShowProviders(false);
65-
};
56+
const handleProviderChange = useCallback(
57+
(newProvider: LLMProvider) => {
58+
setSettings({
59+
selectedProvider: newProvider,
60+
// Model will be auto-set to default for the new provider
61+
});
62+
setShowProviders(false);
63+
},
64+
[setSettings],
65+
);
66+
67+
const handleModelChange = useCallback(
68+
(model: string) => {
69+
// Call the parent onChange first
70+
onChange(model);
6671

67-
const handleRightClick = (e: React.MouseEvent) => {
72+
// Only update settings if the model is actually different
73+
if (model !== settings.model) {
74+
setSettings({ model });
75+
}
76+
77+
setIsOpen(false);
78+
},
79+
[onChange, settings.model, setSettings],
80+
);
81+
82+
const handleRightClick = useCallback((e: React.MouseEvent) => {
6883
e.preventDefault();
6984
if (buttonRef.current) {
7085
setButtonRect(buttonRef.current.getBoundingClientRect());
7186
}
7287
setIsOpen(false);
7388
setShowProviders(true);
74-
};
89+
}, []);
7590

76-
const handleLeftClick = () => {
91+
const handleLeftClick = useCallback(() => {
7792
if (buttonRef.current) {
7893
setButtonRect(buttonRef.current.getBoundingClientRect());
7994
}
8095
setShowProviders(false);
8196
setIsOpen(!isOpen);
82-
};
97+
}, [isOpen]);
8398

8499
const providerOptions = Object.entries(PROVIDERS).map(([key, info]) => ({
85100
value: key as LLMProvider,
86101
label: info.label,
87102
}));
88103

89-
// Use settings.selectedProvider instead of prop to ensure consistency
90104
const currentProvider = settings.selectedProvider;
91105

92106
return (
@@ -136,10 +150,7 @@ export function ModelSelect({
136150
<button
137151
key={model}
138152
type="button"
139-
onClick={() => {
140-
onChange(model);
141-
setIsOpen(false);
142-
}}
153+
onClick={() => handleModelChange(model)}
143154
className={`w-full text-left px-2 py-2 text-sm rounded-lg transition-colors duration-150 ${
144155
model === value
145156
? "bg-blue-50 text-blue-700 font-medium"

src/components/ModelSelector.tsx

Lines changed: 12 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import { Plus, RefreshCw, X } from "lucide-react";
2-
import { useState } from "react";
1+
import { RefreshCw } from "lucide-react";
32
import { useModels } from "../hooks/useModels";
43
import type { AppSettings, LLMProvider } from "../types";
54
import { PROVIDERS } from "../utils/providers";
6-
import Button from "./ui/Button";
7-
import Input from "./ui/Input";
85
import Select from "./ui/Select";
96

107
interface ModelSelectorProps {
@@ -20,48 +17,21 @@ export default function ModelSelector({
2017
selectedModel,
2118
settings,
2219
onModelChange,
23-
onSettingsChange: _onSettingsChange,
20+
onSettingsChange,
2421
}: ModelSelectorProps) {
25-
const {
26-
models,
27-
isLoading,
28-
error,
29-
refreshModels,
30-
addCustomModel,
31-
removeCustomModel,
32-
} = useModels(provider, settings);
33-
const [showCustomInput, setShowCustomInput] = useState(false);
34-
const [customModelInput, setCustomModelInput] = useState("");
22+
const { models, isLoading, error, refreshModels } = useModels(
23+
provider,
24+
settings,
25+
onSettingsChange,
26+
);
3527

3628
const modelOptions = models.map((model) => ({
3729
value: model,
3830
label: model,
3931
}));
4032

41-
const handleAddCustomModel = async () => {
42-
if (!customModelInput.trim()) return;
43-
44-
await addCustomModel(customModelInput.trim());
45-
onModelChange(customModelInput.trim());
46-
setCustomModelInput("");
47-
setShowCustomInput(false);
48-
};
49-
50-
const handleRemoveCustomModel = async (model: string) => {
51-
await removeCustomModel(model);
52-
if (selectedModel === model && models.length > 0) {
53-
onModelChange(models[0]);
54-
}
55-
};
56-
57-
const isCustomModel = (model: string) => {
58-
const customModels = settings.customModels?.[provider] || [];
59-
return customModels.includes(model);
60-
};
61-
6233
const providerInfo = PROVIDERS[provider];
6334
const showRefreshButton = providerInfo?.supportsModelRefresh;
64-
const showAddCustomButton = providerInfo?.supportsCustomModels;
6535

6636
return (
6737
<div className="space-y-3">
@@ -88,16 +58,6 @@ export default function ModelSelector({
8858
/>
8959
</button>
9060
)}
91-
{showAddCustomButton && (
92-
<button
93-
type="button"
94-
onClick={() => setShowCustomInput(!showCustomInput)}
95-
className="p-1 text-gray-400 hover:text-gray-600"
96-
title="Add custom model"
97-
>
98-
<Plus className="w-4 h-4" />
99-
</button>
100-
)}
10161
</div>
10262
</div>
10363

@@ -111,80 +71,18 @@ export default function ModelSelector({
11171
<Select
11272
options={modelOptions}
11373
value={selectedModel}
114-
onChange={onModelChange}
74+
onChange={(model) => {
75+
onModelChange(model);
76+
onSettingsChange({ model });
77+
}}
11578
disabled={isLoading}
11679
placeholder={isLoading ? "Loading models..." : "Select a model"}
11780
/>
118-
119-
{showAddCustomButton && showCustomInput && (
120-
<div className="flex gap-2">
121-
<Input
122-
label=""
123-
value={customModelInput}
124-
onChange={(e) => setCustomModelInput(e.target.value)}
125-
placeholder="Enter custom model name (e.g., llama3.2:7b)"
126-
onKeyDown={(e) => {
127-
if (e.key === "Enter") {
128-
e.preventDefault();
129-
handleAddCustomModel();
130-
}
131-
if (e.key === "Escape") {
132-
setShowCustomInput(false);
133-
setCustomModelInput("");
134-
}
135-
}}
136-
/>
137-
<Button
138-
onClick={handleAddCustomModel}
139-
disabled={!customModelInput.trim()}
140-
size="sm"
141-
>
142-
Add
143-
</Button>
144-
<Button
145-
onClick={() => {
146-
setShowCustomInput(false);
147-
setCustomModelInput("");
148-
}}
149-
variant="outline"
150-
size="sm"
151-
>
152-
Cancel
153-
</Button>
154-
</div>
155-
)}
15681
</div>
15782

158-
{/* Show custom models with remove option - only for providers that support custom models */}
159-
{showAddCustomButton && models.some((model) => isCustomModel(model)) && (
160-
<div className="space-y-1">
161-
<div className="text-xs text-gray-500">Custom Models:</div>
162-
<div className="flex flex-wrap gap-1">
163-
{models
164-
.filter((model) => isCustomModel(model))
165-
.map((model) => (
166-
<div
167-
key={model}
168-
className="flex items-center gap-1 bg-blue-50 text-blue-700 px-2 py-1 rounded text-xs"
169-
>
170-
<span>{model}</span>
171-
<button
172-
type="button"
173-
onClick={() => handleRemoveCustomModel(model)}
174-
className="text-blue-500 hover:text-blue-700"
175-
title="Remove custom model"
176-
>
177-
<X className="w-3 h-3" />
178-
</button>
179-
</div>
180-
))}
181-
</div>
182-
</div>
183-
)}
184-
18583
{models.length === 0 && !isLoading && (
18684
<div className="text-xs text-gray-500 bg-gray-50 p-2 rounded">
187-
No models available. Try refreshing or add a custom model.
85+
No models available. Try refreshing the list.
18886
</div>
18987
)}
19088
</div>

0 commit comments

Comments
 (0)