feat(websearch): enhance web search provider settings and localization

- Updated web search provider settings to include API key and free status indicators.
- Improved localization for English, Japanese, Russian, Chinese, and Taiwanese languages to reflect new API key and free status fields.
- Refactored web search provider management to prevent duplicates and streamline provider addition during state migration.
- Adjusted UI components to conditionally render based on provider type, enhancing user experience.
This commit is contained in:
kangfenmao 2025-04-10 13:07:55 +08:00
parent f9c6bddae5
commit efcffbaa30
12 changed files with 807 additions and 764 deletions

View File

@ -25,6 +25,7 @@ export const useDefaultWebSearchProvider = () => {
export const useWebSearchProviders = () => {
const providers = useAppSelector((state) => state.websearch.providers)
const dispatch = useAppDispatch()
return {
@ -45,6 +46,7 @@ export const useWebSearchProvider = (id: string) => {
const providers = useAppSelector((state) => state.websearch.providers)
const provider = providers.find((provider) => provider.id === id)
const dispatch = useAppDispatch()
if (!provider) {
throw new Error(`Web search provider with id ${id} not found`)
}

View File

@ -1301,7 +1301,9 @@
},
"title": "Web Search",
"overwrite": "Override search service",
"overwrite_tooltip": "Force use search service instead of LLM"
"overwrite_tooltip": "Force use search service instead of LLM",
"apikey": "API key",
"free": "Free"
},
"quickPhrase": {
"title": "Quick Phrases",

View File

@ -1300,7 +1300,9 @@
},
"title": "ウェブ検索",
"overwrite": "サービス検索を上書き",
"overwrite_tooltip": "大規模言語モデルではなく、サービス検索を使用する"
"overwrite_tooltip": "大規模言語モデルではなく、サービス検索を使用する",
"apikey": "API キー",
"free": "無料"
},
"general.auto_check_update.title": "自動更新チェックを有効にする",
"quickPhrase": {

View File

@ -1300,7 +1300,9 @@
},
"title": "Поиск в Интернете",
"overwrite": "Переопределить поставщика поиска",
"overwrite_tooltip": "Использовать поставщика поиска вместо LLM"
"overwrite_tooltip": "Использовать поставщика поиска вместо LLM",
"apikey": "Ключ API",
"free": "Бесплатно"
},
"general.auto_check_update.title": "Включить автоматическую проверку обновлений",
"quickPhrase": {

View File

@ -1301,7 +1301,9 @@
"description": "Tavily 是一个为 AI 代理量身定制的搜索引擎,提供实时、准确的结果、智能查询建议和深入的研究能力",
"title": "Tavily"
},
"title": "网络搜索"
"title": "网络搜索",
"apikey": "API 密钥",
"free": "免费"
},
"quickPhrase": {
"title": "快捷短语",

View File

@ -1300,7 +1300,9 @@
},
"title": "網路搜尋",
"overwrite": "覆蓋搜尋服務商",
"overwrite_tooltip": "強制使用搜尋服務商而不是大語言模型進行搜尋"
"overwrite_tooltip": "強制使用搜尋服務商而不是大語言模型進行搜尋",
"apikey": "API 金鑰",
"free": "免費"
},
"general.auto_check_update.title": "啟用自動更新檢查",
"quickPhrase": {

View File

@ -117,7 +117,6 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
<SettingTitle>
<Flex align="center" gap={8}>
<ProviderLogo shape="square" src={getWebSearchProviderLogo(provider.id)} size={16} />
<ProviderName> {provider.name}</ProviderName>
{officialWebsite && webSearchProviderConfig?.websites && (
<Link target="_blank" href={webSearchProviderConfig.websites.official}>
@ -156,7 +155,6 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
</SettingHelpTextRow>
</>
)}
{hasObjectKey(provider, 'apiHost') && (
<>
<SettingSubtitle style={{ marginTop: 5, marginBottom: 10 }}>

View File

@ -1,10 +1,9 @@
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultWebSearchProvider, useWebSearchProviders } from '@renderer/hooks/useWebSearchProviders'
import { defaultWebSearchProviders } from '@renderer/store/websearch'
import { WebSearchProvider } from '@renderer/types'
import { hasObjectKey } from '@renderer/utils'
import { Select } from 'antd'
import { FC, useEffect, useState } from 'react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
@ -13,15 +12,13 @@ import BlacklistSettings from './BlacklistSettings'
import WebSearchProviderSetting from './WebSearchProviderSetting'
const WebSearchSettings: FC = () => {
const { providers, addWebSearchProvider } = useWebSearchProviders()
const { providers } = useWebSearchProviders()
const { provider: defaultProvider, setDefaultProvider } = useDefaultWebSearchProvider()
const { t } = useTranslation()
const [selectedProvider, setSelectedProvider] = useState<WebSearchProvider | undefined>(defaultProvider)
const { theme: themeMode } = useTheme()
useEffect(() => {
defaultWebSearchProviders.map((p) => addWebSearchProvider(p))
})
const isLocalProvider = selectedProvider?.id.startsWith('local')
function updateSelectedWebSearchProvider(providerId: string) {
const provider = providers.find((p) => p.id === providerId)
@ -45,19 +42,19 @@ const WebSearchSettings: FC = () => {
style={{ width: '200px' }}
onChange={(value: string) => updateSelectedWebSearchProvider(value)}
placeholder={t('settings.websearch.search_provider_placeholder')}
options={providers
.toSorted((p1, p2) => p1.name.localeCompare(p2.name))
.map((p) => ({
options={providers.map((p) => ({
value: p.id,
label: `${p.name} (${hasObjectKey(p, 'apiKey') ? 'ApiKey' : 'Free'})`
label: `${p.name} (${hasObjectKey(p, 'apiKey') ? t('settings.websearch.apikey') : t('settings.websearch.free')})`
}))}
/>
</div>
</SettingRow>
</SettingGroup>
{!isLocalProvider && (
<SettingGroup theme={themeMode}>
{selectedProvider && <WebSearchProviderSetting provider={selectedProvider} />}
</SettingGroup>
)}
<BasicSettings />
<BlacklistSettings />
</SettingContainer>

View File

@ -83,7 +83,7 @@ export default class LocalSearchProvider extends BaseWebSearchProvider {
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected parseValidUrls(htmlContent: string): SearchItem[] {
protected parseValidUrls(_htmlContent: string): SearchItem[] {
throw new Error('Not implemented')
}

View File

@ -42,7 +42,7 @@ const persistedReducer = persistReducer(
{
key: 'cherry-studio',
storage,
version: 94,
version: 95,
blacklist: ['runtime', 'messages'],
migrate
},

View File

@ -14,6 +14,7 @@ import { RootState } from '.'
import { INITIAL_PROVIDERS, moveProvider } from './llm'
import { mcpSlice } from './mcp'
import { DEFAULT_SIDEBAR_ICONS, initialState as settingsInitialState } from './settings'
import { defaultWebSearchProviders } from './websearch'
// remove logo base64 data to reduce the size of the state
function removeMiniAppIconsFromState(state: RootState) {
@ -52,6 +53,17 @@ function addProvider(state: RootState, id: string) {
}
}
function addWebSearchProvider(state: RootState, id: string) {
if (state.websearch && state.websearch.providers) {
if (!state.websearch.providers.find((p) => p.id === id)) {
const provider = defaultWebSearchProviders.find((p) => p.id === id)
if (provider) {
state.websearch.providers.push(provider)
}
}
}
}
const migrateConfig = {
'2': (state: RootState) => {
try {
@ -985,21 +997,9 @@ const migrateConfig = {
},
'77': (state: RootState) => {
try {
addWebSearchProvider(state, 'searxng')
addWebSearchProvider(state, 'exa')
if (state.websearch) {
if (!state.websearch.providers.find((p) => p.id === 'searxng')) {
state.websearch.providers.push(
{
id: 'searxng',
name: 'Searxng',
apiHost: ''
},
{
id: 'exa',
name: 'Exa',
apiKey: ''
}
)
}
state.websearch.providers.forEach((p) => {
// @ts-ignore eslint-disable-next-line
delete p.enabled
@ -1192,6 +1192,16 @@ const migrateConfig = {
} catch (error) {
return state
}
},
'95': (state: RootState) => {
try {
addWebSearchProvider(state, 'local-google')
addWebSearchProvider(state, 'local-bing')
addWebSearchProvider(state, 'local-baidu')
return state
} catch (error) {
return state
}
}
}

1478
yarn.lock

File diff suppressed because it is too large Load Diff