import { useState, useEffect, useRef } from 'react' import type { SearchParams, UserSettings } from '@/types' import { useTranslation } from '@/i18n' import { CollapsibleSection } from './CollapsibleSection' import { TagInput } from './TagInput' import { SearchAdapterFactory } from '@/services/adapters' import { EnginePreferenceService } from '@/services/engine-preference' import { useStorage } from '@/hooks/useStorage' import { SyntaxParser } from '@/utils/syntax-parser' interface SearchFormProps { searchParams: SearchParams onSearchParamsChange: (params: SearchParams) => void // Round 2: 支持外部控制的高级选项状态 showAdvanced?: boolean onToggleAdvanced?: (show: boolean) => void // 悬浮面板模式:隐藏引擎选择器(当前页面已确定搜索引擎) hideEngineSelector?: boolean } export function SearchForm({ searchParams, onSearchParamsChange, showAdvanced: externalShowAdvanced, onToggleAdvanced, hideEngineSelector = false }: SearchFormProps) { const [internalShowAdvanced, setInternalShowAdvanced] = useState(false) const { t } = useTranslation() const hasAutoInjectedRef = useRef(false) // 防止重复自动注入 // 使用外部状态或内部状态 const showAdvanced = externalShowAdvanced !== undefined ? externalShowAdvanced : internalShowAdvanced const setShowAdvanced = onToggleAdvanced || setInternalShowAdvanced // 🔥 从用户设置中获取引擎偏好 const { data: settings } = useStorage('user_settings') // 🔥 使用用户偏好的可见引擎列表(已排序) const visibleEngines = EnginePreferenceService.getVisibleEngines(settings?.enginePreferences) // 🔥 获取当前引擎的适配器和支持的功能 const adapter = SearchAdapterFactory.getAdapter(searchParams.engine) const supportedFeatures = adapter.getSupportedFeatures() // 🔥 检查功能是否支持 const isFeatureSupported = (feature: string) => { return supportedFeatures.includes(feature as any) } // 更新搜索参数 const updateParam = (key: K, value: SearchParams[K]) => { const newParams = { ...searchParams, [key]: value } onSearchParamsChange(newParams) } // 🔥 自动解析关键词中的高级语法 useEffect(() => { // 只在第一次展开高级选项时自动解析 if (showAdvanced && !hasAutoInjectedRef.current && searchParams.keyword) { const parsedSyntax = SyntaxParser.parse(searchParams.keyword, searchParams.engine) // 检查是否解析出了有效的语法 const hasValidSyntax = SyntaxParser.isValidParsedSyntax(parsedSyntax) && ( !!parsedSyntax.inTitle || !!parsedSyntax.inUrl || !!parsedSyntax.inText || (parsedSyntax.sites && parsedSyntax.sites.length > 0) || (parsedSyntax.fileTypes && parsedSyntax.fileTypes.length > 0) || (parsedSyntax.exactMatches && parsedSyntax.exactMatches.length > 0) || (parsedSyntax.excludeWords && parsedSyntax.excludeWords.length > 0) ) if (hasValidSyntax) { // 合并解析结果到现有参数 const mergedParams = SyntaxParser.mergeParams(searchParams, parsedSyntax) onSearchParamsChange(mergedParams) hasAutoInjectedRef.current = true } } // 当折叠高级选项时重置标记,允许下次展开时再次解析(如果关键词已变化) if (!showAdvanced) { hasAutoInjectedRef.current = false } }, [showAdvanced, searchParams.keyword]) // 监听高级选项状态和关键词变化 // 切换高级选项 const toggleAdvanced = () => { if (onToggleAdvanced) { onToggleAdvanced(!showAdvanced) } else { setShowAdvanced(!showAdvanced) } } return (
{/* 关键词输入 */}
updateParam('keyword', e.target.value)} placeholder={t('searchForm.keywordPlaceholder')} className="input" autoFocus />
{/* 搜索引擎选择 - 悬浮面板模式下隐藏(当前页面已确定搜索引擎) */} {!hideEngineSelector && (
)} {/* 高级选项切换按钮 */} {/* 🔥 动态高级选项 */} {showAdvanced && (
{/* ====== Twitter 专属功能 ====== */} {/* 用户筛选组 */} {isFeatureSupported('from_user') && ( {isFeatureSupported('from_user') && (
updateParam('fromUsers', tags)} placeholder={t('searchForm.fromUser.placeholder') + t('searchForm.fromUser.multipleHint')} maxTags={10} />

{t('searchForm.fromUser.description')} • {t('searchForm.fromUser.hint')}

)} {isFeatureSupported('to_user') && (
updateParam('toUsers', tags)} placeholder={t('searchForm.toUser.placeholder') + t('searchForm.toUser.multipleHint')} maxTags={10} />

{t('searchForm.toUser.description')} • {t('searchForm.toUser.hint')}

)}
)} {/* 互动筛选组 */} {isFeatureSupported('min_retweets') && ( {isFeatureSupported('min_retweets') && (
updateParam('minRetweets', Number(e.target.value))} placeholder={t('searchForm.minRetweets.placeholder')} className="input" min="0" />

{t('searchForm.minRetweets.description')}

)} {isFeatureSupported('min_faves') && (
updateParam('minFaves', Number(e.target.value))} placeholder={t('searchForm.minFaves.placeholder')} className="input" min="0" />

{t('searchForm.minFaves.description')}

)} {isFeatureSupported('content_filters') && (
{(['images', 'videos', 'links', 'media', 'replies', 'retweets', 'news'] as const).map((filter) => ( ))}

{t('searchForm.contentFilters.description')}

)}
)} {/* ====== 传统搜索引擎功能 ====== */} {/* 位置限定组 */} {isFeatureSupported('site') && ( {isFeatureSupported('site') && (
updateParam('sites', tags)} placeholder={t('searchForm.site.placeholder') + t('searchForm.site.multipleHint')} maxTags={10} />

{t('searchForm.site.description')} • {t('searchForm.site.hint')}

)} {isFeatureSupported('filetype') && (
updateParam('fileTypes', tags)} placeholder={t('searchForm.fileType.placeholder')} maxTags={5} />

{t('searchForm.fileType.description')} • {t('searchForm.fileType.hint')}

)} {isFeatureSupported('intitle') && (
updateParam('inTitle', e.target.value)} placeholder={t('searchForm.inTitle.placeholder')} className="input" />

{t('searchForm.inTitle.description')}

)} {isFeatureSupported('inurl') && (
updateParam('inUrl', e.target.value)} placeholder={t('searchForm.inUrl.placeholder')} className="input" />

{t('searchForm.inUrl.description')}

)} {isFeatureSupported('intext') && (
updateParam('inText', e.target.value)} placeholder={t('searchForm.inText.placeholder')} className="input" />

{t('searchForm.inText.description')}

)}
)} {/* 匹配精度组 */} {isFeatureSupported('exact_match') && ( {isFeatureSupported('exact_match') && (
updateParam('exactMatches', tags)} placeholder={t('searchForm.exactMatch.placeholder') + t('searchForm.exactMatch.multipleHint')} maxTags={5} />

{t('searchForm.exactMatch.description')} • {t('searchForm.exactMatch.hint')}

)} {isFeatureSupported('wildcard') && (
updateParam('wildcardQuery', e.target.value)} placeholder={t('searchForm.wildcardQuery.placeholder')} className="input" />

{t('searchForm.wildcardQuery.description')}

)} {isFeatureSupported('allintitle') && (
updateParam('allInTitle', e.target.value)} placeholder={t('searchForm.allInTitle.placeholder')} className="input" />

{t('searchForm.allInTitle.description')}

)}
)} {/* 逻辑运算组 */} {(isFeatureSupported('or_keywords') || isFeatureSupported('exclude')) && ( {isFeatureSupported('or_keywords') && (
updateParam('orKeywords', tags)} placeholder={t('searchForm.orKeywords.placeholder')} maxTags={5} />

{t('searchForm.orKeywords.description')}

)} {isFeatureSupported('exclude') && (
updateParam('excludeWords', tags)} placeholder={t('searchForm.excludeWords.placeholder')} maxTags={10} />

{t('searchForm.excludeWords.description')}

)}
)} {/* 范围过滤组 */} {(isFeatureSupported('date_range') || isFeatureSupported('number_range')) && ( {isFeatureSupported('date_range') && (
updateParam('dateRange', { from: e.target.value, to: searchParams.dateRange?.to || '' }) } className="input-date" placeholder={t('searchForm.dateRange.from')} />

{t('searchForm.dateRange.from')}

updateParam('dateRange', { from: searchParams.dateRange?.from || '', to: e.target.value }) } className="input-date" placeholder={t('searchForm.dateRange.to')} />

{t('searchForm.dateRange.to')}

{t('searchForm.dateRange.description')}

)} {isFeatureSupported('number_range') && (
updateParam('numberRange', { min: e.target.value ? Number(e.target.value) : undefined, max: searchParams.numberRange?.max } as any) } className="input" placeholder={t('searchForm.numberRange.min')} />

{t('searchForm.numberRange.min')}

updateParam('numberRange', { min: searchParams.numberRange?.min, max: e.target.value ? Number(e.target.value) : undefined } as any) } className="input" placeholder={t('searchForm.numberRange.max')} />

