File size: 4,679 Bytes
f56a29b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
import { useState } from 'react';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { useI18n } from '@/lib/hooks/use-i18n';
import { useSettingsStore } from '@/lib/store/settings';
import { WEB_SEARCH_PROVIDERS } from '@/lib/web-search/constants';
import type { WebSearchProviderId } from '@/lib/web-search/types';
import { Eye, EyeOff } from 'lucide-react';
interface WebSearchSettingsProps {
selectedProviderId: WebSearchProviderId;
}
export function WebSearchSettings({ selectedProviderId }: WebSearchSettingsProps) {
const { t } = useI18n();
const [showApiKey, setShowApiKey] = useState(false);
const webSearchProvidersConfig = useSettingsStore((state) => state.webSearchProvidersConfig);
const setWebSearchProviderConfig = useSettingsStore((state) => state.setWebSearchProviderConfig);
const provider = WEB_SEARCH_PROVIDERS[selectedProviderId];
const isServerConfigured = !!webSearchProvidersConfig[selectedProviderId]?.isServerConfigured;
// Reset showApiKey when provider changes (derived state pattern)
const [prevSelectedProviderId, setPrevSelectedProviderId] = useState(selectedProviderId);
if (selectedProviderId !== prevSelectedProviderId) {
setPrevSelectedProviderId(selectedProviderId);
setShowApiKey(false);
}
return (
<div className="space-y-6 max-w-3xl">
{/* Server-configured notice */}
{isServerConfigured && (
<div className="rounded-lg border border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30 p-3 text-sm text-blue-700 dark:text-blue-300">
{t('settings.serverConfiguredNotice')}
</div>
)}
{/* API Key + Base URL Configuration */}
{(provider.requiresApiKey || isServerConfigured) && (
<>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label className="text-sm">{t('settings.webSearchApiKey')}</Label>
<div className="relative">
<Input
name={`web-search-api-key-${selectedProviderId}`}
type={showApiKey ? 'text' : 'password'}
autoComplete="new-password"
autoCapitalize="none"
autoCorrect="off"
spellCheck={false}
placeholder={
isServerConfigured ? t('settings.optionalOverride') : t('settings.enterApiKey')
}
value={webSearchProvidersConfig[selectedProviderId]?.apiKey || ''}
onChange={(e) =>
setWebSearchProviderConfig(selectedProviderId, {
apiKey: e.target.value,
})
}
className="font-mono text-sm pr-10"
/>
<button
type="button"
onClick={() => setShowApiKey(!showApiKey)}
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
>
{showApiKey ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
<p className="text-xs text-muted-foreground">{t('settings.webSearchApiKeyHint')}</p>
</div>
<div className="space-y-2">
<Label className="text-sm">{t('settings.webSearchBaseUrl')}</Label>
<Input
name={`web-search-base-url-${selectedProviderId}`}
autoComplete="off"
autoCapitalize="none"
autoCorrect="off"
spellCheck={false}
placeholder={provider.defaultBaseUrl || 'https://api.tavily.com'}
value={webSearchProvidersConfig[selectedProviderId]?.baseUrl || ''}
onChange={(e) =>
setWebSearchProviderConfig(selectedProviderId, {
baseUrl: e.target.value,
})
}
className="text-sm"
/>
</div>
</div>
{/* Request URL Preview */}
{(() => {
const effectiveBaseUrl =
webSearchProvidersConfig[selectedProviderId]?.baseUrl ||
provider.defaultBaseUrl ||
'';
if (!effectiveBaseUrl) return null;
const fullUrl = effectiveBaseUrl + '/search';
return (
<p className="text-xs text-muted-foreground break-all">
{t('settings.requestUrl')}: {fullUrl}
</p>
);
})()}
</>
)}
</div>
);
}
|