search-syntax-pro/docs/archived-specs/search-engine-adapters-expansion/requirements-spec.md
Zain 4c52de98ad feat(search): 支持Yandex和DuckDuckGo搜索引擎
- 新增Yandex和DuckDuckGo搜索引擎适配器配置
- 更新manifest.json添加新搜索引擎权限
- 优化URL工具类支持更多搜索引擎参数解析
- 移除调试日志输出
- 修复百度搜索输入框选择器兼容性问题
- 添加文档归档和历史需求规范

版本升级至1.8.6
2025-12-02 16:28:56 +08:00

1579 行
45 KiB
Markdown

# Technical Specification - Search Engine Adapters Expansion
**Project**: SearchSyntax Pro v1.8.1
**Feature**: 新增 7 个搜索引擎适配器支持
**Document Version**: 1.0
**Created**: 2025-11-26
**Status**: Ready for Implementation
---
## 1. Problem Statement
### Business Issue
SearchSyntax Pro 当前仅支持 10 个搜索引擎,缺乏对多个主流搜索引擎的支持,限制了用户在特定市场和隐私友好搜索场景下的选择。
### Current State
- 已支持引擎: Baidu, Google, Bing, DuckDuckGo, Brave, Yandex, Twitter, Reddit, GitHub, Stack Overflow
- 缺失引擎: Yahoo, Startpage, Ecosia, Qwant, Naver, Sogou, 360搜索
- 用户无法使用这些搜索引擎的高级语法功能
### Expected Outcome
添加 7 个新搜索引擎适配器,所有语法支持与官方搜索引擎能力保持一致,提供完整的中英文国际化支持。
---
## 2. Solution Overview
### Approach
采用现有适配器模式,通过复用 Google/Bing/Baidu 适配器逻辑,实现 7 个新搜索引擎的适配器。分三个阶段实施,优先兼容性引擎,再处理区域市场引擎。
### Core Changes
1. **适配器实现**: 创建 7 个新的适配器类文件
2. **类型定义**: 扩展 `SearchEngine` 类型
3. **工厂注册**: 在 factory.ts 注册新适配器
4. **国际化**: 添加引擎名称和验证消息翻译
5. **测试**: 为核心逻辑编写单元测试
### Success Criteria
- 所有 7 个引擎可正常构建查询 URL
- 语法支持与官方文档一致
- 多关键词 OR 组合在支持的引擎中正常工作
- 语法降级正确处理不支持的语法
- 所有用户可见文本有中英文翻译
- 核心逻辑单元测试通过
---
## 3. Technical Implementation
### 3.1 Database Changes
**无数据库变更** - 仅使用 Chrome Storage API
### 3.2 Code Changes
#### Phase 1: 兼容性引擎 (3个)
##### 3.2.1 Yahoo Search Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/yahoo.ts`
**实现规格**:
```typescript
/**
* Yahoo 搜索引擎适配器
* 基于 Bing 语法兼容性实现
*/
export class YahooAdapter implements SearchEngineAdapter {
getName(): string {
return '雅虎' // 将通过 i18n 翻译
}
getBaseUrl(): string {
return 'https://search.yahoo.com/search'
}
// 查询参数格式: ?p={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?p=${encodeURIComponent(query)}`
}
// 支持的语法 (与 Bing 相同)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact',
'date_range',
'intitle',
'inurl',
'exclude',
'or',
'intext', // Yahoo 使用 inbody:
'number_range',
'wildcard',
'allintitle', // 降级为多个 intitle
'related'
// 不支持 'cache'
]
}
// 多关键词支持 (与 Bing 一致)
// - sites: 支持 OR 组合
// - fileTypes: 支持 OR 组合
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
if (params.cacheSite) {
console.warn('Yahoo不支持cache语法,该参数将被忽略')
degradedParams.cacheSite = undefined
}
return degradedParams
}
// UI 功能特性
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype', 'intitle', 'inurl', 'intext',
'exact_match', 'exclude', 'or_keywords',
'date_range', 'related'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype', 'intitle', 'inurl', 'intext'],
precision: ['exact_match'],
logic: ['exclude', 'or_keywords'],
range: ['date_range'],
special: ['related']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://search.yahoo.com/search`
- **查询参数**: `?p=` (区别于 Google 的 `?q=`)
- **语法处理**: 完全复用 BingAdapter 的 buildSearchQuery 逻辑
- **intext 映射**: 使用 `inbody:` (与 Bing 一致)
- **allintitle 降级**: 转换为多个 `intitle:` (与 Bing 一致)
- **cache 降级**: 设为 undefined 并记录警告
##### 3.2.2 Startpage Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/startpage.ts`
**实现规格**:
```typescript
/**
* Startpage 搜索引擎适配器
* 基于 Google 语法兼容性实现 (隐私友好)
*/
export class StartpageAdapter implements SearchEngineAdapter {
getName(): string {
return 'Startpage'
}
getBaseUrl(): string {
return 'https://www.startpage.com/sp/search'
}
// 查询参数格式: ?query={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?query=${encodeURIComponent(query)}`
}
// 支持的语法 (Google 子集)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact',
'date_range', // before:/after:
'intitle',
'exclude',
'or'
// 不支持: inurl, intext, wildcard, allintitle, related, cache, number_range
]
}
// 多关键词支持 (与 Google 一致)
// - sites: 支持 OR 组合
// - fileTypes: 支持 OR 组合
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
const warnings: string[] = []
// Startpage 不支持的语法直接忽略
if (params.inUrl) {
degradedParams.inUrl = undefined
warnings.push('Startpage不支持inurl语法')
}
if (params.inText) {
degradedParams.inText = undefined
warnings.push('Startpage不支持intext语法')
}
if (params.wildcardQuery) {
degradedParams.wildcardQuery = undefined
warnings.push('Startpage不支持通配符语法')
}
if (params.allInTitle) {
degradedParams.allInTitle = undefined
warnings.push('Startpage不支持allintitle语法')
}
if (params.relatedSite) {
degradedParams.relatedSite = undefined
warnings.push('Startpage不支持related语法')
}
if (params.cacheSite) {
degradedParams.cacheSite = undefined
warnings.push('Startpage不支持cache语法')
}
if (params.numberRange) {
degradedParams.numberRange = undefined
warnings.push('Startpage不支持数字范围语法')
}
if (warnings.length > 0) {
console.warn(`[Startpage] 语法降级:`, warnings.join('; '))
}
return degradedParams
}
// 日期格式: before:YYYY-MM-DD after:YYYY-MM-DD (与 Google 一致)
private buildDateFilter(dateRange: { from: string; to: string }): string {
const { from, to } = dateRange
if (from && to) {
return `after:${this.formatDate(from)} before:${this.formatDate(to)}`
} else if (from) {
return `after:${this.formatDate(from)}`
} else if (to) {
return `before:${this.formatDate(to)}`
}
return ''
}
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype', 'intitle',
'exact_match', 'exclude', 'or_keywords',
'date_range'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype', 'intitle'],
precision: ['exact_match'],
logic: ['exclude', 'or_keywords'],
range: ['date_range']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://www.startpage.com/sp/search`
- **查询参数**: `?query=` (注意不是 `?q=`)
- **语法处理**: 复用 GoogleAdapter 核心逻辑,但过滤不支持的语法
- **日期格式**: `before:` / `after:` (与 Google 一致)
- **语法降级**: 大量不支持语法需要降级处理
##### 3.2.3 Ecosia Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/ecosia.ts`
**实现规格**:
```typescript
/**
* Ecosia 搜索引擎适配器
* 基于 Google/Bing 混合语法实现 (环保搜索)
*/
export class EcosiaAdapter implements SearchEngineAdapter {
getName(): string {
return 'Ecosia'
}
getBaseUrl(): string {
return 'https://www.ecosia.org/search'
}
// 查询参数格式: ?q={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?q=${encodeURIComponent(query)}`
}
// 支持的语法 (Google/Bing 基础子集)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact',
'exclude',
'or'
// 官方文档仅明确支持这些基础操作符
// 不支持: date_range, intitle, inurl, intext 等高级语法
]
}
// 多关键词支持 (与 Google/Bing 一致)
// - sites: 支持 OR 组合
// - fileTypes: 支持 OR 组合
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
const warnings: string[] = []
// Ecosia 仅支持基础语法
const unsupportedFields = [
'dateRange', 'inTitle', 'inUrl', 'inText', 'allInTitle',
'numberRange', 'wildcardQuery', 'relatedSite', 'cacheSite'
]
unsupportedFields.forEach(field => {
if (params[field as keyof SearchParams]) {
(degradedParams as any)[field] = undefined
warnings.push(`Ecosia不支持${field}语法`)
}
})
if (warnings.length > 0) {
console.warn(`[Ecosia] 语法降级:`, warnings.join('; '))
}
return degradedParams
}
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype',
'exact_match', 'exclude', 'or_keywords'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype'],
precision: ['exact_match'],
logic: ['exclude', 'or_keywords']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://www.ecosia.org/search`
- **查询参数**: `?q=`
- **语法处理**: 仅实现基础语法 (site, filetype, exact, exclude, OR)
- **语法降级**: 大量高级语法不支持,需要全部降级
#### Phase 2: 区域市场引擎 (3个)
##### 3.2.4 Naver Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/naver.ts`
**实现规格**:
```typescript
/**
* Naver 搜索引擎适配器
* 韩国市场第一大搜索引擎
*/
export class NaverAdapter implements SearchEngineAdapter {
getName(): string {
return 'Naver' // 不翻译为韩语,仅中英文
}
getBaseUrl(): string {
return 'https://search.naver.com/search.naver'
}
// 查询参数格式: ?query={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?query=${encodeURIComponent(query)}`
}
// 支持的语法 (基础子集)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact',
'exclude'
// Naver 官方文档支持有限,不支持 OR, intitle, inurl 等高级语法
]
}
// 多关键词支持 (有限)
// - sites: 不支持 OR 组合 (单个 site: 仅)
// - fileTypes: 支持基础文件类型 (pdf, doc, xls, ppt, hwp)
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
const warnings: string[] = []
// Naver 不支持多站点 OR 组合
if (params.sites && params.sites.length > 1) {
degradedParams.sites = [params.sites[0]] // 仅保留第一个
warnings.push('Naver不支持多站点OR组合,仅使用第一个站点')
}
// Naver 不支持多文件类型 OR 组合
if (params.fileTypes && params.fileTypes.length > 1) {
degradedParams.fileTypes = [params.fileTypes[0]]
warnings.push('Naver不支持多文件类型OR组合,仅使用第一个类型')
}
// 不支持的高级语法
const unsupportedFields = [
'orKeywords', 'dateRange', 'inTitle', 'inUrl', 'inText',
'allInTitle', 'numberRange', 'wildcardQuery', 'relatedSite', 'cacheSite'
]
unsupportedFields.forEach(field => {
if (params[field as keyof SearchParams]) {
(degradedParams as any)[field] = undefined
warnings.push(`Naver不支持${field}语法`)
}
})
if (warnings.length > 0) {
console.warn(`[Naver] 语法降级:`, warnings.join('; '))
}
return degradedParams
}
// 文件类型支持: pdf, doc, xls, ppt, hwp (韩国 Hancom 文档格式)
private getSupportedFileTypes(): string[] {
return ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'hwp']
}
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype',
'exact_match', 'exclude'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype'],
precision: ['exact_match'],
logic: ['exclude']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://search.naver.com/search.naver`
- **查询参数**: `?query=`
- **语法处理**: 仅基础语法,不支持 OR 组合
- **特殊文件类型**: 支持韩国 HWP 格式 (Hancom Office)
- **多关键词限制**: 不支持 OR 组合,降级为单个
##### 3.2.5 Sogou Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/sogou.ts`
**实现规格**:
```typescript
/**
* Sogou 搜索引擎适配器
* 中国第二大搜索引擎
*/
export class SogouAdapter implements SearchEngineAdapter {
getName(): string {
return '搜狗'
}
getBaseUrl(): string {
return 'https://www.sogou.com/web'
}
// 查询参数格式: ?query={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?query=${encodeURIComponent(query)}`
}
// 支持的语法 (参考 Baidu,部分支持)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact',
'intitle',
'inurl',
'exclude',
'or'
// 不支持: date_range, intext, allintitle, number_range, wildcard, related, cache
]
}
// 多关键词支持 (部分支持)
// - sites: 支持 OR 组合
// - fileTypes: 支持 OR 组合 (pdf, doc, xls, ppt, txt)
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
const warnings: string[] = []
// Sogou 不支持的语法
const unsupportedFields = [
'dateRange', 'inText', 'allInTitle', 'numberRange',
'wildcardQuery', 'relatedSite', 'cacheSite'
]
unsupportedFields.forEach(field => {
if (params[field as keyof SearchParams]) {
(degradedParams as any)[field] = undefined
warnings.push(`Sogou不支持${field}语法`)
}
})
if (warnings.length > 0) {
console.warn(`[Sogou] 语法降级:`, warnings.join('; '))
}
return degradedParams
}
// 文件类型支持: pdf, doc, docx, xls, xlsx, ppt, pptx, txt
private getSupportedFileTypes(): string[] {
return ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']
}
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype', 'intitle', 'inurl',
'exact_match', 'exclude', 'or_keywords'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype', 'intitle', 'inurl'],
precision: ['exact_match'],
logic: ['exclude', 'or_keywords']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://www.sogou.com/web`
- **查询参数**: `?query=`
- **语法处理**: 参考 BaiduAdapter,支持中等复杂度语法
- **中文优化**: 支持中文分词和拼音搜索 (引擎内部处理)
- **多关键词支持**: 支持 OR 组合
##### 3.2.6 360 Search Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/so360.ts`
**实现规格**:
```typescript
/**
* 360搜索引擎适配器
* 中国市场第三大搜索引擎
*/
export class So360Adapter implements SearchEngineAdapter {
getName(): string {
return '360搜索'
}
getBaseUrl(): string {
return 'https://www.so.com/s'
}
// 查询参数格式: ?q={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?q=${encodeURIComponent(query)}`
}
// 支持的语法 (基础子集)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact',
'intitle',
'exclude'
// 不支持: OR, inurl, intext, date_range 等高级语法
]
}
// 多关键词支持 (有限)
// - sites: 不支持 OR 组合
// - fileTypes: 不支持 OR 组合
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
const warnings: string[] = []
// 360搜索 不支持多站点 OR 组合
if (params.sites && params.sites.length > 1) {
degradedParams.sites = [params.sites[0]]
warnings.push('360搜索不支持多站点OR组合,仅使用第一个站点')
}
// 360搜索 不支持多文件类型 OR 组合
if (params.fileTypes && params.fileTypes.length > 1) {
degradedParams.fileTypes = [params.fileTypes[0]]
warnings.push('360搜索不支持多文件类型OR组合,仅使用第一个类型')
}
// 不支持的高级语法
const unsupportedFields = [
'orKeywords', 'dateRange', 'inUrl', 'inText', 'allInTitle',
'numberRange', 'wildcardQuery', 'relatedSite', 'cacheSite'
]
unsupportedFields.forEach(field => {
if (params[field as keyof SearchParams]) {
(degradedParams as any)[field] = undefined
warnings.push(`360搜索不支持${field}语法`)
}
})
if (warnings.length > 0) {
console.warn(`[360搜索] 语法降级:`, warnings.join('; '))
}
return degradedParams
}
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype', 'intitle',
'exact_match', 'exclude'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype', 'intitle'],
precision: ['exact_match'],
logic: ['exclude']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://www.so.com/s`
- **查询参数**: `?q=`
- **语法处理**: 基础语法支持,类似 Naver
- **多关键词限制**: 不支持 OR 组合,降级为单个
- **中文优化**: 支持中文内容优化 (引擎内部处理)
#### Phase 3: 欧洲市场引擎 (1个)
##### 3.2.7 Qwant Adapter
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/qwant.ts`
**实现规格**:
```typescript
/**
* Qwant 搜索引擎适配器
* 法国隐私友好搜索引擎
*/
export class QwantAdapter implements SearchEngineAdapter {
getName(): string {
return 'Qwant'
}
getBaseUrl(): string {
return 'https://www.qwant.com/'
}
// 查询参数格式: ?q={query}
buildQuery(params: SearchParams): string {
const baseUrl = this.getBaseUrl()
const query = this.buildSearchQuery(params)
return `${baseUrl}?q=${encodeURIComponent(query)}`
}
// 支持的语法 (基础子集)
getSupportedSyntax(): SyntaxType[] {
return [
'site',
'filetype',
'exact'
// Qwant 官方文档显示支持极其有限
// 不支持: exclude, OR 及所有高级语法
]
}
// 多关键词支持 (不支持)
// - sites: 不支持 OR 组合
// - fileTypes: 不支持 OR 组合
// - exactMatches: 原生并列支持
// 语法降级
degradeSyntax(params: SearchParams): SearchParams {
const degradedParams = { ...params }
const warnings: string[] = []
// Qwant 不支持多站点 OR 组合
if (params.sites && params.sites.length > 1) {
degradedParams.sites = [params.sites[0]]
warnings.push('Qwant不支持多站点OR组合,仅使用第一个站点')
}
// Qwant 不支持多文件类型 OR 组合
if (params.fileTypes && params.fileTypes.length > 1) {
degradedParams.fileTypes = [params.fileTypes[0]]
warnings.push('Qwant不支持多文件类型OR组合,仅使用第一个类型')
}
// 不支持的所有高级语法
const unsupportedFields = [
'excludeWords', 'orKeywords', 'dateRange', 'inTitle', 'inUrl',
'inText', 'allInTitle', 'numberRange', 'wildcardQuery',
'relatedSite', 'cacheSite'
]
unsupportedFields.forEach(field => {
if (params[field as keyof SearchParams]) {
(degradedParams as any)[field] = undefined
warnings.push(`Qwant不支持${field}语法`)
}
})
if (warnings.length > 0) {
console.warn(`[Qwant] 语法降级:`, warnings.join('; '))
}
return degradedParams
}
getSupportedFeatures(): UIFeatureType[] {
return [
'site', 'filetype',
'exact_match'
]
}
getFeatureGroups(): EngineFeatureGroups {
return {
location: ['site', 'filetype'],
precision: ['exact_match']
}
}
}
```
**关键实现细节**:
- **baseUrl**: `https://www.qwant.com/`
- **查询参数**: `?q=`
- **语法处理**: 最小语法支持 (仅 site, filetype, exact)
- **多关键词限制**: 不支持 OR 组合
- **Qwick 快捷方式**: 可选实现 (如 !w Wikipedia),暂不实现
### 3.3 Type Definitions
**文件路径**: `/Users/lhly/chromeex/ssp/src/types/index.ts`
**修改内容**:
```typescript
// 第 2 行,修改 SearchEngine 类型定义
export type SearchEngine =
| 'baidu'
| 'google'
| 'bing'
| 'twitter'
| 'duckduckgo'
| 'brave'
| 'yandex'
| 'reddit'
| 'github'
| 'stackoverflow'
// 🔥 新增 7 个搜索引擎
| 'yahoo'
| 'startpage'
| 'ecosia'
| 'qwant'
| 'naver'
| 'sogou'
| 'so360';
```
### 3.4 Factory Registration
**文件路径**: `/Users/lhly/chromeex/ssp/src/services/adapters/factory.ts`
**修改内容**:
1. **Import 语句** (第 1-11 行后新增):
```typescript
import { YahooAdapter } from './yahoo'
import { StartpageAdapter } from './startpage'
import { EcosiaAdapter } from './ecosia'
import { QwantAdapter } from './qwant'
import { NaverAdapter } from './naver'
import { SogouAdapter } from './sogou'
import { So360Adapter } from './so360'
```
2. **createAdapter 方法** (第 39-63 行,switch case 新增):
```typescript
private static createAdapter(engine: SearchEngine): SearchEngineAdapter {
switch (engine) {
// 现有引擎 ...
case 'stackoverflow':
return new StackOverflowAdapter()
// 🔥 新增 7 个引擎适配器
case 'yahoo':
return new YahooAdapter()
case 'startpage':
return new StartpageAdapter()
case 'ecosia':
return new EcosiaAdapter()
case 'qwant':
return new QwantAdapter()
case 'naver':
return new NaverAdapter()
case 'sogou':
return new SogouAdapter()
case 'so360':
return new So360Adapter()
default:
throw new Error(`不支持的搜索引擎: ${engine}`)
}
}
```
3. **getSupportedEngines 方法** (第 69-71 行,数组新增):
```typescript
static getSupportedEngines(): SearchEngine[] {
return [
'baidu', 'google', 'bing', 'twitter', 'duckduckgo',
'brave', 'yandex', 'reddit', 'github', 'stackoverflow',
// 🔥 新增 7 个引擎
'yahoo', 'startpage', 'ecosia', 'qwant',
'naver', 'sogou', 'so360'
]
}
```
### 3.5 Internationalization (i18n)
**文件路径**: `/Users/lhly/chromeex/ssp/src/i18n/translations.ts`
**修改内容** (在第 16 行后新增):
#### 3.5.1 引擎名称翻译
```typescript
const translations: Record<Language, Record<string, string>> = {
'zh-CN': {
// 现有引擎 ...
'common.searchEngines.stackoverflow': 'Stack Overflow',
// 🔥 新增 7 个引擎名称 (中文)
'common.searchEngines.yahoo': 'Yahoo搜索',
'common.searchEngines.startpage': 'Startpage',
'common.searchEngines.ecosia': 'Ecosia',
'common.searchEngines.qwant': 'Qwant',
'common.searchEngines.naver': 'Naver',
'common.searchEngines.sogou': '搜狗',
'common.searchEngines.so360': '360搜索',
// 其余翻译 ...
},
'en-US': {
// 现有引擎 ...
'common.searchEngines.stackoverflow': 'Stack Overflow',
// 🔥 新增 7 个引擎名称 (英文)
'common.searchEngines.yahoo': 'Yahoo Search',
'common.searchEngines.startpage': 'Startpage',
'common.searchEngines.ecosia': 'Ecosia',
'common.searchEngines.qwant': 'Qwant',
'common.searchEngines.naver': 'Naver',
'common.searchEngines.sogou': 'Sogou',
'common.searchEngines.so360': '360 Search',
// 其余翻译 ...
}
}
```
#### 3.5.2 验证消息翻译
**新增翻译键** (在适当位置添加):
```typescript
'zh-CN': {
// ... 现有翻译 ...
// 🔥 新增适配器验证消息
'adapter.validation.unsupportedSyntax': '{engine}不支持{syntax}语法,该参数将被忽略',
'adapter.validation.unsupportedMultiKeyword': '{engine}不支持多关键词OR组合,仅使用第一个',
'adapter.validation.syntaxDegraded': '{engine}不支持部分高级语法,已自动降级',
'adapter.validation.queryLengthExceeded': '查询长度超过{engine}的限制({max}字符)',
// ... 其余翻译 ...
},
'en-US': {
// ... 现有翻译 ...
// 🔥 新增适配器验证消息
'adapter.validation.unsupportedSyntax': '{engine} does not support {syntax} syntax, parameter ignored',
'adapter.validation.unsupportedMultiKeyword': '{engine} does not support multi-keyword OR combination, using first one only',
'adapter.validation.syntaxDegraded': '{engine} does not support some advanced syntax, automatically degraded',
'adapter.validation.queryLengthExceeded': 'Query length exceeds {engine} limit ({max} characters)',
// ... 其余翻译 ...
}
```
### 3.6 Configuration Changes
**无需配置文件修改** - 所有配置通过代码硬编码
### 3.7 Query Length Limits
**统一查询长度限制** (在各适配器的 validateParams 方法中):
```typescript
// 所有引擎统一使用 180 字符限制 (最保守策略)
const MAX_QUERY_LENGTH = 180
async validateParams(params: SearchParams): Promise<ValidationResult> {
const warnings: string[] = []
const language = await getCurrentLanguage()
const t = (key: string, vars?) => translate(language, key, vars)
const fullQuery = this.buildSearchQuery(params)
if (fullQuery.length > MAX_QUERY_LENGTH) {
warnings.push(
t('adapter.validation.queryLengthExceeded', {
engine: this.getName(),
max: MAX_QUERY_LENGTH.toString()
})
)
}
return { isValid: true, warnings }
}
```
---
## 4. Implementation Sequence
### Phase 1: 兼容性引擎 (预计 2-3 小时)
#### Step 1.1: Yahoo Adapter 实现
- [ ] 创建 `/src/services/adapters/yahoo.ts`
- [ ] 复用 BingAdapter 核心逻辑
- [ ] 修改 baseUrl 为 `https://search.yahoo.com/search`
- [ ] 修改查询参数为 `?p=`
- [ ] 实现 degradeSyntax (处理 cache)
- [ ] 创建 `/src/services/adapters/yahoo.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 1.2: Startpage Adapter 实现
- [ ] 创建 `/src/services/adapters/startpage.ts`
- [ ] 复用 GoogleAdapter 核心逻辑
- [ ] 修改 baseUrl 为 `https://www.startpage.com/sp/search`
- [ ] 修改查询参数为 `?query=`
- [ ] 实现 degradeSyntax (处理多个不支持语法)
- [ ] 创建 `/src/services/adapters/startpage.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 1.3: Ecosia Adapter 实现
- [ ] 创建 `/src/services/adapters/ecosia.ts`
- [ ] 实现基础语法支持 (site, filetype, exact, exclude, OR)
- [ ] 修改 baseUrl 为 `https://www.ecosia.org/search`
- [ ] 实现 degradeSyntax (处理所有高级语法)
- [ ] 创建 `/src/services/adapters/ecosia.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 1.4: Phase 1 集成
- [ ] 更新 `src/types/index.ts` (添加 yahoo, startpage, ecosia)
- [ ] 更新 `src/services/adapters/factory.ts` (注册 3 个适配器)
- [ ] 更新 `src/i18n/translations.ts` (添加引擎名称翻译)
- [ ] 手动测试 3 个引擎的搜索功能
- [ ] 验证语法降级警告正确显示
### Phase 2: 区域市场引擎 (预计 3-4 小时)
#### Step 2.1: Naver Adapter 实现
- [ ] 创建 `/src/services/adapters/naver.ts`
- [ ] 实现基础语法 (site, filetype, exact, exclude)
- [ ] 修改 baseUrl 为 `https://search.naver.com/search.naver`
- [ ] 实现 degradeSyntax (禁用 OR 组合,处理高级语法)
- [ ] 添加 HWP 文件类型支持
- [ ] 创建 `/src/services/adapters/naver.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 2.2: Sogou Adapter 实现
- [ ] 创建 `/src/services/adapters/sogou.ts`
- [ ] 参考 BaiduAdapter,实现中等复杂度语法
- [ ] 修改 baseUrl 为 `https://www.sogou.com/web`
- [ ] 修改查询参数为 `?query=`
- [ ] 实现 degradeSyntax (处理不支持的高级语法)
- [ ] 创建 `/src/services/adapters/sogou.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 2.3: 360 Search Adapter 实现
- [ ] 创建 `/src/services/adapters/so360.ts`
- [ ] 实现基础语法 (site, filetype, exact, intitle, exclude)
- [ ] 修改 baseUrl 为 `https://www.so.com/s`
- [ ] 实现 degradeSyntax (禁用 OR 组合,处理高级语法)
- [ ] 创建 `/src/services/adapters/so360.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 2.4: Phase 2 集成
- [ ] 更新 `src/types/index.ts` (添加 naver, sogou, so360)
- [ ] 更新 `src/services/adapters/factory.ts` (注册 3 个适配器)
- [ ] 更新 `src/i18n/translations.ts` (添加引擎名称翻译)
- [ ] 手动测试 3 个引擎的搜索功能
- [ ] 验证中文内容搜索正常工作
### Phase 3: 欧洲市场引擎 (预计 1-2 小时)
#### Step 3.1: Qwant Adapter 实现
- [ ] 创建 `/src/services/adapters/qwant.ts`
- [ ] 实现最小语法支持 (site, filetype, exact)
- [ ] 修改 baseUrl 为 `https://www.qwant.com/`
- [ ] 实现 degradeSyntax (处理所有高级语法)
- [ ] 创建 `/src/services/adapters/qwant.test.ts`
- [ ] 编写核心逻辑单元测试
#### Step 3.2: Phase 3 集成
- [ ] 更新 `src/types/index.ts` (添加 qwant)
- [ ] 更新 `src/services/adapters/factory.ts` (注册 qwant)
- [ ] 更新 `src/i18n/translations.ts` (添加引擎名称翻译)
- [ ] 手动测试 Qwant 搜索功能
### Final Integration (预计 1 小时)
#### Step 4.1: 完整性验证
- [ ] 运行所有单元测试 (`npm run test`)
- [ ] 运行类型检查 (`npm run type-check`)
- [ ] 运行 ESLint (`npm run lint`)
- [ ] 手动测试所有 17 个引擎 (10 + 7)
#### Step 4.2: 国际化验证
- [ ] 切换到中文,验证所有引擎名称正确显示
- [ ] 切换到英文,验证所有引擎名称正确显示
- [ ] 验证语法降级警告消息的中英文显示
#### Step 4.3: 功能验证
- [ ] 测试多关键词 OR 组合 (在支持的引擎中)
- [ ] 测试语法降级 (在不支持的引擎中)
- [ ] 测试查询长度警告
- [ ] 测试引擎切换功能
#### Step 4.4: 文档更新
- [ ] 更新 `README.md` (如果需要)
- [ ] 更新版本号至 1.9.0
- [ ] 生成 CHANGELOG
---
## 5. Test Specification
### 5.1 Unit Test Structure
**测试文件命名**: `{adapter-name}.test.ts`
**测试框架**: Jest + @testing-library
### 5.2 Core Test Cases
#### 5.2.1 Yahoo Adapter Tests
**文件**: `/src/services/adapters/yahoo.test.ts`
```typescript
import { YahooAdapter } from './yahoo'
import type { SearchParams } from '@/types'
describe('YahooAdapter', () => {
let adapter: YahooAdapter
beforeEach(() => {
adapter = new YahooAdapter()
})
describe('基础功能', () => {
test('应返回正确的引擎名称', () => {
expect(adapter.getName()).toBe('雅虎')
})
test('应返回正确的基础URL', () => {
expect(adapter.getBaseUrl()).toBe('https://search.yahoo.com/search')
})
})
describe('URL构建', () => {
test('应构建基础搜索URL', () => {
const params: SearchParams = {
keyword: 'test query',
engine: 'yahoo'
}
const url = adapter.buildQuery(params)
expect(url).toContain('search.yahoo.com/search')
expect(url).toContain('?p=')
expect(url).toContain('test%20query')
})
test('应支持site语法', () => {
const params: SearchParams = {
keyword: 'test',
engine: 'yahoo',
site: 'example.com'
}
const url = adapter.buildQuery(params)
expect(url).toContain('site%3Aexample.com')
})
test('应支持多站点OR组合', () => {
const params: SearchParams = {
keyword: 'test',
engine: 'yahoo',
sites: ['example.com', 'test.com']
}
const url = adapter.buildQuery(params)
expect(url).toContain('site%3Aexample.com')
expect(url).toContain('OR')
expect(url).toContain('site%3Atest.com')
})
test('应支持filetype语法', () => {
const params: SearchParams = {
keyword: 'test',
engine: 'yahoo',
fileType: 'pdf'
}
const url = adapter.buildQuery(params)
expect(url).toContain('filetype%3Apdf')
})
test('应支持多文件类型OR组合', () => {
const params: SearchParams = {
keyword: 'test',
engine: 'yahoo',
fileTypes: ['pdf', 'doc']
}
const url = adapter.buildQuery(params)
expect(url).toContain('filetype%3Apdf')
expect(url).toContain('OR')
expect(url).toContain('filetype%3Adoc')
})
})
describe('语法支持', () => {
test('应支持Bing兼容语法', () => {
const supportedSyntax = adapter.getSupportedSyntax()
expect(supportedSyntax).toContain('site')
expect(supportedSyntax).toContain('filetype')
expect(supportedSyntax).toContain('intitle')
expect(supportedSyntax).toContain('inurl')
expect(supportedSyntax).toContain('related')
})
test('不应支持cache语法', () => {
const supportedSyntax = adapter.getSupportedSyntax()
expect(supportedSyntax).not.toContain('cache')
})
})
describe('语法降级', () => {
test('应降级cache语法', () => {
const params: SearchParams = {
keyword: 'test',
engine: 'yahoo',
cacheSite: 'example.com'
}
const degraded = adapter.degradeSyntax(params)
expect(degraded.cacheSite).toBeUndefined()
})
test('应保留支持的语法', () => {
const params: SearchParams = {
keyword: 'test',
engine: 'yahoo',
site: 'example.com',
fileType: 'pdf'
}
const degraded = adapter.degradeSyntax(params)
expect(degraded.site).toBe('example.com')
expect(degraded.fileType).toBe('pdf')
})
})
})
```
#### 5.2.2 Startpage Adapter Tests
**文件**: `/src/services/adapters/startpage.test.ts`
```typescript
import { StartpageAdapter } from './startpage'
describe('StartpageAdapter', () => {
let adapter: StartpageAdapter
beforeEach(() => {
adapter = new StartpageAdapter()
})
test('应使用query参数而不是q', () => {
const params = {
keyword: 'test',
engine: 'startpage' as const
}
const url = adapter.buildQuery(params)
expect(url).toContain('?query=')
expect(url).not.toContain('?q=')
})
test('应降级不支持的inurl语法', () => {
const params = {
keyword: 'test',
engine: 'startpage' as const,
inUrl: 'blog'
}
const degraded = adapter.degradeSyntax(params)
expect(degraded.inUrl).toBeUndefined()
})
test('应支持date_range语法', () => {
const supportedSyntax = adapter.getSupportedSyntax()
expect(supportedSyntax).toContain('date_range')
expect(supportedSyntax).not.toContain('inurl')
})
})
```
#### 5.2.3 Naver Adapter Tests
**文件**: `/src/services/adapters/naver.test.ts`
```typescript
import { NaverAdapter } from './naver'
describe('NaverAdapter', () => {
let adapter: NaverAdapter
beforeEach(() => {
adapter = new NaverAdapter()
})
test('应降级多站点OR组合为单个站点', () => {
const params = {
keyword: 'test',
engine: 'naver' as const,
sites: ['site1.com', 'site2.com', 'site3.com']
}
const degraded = adapter.degradeSyntax(params)
expect(degraded.sites).toHaveLength(1)
expect(degraded.sites![0]).toBe('site1.com')
})
test('应降级多文件类型OR组合为单个类型', () => {
const params = {
keyword: 'test',
engine: 'naver' as const,
fileTypes: ['pdf', 'doc', 'xls']
}
const degraded = adapter.degradeSyntax(params)
expect(degraded.fileTypes).toHaveLength(1)
expect(degraded.fileTypes![0]).toBe('pdf')
})
test('应降级OR关键词语法', () => {
const params = {
keyword: 'test',
engine: 'naver' as const,
orKeywords: ['keyword1', 'keyword2']
}
const degraded = adapter.degradeSyntax(params)
expect(degraded.orKeywords).toBeUndefined()
})
})
```
### 5.3 Test Coverage Requirements
**必须测试的核心逻辑** (每个适配器):
1. ✅ URL 构建正确性 (baseUrl + 查询参数格式)
2. ✅ 单个语法支持 (site, filetype, exact)
3. ✅ 多关键词 OR 组合 (如果引擎支持)
4. ✅ 语法降级处理 (不支持的语法应设为 undefined)
5. ✅ getSupportedSyntax 返回正确列表
**不需要测试**:
- ❌ UI 集成测试
- ❌ E2E 测试
- ❌ 性能测试
- ❌ 国际化翻译内容测试 (手动验证即可)
---
## 6. Validation Plan
### 6.1 Unit Tests
**运行命令**: `npm run test`
**测试内容**:
- 所有适配器的 URL 构建逻辑
- 语法支持列表正确性
- 多关键词 OR 组合正确性
- 语法降级处理正确性
### 6.2 Integration Tests
**手动测试流程**:
1. **引擎切换测试**
- 打开扩展弹窗
- 依次切换到 17 个搜索引擎
- 验证引擎名称正确显示 (中英文)
2. **搜索功能测试**
- 每个引擎执行基础关键词搜索
- 验证跳转到正确的搜索引擎页面
- 验证 URL 参数格式正确
3. **语法支持测试**
- 在支持 OR 的引擎中测试多站点/多文件类型
- 在不支持 OR 的引擎中验证降级警告
- 测试高级语法在各引擎中的行为
4. **国际化测试**
- 切换语言到中文,验证所有引擎名称
- 切换语言到英文,验证所有引擎名称
- 验证警告消息的中英文显示
### 6.3 Business Logic Verification
**验收测试场景**:
| 场景 | 引擎 | 操作 | 预期结果 |
|------|------|------|---------|
| 多站点搜索 | Yahoo | 输入 2 个站点 | URL 包含 `(site:a.com OR site:b.com)` |
| 多站点降级 | Naver | 输入 2 个站点 | 仅使用第一个站点,显示警告 |
| cache 降级 | Yahoo | 输入 cache 站点 | cache 参数被忽略,显示警告 |
| 日期范围 | Startpage | 输入日期范围 | URL 包含 `before:` 和 `after:` |
| 基础语法 | Qwant | 输入 site + filetype | URL 正确构建 |
| 高级语法降级 | Qwant | 输入 intitle | intitle 被忽略,显示警告 |
| 中文搜索 | Sogou | 输入中文关键词 | 正确处理中文 URL 编码 |
---
## 7. Implementation Constraints
### 7.1 MUST Requirements
1. **Direct Implementability**: 每个适配器必须可直接编码实现,无需额外架构设计
2. **Syntax Consistency**: 语法支持必须与官方搜索引擎一致
3. **Degradation Handling**: 不支持的语法必须正确降级并发出警告
4. **i18n Completeness**: 所有用户可见文本必须有中英文翻译
5. **Test Coverage**: 核心逻辑必须有单元测试覆盖
### 7.2 MUST NOT Requirements
1. **No Over-Engineering**: 不引入复杂设计模式 (直接复用现有适配器逻辑)
2. **No Additional Languages**: 不添加中英文以外的语言支持
3. **No UI Changes**: 不修改现有 UI 组件 (使用现有下拉选择器)
4. **No Backend Services**: 不引入服务器端依赖
5. **No Breaking Changes**: 保持向后兼容,不破坏现有功能
### 7.3 Code Quality Standards
- **TypeScript**: 严格类型检查,无 any 类型
- **ESLint**: 无 linting 错误
- **Code Style**: 与现有适配器保持一致
- **Comments**: 关键逻辑添加中文注释
- **Naming**: 遵循现有命名约定
---
## 8. Risk Mitigation
### 8.1 Identified Risks
| 风险 | 可能性 | 影响 | 缓解措施 |
|------|--------|------|---------|
| 官方 URL 格式变化 | 中 | 高 | 使用 try-catch 包裹,允许降级处理 |
| 语法支持文档不准确 | 高 | 中 | 通过实际测试验证,保守策略 |
| 多关键词支持不确定 | 中 | 中 | 默认禁用,测试验证后启用 |
| 查询长度限制未知 | 低 | 低 | 统一使用 180 字符保守限制 |
| 国际化翻译遗漏 | 低 | 中 | 使用 checklist 逐项验证 |
### 8.2 Fallback Strategies
1. **URL 构建失败**: 返回基础 URL + 关键词,允许用户手动搜索
2. **语法不支持**: 降级处理,发出警告,允许搜索继续
3. **查询过长**: 仅警告,不阻止搜索
4. **验证失败**: 宽松验证策略,仅警告不阻止
---
## 9. Acceptance Criteria
### 9.1 Functional Acceptance
- [ ] 所有 7 个新引擎可正常搜索
- [ ] 多站点 OR 组合在 Yahoo, Startpage, Ecosia, Sogou 中工作
- [ ] 多文件类型 OR 组合在支持的引擎中工作
- [ ] 语法降级在 Naver, 360搜索, Qwant 中正确处理
- [ ] cache 语法在 Yahoo 中正确降级
- [ ] 查询长度超限时显示警告
### 9.2 i18n Acceptance
- [ ] 所有 7 个引擎名称有中文翻译
- [ ] 所有 7 个引擎名称有英文翻译
- [ ] 语法降级警告有中英文翻译
- [ ] 切换语言时所有文本正确更新
- [ ] 不包含中英文以外的语言
### 9.3 Code Quality Acceptance
- [ ] 所有单元测试通过 (`npm run test`)
- [ ] TypeScript 类型检查通过 (`npm run type-check`)
- [ ] ESLint 检查通过 (`npm run lint`)
- [ ] 代码风格与现有适配器一致
- [ ] 关键逻辑有中文注释
### 9.4 User Experience Acceptance
- [ ] 引擎选择器包含所有 17 个引擎
- [ ] 引擎切换流畅无卡顿
- [ ] 搜索结果在正确的引擎中打开
- [ ] 警告消息清晰易懂
- [ ] 不支持的语法不阻止搜索
---
## 10. Post-Implementation
### 10.1 Documentation Updates
- [ ] 更新 `README.md` 支持的引擎列表
- [ ] 生成 `CHANGELOG.md` 版本 1.9.0
- [ ] 更新 `manifest.json` 版本号
### 10.2 Version Release
**新版本号**: 1.9.0
**发布说明**:
```markdown
## v1.9.0 - 2025-11-26
### 新增功能
- ✨ 新增 7 个搜索引擎支持
- Yahoo Search (雅虎搜索)
- Startpage (隐私友好搜索)
- Ecosia (环保搜索引擎)
- Qwant (欧洲隐私搜索)
- Naver (韩国搜索引擎)
- Sogou (搜狗搜索)
- 360搜索
### 改进
- 🌐 完整的中英文国际化支持
- ⚙️ 智能语法降级处理
- ✅ 完整的单元测试覆盖
### 技术细节
- 所有语法支持与官方搜索引擎保持一致
- 多关键词 OR 组合在支持的引擎中正常工作
- 不支持的语法自动降级并发出友好警告
```
### 10.3 Future Enhancements
**可选后续改进** (不在本次实施范围):
1. 添加 Perplexity AI 和 You.com 支持
2. 实现 Qwant Qwick 快捷方式
3. 优化中文搜索引擎的分词处理
4. 添加引擎特定的搜索建议
---
## 11. Reference Materials
### 11.1 Official Documentation
- **Yahoo**: Bing 语法兼容性参考
- **Startpage**: [官方 Help Center](https://support.startpage.com/)
- **Ecosia**: [官方 FAQ](https://ecosia.zendesk.com/)
- **Qwant**: [官方帮助页面](https://help.qwant.com/)
- **Naver**: [Naver Search API 文档](https://developers.naver.com/)
- **Sogou**: [搜狗帮助中心](https://help.sogou.com/)
- **360搜索**: [360 帮助页面](https://help.so.com/)
### 11.2 Existing Code References
- **Google Adapter**: `/src/services/adapters/google.ts` (最完整语法支持)
- **Bing Adapter**: `/src/services/adapters/bing.ts` (Yahoo 参考)
- **Baidu Adapter**: `/src/services/adapters/baidu.ts` (中文引擎参考)
- **Factory Pattern**: `/src/services/adapters/factory.ts`
- **Type Definitions**: `/src/types/index.ts`
- **i18n System**: `/src/i18n/translations.ts`
### 11.3 Test Examples
- **Twitter Tests**: `/src/services/adapters/twitter.test.ts` (现有测试参考)
---
## Appendix A: Complete Syntax Support Matrix
| 搜索引擎 | site | filetype | exact | intitle | inurl | intext | OR | exclude | date_range | cache | related |
|---------|------|----------|-------|---------|-------|--------|----|---------|-----------|----- --|---------|
| Yahoo | ✅ | ✅ | ✅ | ✅ | ✅ | ✅* | ✅ | ✅ | ✅ | ❌ | ✅ |
| Startpage | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
| Ecosia | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| Qwant | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Naver | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
| Sogou | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ |
| 360搜索 | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
**注释**:
- `*` Yahoo 使用 `inbody:` 而不是 `intext:`
- ✅ = 完整支持
- ❌ = 不支持,需要降级处理
---
## Appendix B: Multi-Keyword OR Support Matrix
| 搜索引擎 | sites OR | fileTypes OR | exactMatches 并列 |
|---------|----------|--------------|------------------|
| Yahoo | ✅ | ✅ | ✅ |
| Startpage | ✅ | ✅ | ✅ |
| Ecosia | ✅ | ✅ | ✅ |
| Qwant | ❌ | ❌ | ✅ |
| Naver | ❌ | ❌ | ✅ |
| Sogou | ✅ | ✅ | ✅ |
| 360搜索 | ❌ | ❌ | ✅ |
---
**Document Status**: ✅ Ready for Implementation
**Estimated Implementation Time**: 7-10 hours
**Last Updated**: 2025-11-26