{t('searchForm.numberRange.max')}

{t('searchForm.numberRange.description')}

)}
)} {/* 🔥 Reddit专属 - 板块筛选 */} {searchParams.engine === 'reddit' && (
updateParam('subreddits', tags)} placeholder={t('searchForm.reddit.placeholder')} maxTags={10} />

{t('searchForm.reddit.description')} • {t('searchForm.reddit.hint')}

)} {/* 🔥 GitHub专属 - 编程语言筛选 */} {searchParams.engine === 'github' && (
updateParam('languages', tags)} placeholder={t('searchForm.github.placeholder')} maxTags={5} />

{t('searchForm.github.description')} • {t('searchForm.github.hint')}

)} {/* 🔥 Stack Overflow专属 - 标签筛选 */} {searchParams.engine === 'stackoverflow' && (
updateParam('tags', tags)} placeholder={t('searchForm.stackoverflow.placeholder')} maxTags={10} />

{t('searchForm.stackoverflow.description')} • {t('searchForm.stackoverflow.hint')}

)} {/* 特殊功能组 */} {(isFeatureSupported('related') || isFeatureSupported('cache') || adapter.getLanguageOptions?.()) && ( {isFeatureSupported('related') && (
updateParam('relatedSite', e.target.value)} placeholder={t('searchForm.relatedSite.placeholder')} className="input" />

{t('searchForm.relatedSite.description')}

)} {isFeatureSupported('cache') && (
updateParam('cacheSite', e.target.value)} placeholder={t('searchForm.cacheSite.placeholder')} className="input" />

{t('searchForm.cacheSite.description')}

)} {/* 🔥 动态语言选择器 - 根据适配器配置自动适配 */} {(() => { const languageConfig = adapter.getLanguageOptions?.() if (!languageConfig) return null return (

{searchParams.engine === 'twitter' && t('searchForm.language.description.twitter')} {searchParams.engine === 'github' && t('searchForm.language.description.github')}

) })()}
)}
)}
) }