'use client'; import { useState } from 'react'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { useI18n } from '@/lib/hooks/use-i18n'; import { useSettingsStore } from '@/lib/store/settings'; import { BAIDU_SUB_SOURCES, CLAUDE_WEB_SEARCH_DEFAULT_MODEL, CLAUDE_WEB_SEARCH_MODELS, WEB_SEARCH_PROVIDERS, } from '@/lib/web-search/constants'; import type { BaiduSubSources, WebSearchProviderId } from '@/lib/web-search/types'; import { ExternalLink, 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 baiduSubSources = useSettingsStore((state) => state.baiduSubSources); const setBaiduSubSources = useSettingsStore((state) => state.setBaiduSubSources); const provider = WEB_SEARCH_PROVIDERS[selectedProviderId]; const isServerConfigured = !!webSearchProvidersConfig[selectedProviderId]?.isServerConfigured; const isOperatorManagedBaseUrl = selectedProviderId === 'searxng'; // Managed providers are admin-owned: hide the key/base-URL override inputs. const showCredentialFields = !isServerConfigured && !isOperatorManagedBaseUrl; const buildRequestUrl = (baseUrl: string) => { const trimmed = baseUrl.replace(/\/$/, ''); if (!provider.endpointPath) return trimmed; if (trimmed.endsWith(provider.endpointPath)) return trimmed; return `${trimmed}${provider.endpointPath}`; }; // Reset showApiKey when provider changes (derived state pattern) const [prevSelectedProviderId, setPrevSelectedProviderId] = useState(selectedProviderId); if (selectedProviderId !== prevSelectedProviderId) { setPrevSelectedProviderId(selectedProviderId); setShowApiKey(false); } return (
{/* Server-configured notice */} {isServerConfigured && (
{t('settings.serverConfiguredNotice')}
)} {isOperatorManagedBaseUrl && !isServerConfigured && (
{t('settings.searxngServerOnlyNotice')}
)} {!provider.requiresApiKey && !isServerConfigured && !isOperatorManagedBaseUrl && (
{t('settings.webSearchApiKeyOptional')}
)} {/* API Key + Base URL Configuration */} {showCredentialFields && ( <>
setWebSearchProviderConfig(selectedProviderId, { apiKey: e.target.value, }) } className="font-mono text-sm pr-10" />

{t('settings.webSearchApiKeyHint')}

setWebSearchProviderConfig(selectedProviderId, { baseUrl: e.target.value, }) } className="text-sm" />
{/* Request URL Preview */} {(() => { const effectiveBaseUrl = webSearchProvidersConfig[selectedProviderId]?.baseUrl || provider.defaultBaseUrl || ''; if (!effectiveBaseUrl) return null; const fullUrl = buildRequestUrl(effectiveBaseUrl); return (

{t('settings.requestUrl')}: {fullUrl}

); })()} )} {/* Claude search model — hidden for managed providers, where the operator pins the model via WEB_SEARCH_CLAUDE_MODELS */} {selectedProviderId === 'claude' && !isServerConfigured && (

{t('settings.claudeSearchModelHint')}

)} {selectedProviderId === 'baidu' && (

{t('settings.baiduSubSourcesDescription')}

{( Object.entries(BAIDU_SUB_SOURCES) as Array< [keyof BaiduSubSources, (typeof BAIDU_SUB_SOURCES)[keyof typeof BAIDU_SUB_SOURCES]] > ).map(([key, meta]) => { const enabled = baiduSubSources[key]; return (
{t(meta.labelKey)}
{t(meta.descriptionKey)} {meta.docsUrl && ( {t('settings.viewDocs')} )}
setBaiduSubSources({ [key]: checked })} aria-label={t(meta.labelKey)} />
); })}
)}
); }