fix: first round of fixes of implementation
This commit is contained in:
@@ -366,7 +366,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({ conversationId }) => {
|
||||
return Object.entries(groups).map(([provider, models]) => (
|
||||
<React.Fragment key={provider}>
|
||||
{Object.keys(groups).length > 1 && (
|
||||
<div className="model-group-header">{provider === 'mistral' ? 'Mistral' : 'OpenCode'}</div>
|
||||
<div className="model-group-header">{provider === 'mistral' ? tr('settings.ai.providerMistral') : tr('settings.ai.providerOpenCode')}</div>
|
||||
)}
|
||||
{models.map(model => (
|
||||
<button
|
||||
|
||||
@@ -1381,7 +1381,7 @@ const TaxonomySection: React.FC<{
|
||||
return Object.entries(groups).map(([provider, models]) => (
|
||||
<React.Fragment key={provider}>
|
||||
{Object.keys(groups).length > 1 && (
|
||||
<div className="model-group-header">{provider === 'mistral' ? 'Mistral' : 'OpenCode'}</div>
|
||||
<div className="model-group-header">{provider === 'mistral' ? t('settings.ai.providerMistral') : t('settings.ai.providerOpenCode')}</div>
|
||||
)}
|
||||
{models.map(model => (
|
||||
<button
|
||||
|
||||
@@ -1215,15 +1215,23 @@ export const SettingsView: React.FC = () => {
|
||||
};
|
||||
|
||||
// Group models by provider for optgroup display
|
||||
const groupedModels = useMemo(() => {
|
||||
const groupModelsByProvider = useCallback((models: typeof availableModels) => {
|
||||
const groups: Record<string, typeof availableModels> = {};
|
||||
for (const model of availableModels) {
|
||||
for (const model of models) {
|
||||
const provider = model.provider || 'other';
|
||||
if (!groups[provider]) groups[provider] = [];
|
||||
groups[provider].push(model);
|
||||
}
|
||||
return groups;
|
||||
}, [availableModels]);
|
||||
}, []);
|
||||
|
||||
const groupedModels = useMemo(() => groupModelsByProvider(availableModels), [availableModels, groupModelsByProvider]);
|
||||
|
||||
// Vision-capable models only (for image analysis model selector)
|
||||
const groupedVisionModels = useMemo(
|
||||
() => groupModelsByProvider(availableModels.filter(m => m.vision)),
|
||||
[availableModels, groupModelsByProvider]
|
||||
);
|
||||
|
||||
const providerLabel = (provider: string) => {
|
||||
if (provider === 'anthropic' || provider === 'openai' || provider === 'google' || provider === 'other') return t('settings.ai.providerOpenCode');
|
||||
@@ -1232,18 +1240,22 @@ export const SettingsView: React.FC = () => {
|
||||
};
|
||||
|
||||
// Render a model <select> with optgroup by provider
|
||||
const renderModelSelect = (id: string, value: string, onChange: (v: string) => void, disabled?: boolean) => (
|
||||
<select id={id} value={value} onChange={(e) => onChange(e.target.value)} disabled={disabled}>
|
||||
{availableModels.length === 0 && <option value="">{t('settings.ai.noModels')}</option>}
|
||||
{Object.entries(groupedModels).map(([provider, models]) => (
|
||||
<optgroup key={provider} label={providerLabel(provider)}>
|
||||
{models.map(model => (
|
||||
<option key={model.id} value={model.id}>{model.name}</option>
|
||||
))}
|
||||
</optgroup>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
const renderModelSelect = (id: string, value: string, onChange: (v: string) => void, disabled?: boolean, groups?: Record<string, typeof availableModels>) => {
|
||||
const modelGroups = groups || groupedModels;
|
||||
const modelList = Object.values(modelGroups).flat();
|
||||
return (
|
||||
<select id={id} value={value} onChange={(e) => onChange(e.target.value)} disabled={disabled}>
|
||||
{modelList.length === 0 && <option value="">{t('settings.ai.noModels')}</option>}
|
||||
{Object.entries(modelGroups).map(([provider, models]) => (
|
||||
<optgroup key={provider} label={providerLabel(provider)}>
|
||||
{models.map(model => (
|
||||
<option key={model.id} value={model.id}>{model.name}</option>
|
||||
))}
|
||||
</optgroup>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
};
|
||||
|
||||
const renderAISettings = () => (
|
||||
<SettingSection
|
||||
@@ -1384,7 +1396,7 @@ export const SettingsView: React.FC = () => {
|
||||
label={t('settings.ai.imageAnalysisModelLabel')}
|
||||
description={t('settings.ai.imageAnalysisModelDescription')}
|
||||
>
|
||||
{renderModelSelect('ai-image-analysis-model', imageAnalysisModel, handleImageAnalysisModelChange, !aiHasApiKey && !aiHasMistralKey)}
|
||||
{renderModelSelect('ai-image-analysis-model', imageAnalysisModel, handleImageAnalysisModelChange, !aiHasApiKey && !aiHasMistralKey, groupedVisionModels)}
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
|
||||
@@ -734,7 +734,7 @@
|
||||
"settings.ai.modelInfoTokens": "Token",
|
||||
"settings.ai.modelInfoPerMTok": "/MTok",
|
||||
"settings.ai.mistralApiKeyLabel": "Mistral API-Schlüssel",
|
||||
"settings.ai.mistralApiKeyDescription": "Ihr API-Schlüssel von Mistral AI. Ermöglicht Mistral-Modelle als Alternative zu OpenCode.",
|
||||
"settings.ai.mistralApiKeyDescription": "Dein API-Schlüssel von Mistral AI. Ermöglicht Mistral-Modelle als Alternative zu OpenCode.",
|
||||
"settings.ai.mistralApiKeyConfigured": "Mistral API-Schlüssel konfiguriert",
|
||||
"settings.ai.changeMistralApiKey": "Mistral API-Schlüssel ändern",
|
||||
"settings.ai.titleModelLabel": "Titelgenerierungsmodell",
|
||||
@@ -744,7 +744,7 @@
|
||||
"settings.ai.providerOpenCode": "OpenCode",
|
||||
"settings.ai.providerMistral": "Mistral",
|
||||
"settings.ai.providerOther": "Andere",
|
||||
"chat.providerKeyMissing": "Das Modell '{{model}}' benötigt einen {{provider}} API-Schlüssel. Konfigurieren Sie ihn in den Einstellungen.",
|
||||
"chat.providerKeyMissing": "Das Modell '{{model}}' benötigt einen {{provider}} API-Schlüssel. Konfiguriere ihn in den Einstellungen.",
|
||||
"settings.toast.modelCatalogRefreshed": "Modellkatalog aktualisiert ({{count}} Modelle)",
|
||||
"settings.toast.modelCatalogUpToDate": "Modellkatalog ist bereits aktuell",
|
||||
"settings.toast.modelCatalogRefreshFailed": "Modellkatalog konnte nicht aktualisiert werden",
|
||||
|
||||
Reference in New Issue
Block a user