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:
parent
f9c6bddae5
commit
efcffbaa30
@ -25,6 +25,7 @@ export const useDefaultWebSearchProvider = () => {
|
|||||||
|
|
||||||
export const useWebSearchProviders = () => {
|
export const useWebSearchProviders = () => {
|
||||||
const providers = useAppSelector((state) => state.websearch.providers)
|
const providers = useAppSelector((state) => state.websearch.providers)
|
||||||
|
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -45,6 +46,7 @@ export const useWebSearchProvider = (id: string) => {
|
|||||||
const providers = useAppSelector((state) => state.websearch.providers)
|
const providers = useAppSelector((state) => state.websearch.providers)
|
||||||
const provider = providers.find((provider) => provider.id === id)
|
const provider = providers.find((provider) => provider.id === id)
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
throw new Error(`Web search provider with id ${id} not found`)
|
throw new Error(`Web search provider with id ${id} not found`)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1301,7 +1301,9 @@
|
|||||||
},
|
},
|
||||||
"title": "Web Search",
|
"title": "Web Search",
|
||||||
"overwrite": "Override search service",
|
"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": {
|
"quickPhrase": {
|
||||||
"title": "Quick Phrases",
|
"title": "Quick Phrases",
|
||||||
|
|||||||
@ -1300,7 +1300,9 @@
|
|||||||
},
|
},
|
||||||
"title": "ウェブ検索",
|
"title": "ウェブ検索",
|
||||||
"overwrite": "サービス検索を上書き",
|
"overwrite": "サービス検索を上書き",
|
||||||
"overwrite_tooltip": "大規模言語モデルではなく、サービス検索を使用する"
|
"overwrite_tooltip": "大規模言語モデルではなく、サービス検索を使用する",
|
||||||
|
"apikey": "API キー",
|
||||||
|
"free": "無料"
|
||||||
},
|
},
|
||||||
"general.auto_check_update.title": "自動更新チェックを有効にする",
|
"general.auto_check_update.title": "自動更新チェックを有効にする",
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
|
|||||||
@ -1300,7 +1300,9 @@
|
|||||||
},
|
},
|
||||||
"title": "Поиск в Интернете",
|
"title": "Поиск в Интернете",
|
||||||
"overwrite": "Переопределить поставщика поиска",
|
"overwrite": "Переопределить поставщика поиска",
|
||||||
"overwrite_tooltip": "Использовать поставщика поиска вместо LLM"
|
"overwrite_tooltip": "Использовать поставщика поиска вместо LLM",
|
||||||
|
"apikey": "Ключ API",
|
||||||
|
"free": "Бесплатно"
|
||||||
},
|
},
|
||||||
"general.auto_check_update.title": "Включить автоматическую проверку обновлений",
|
"general.auto_check_update.title": "Включить автоматическую проверку обновлений",
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
|
|||||||
@ -1301,7 +1301,9 @@
|
|||||||
"description": "Tavily 是一个为 AI 代理量身定制的搜索引擎,提供实时、准确的结果、智能查询建议和深入的研究能力",
|
"description": "Tavily 是一个为 AI 代理量身定制的搜索引擎,提供实时、准确的结果、智能查询建议和深入的研究能力",
|
||||||
"title": "Tavily"
|
"title": "Tavily"
|
||||||
},
|
},
|
||||||
"title": "网络搜索"
|
"title": "网络搜索",
|
||||||
|
"apikey": "API 密钥",
|
||||||
|
"free": "免费"
|
||||||
},
|
},
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
"title": "快捷短语",
|
"title": "快捷短语",
|
||||||
|
|||||||
@ -1300,7 +1300,9 @@
|
|||||||
},
|
},
|
||||||
"title": "網路搜尋",
|
"title": "網路搜尋",
|
||||||
"overwrite": "覆蓋搜尋服務商",
|
"overwrite": "覆蓋搜尋服務商",
|
||||||
"overwrite_tooltip": "強制使用搜尋服務商而不是大語言模型進行搜尋"
|
"overwrite_tooltip": "強制使用搜尋服務商而不是大語言模型進行搜尋",
|
||||||
|
"apikey": "API 金鑰",
|
||||||
|
"free": "免費"
|
||||||
},
|
},
|
||||||
"general.auto_check_update.title": "啟用自動更新檢查",
|
"general.auto_check_update.title": "啟用自動更新檢查",
|
||||||
"quickPhrase": {
|
"quickPhrase": {
|
||||||
|
|||||||
@ -117,7 +117,6 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
<SettingTitle>
|
<SettingTitle>
|
||||||
<Flex align="center" gap={8}>
|
<Flex align="center" gap={8}>
|
||||||
<ProviderLogo shape="square" src={getWebSearchProviderLogo(provider.id)} size={16} />
|
<ProviderLogo shape="square" src={getWebSearchProviderLogo(provider.id)} size={16} />
|
||||||
|
|
||||||
<ProviderName> {provider.name}</ProviderName>
|
<ProviderName> {provider.name}</ProviderName>
|
||||||
{officialWebsite && webSearchProviderConfig?.websites && (
|
{officialWebsite && webSearchProviderConfig?.websites && (
|
||||||
<Link target="_blank" href={webSearchProviderConfig.websites.official}>
|
<Link target="_blank" href={webSearchProviderConfig.websites.official}>
|
||||||
@ -156,7 +155,6 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
</SettingHelpTextRow>
|
</SettingHelpTextRow>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasObjectKey(provider, 'apiHost') && (
|
{hasObjectKey(provider, 'apiHost') && (
|
||||||
<>
|
<>
|
||||||
<SettingSubtitle style={{ marginTop: 5, marginBottom: 10 }}>
|
<SettingSubtitle style={{ marginTop: 5, marginBottom: 10 }}>
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { useDefaultWebSearchProvider, useWebSearchProviders } from '@renderer/hooks/useWebSearchProviders'
|
import { useDefaultWebSearchProvider, useWebSearchProviders } from '@renderer/hooks/useWebSearchProviders'
|
||||||
import { defaultWebSearchProviders } from '@renderer/store/websearch'
|
|
||||||
import { WebSearchProvider } from '@renderer/types'
|
import { WebSearchProvider } from '@renderer/types'
|
||||||
import { hasObjectKey } from '@renderer/utils'
|
import { hasObjectKey } from '@renderer/utils'
|
||||||
import { Select } from 'antd'
|
import { Select } from 'antd'
|
||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
@ -13,15 +12,13 @@ import BlacklistSettings from './BlacklistSettings'
|
|||||||
import WebSearchProviderSetting from './WebSearchProviderSetting'
|
import WebSearchProviderSetting from './WebSearchProviderSetting'
|
||||||
|
|
||||||
const WebSearchSettings: FC = () => {
|
const WebSearchSettings: FC = () => {
|
||||||
const { providers, addWebSearchProvider } = useWebSearchProviders()
|
const { providers } = useWebSearchProviders()
|
||||||
const { provider: defaultProvider, setDefaultProvider } = useDefaultWebSearchProvider()
|
const { provider: defaultProvider, setDefaultProvider } = useDefaultWebSearchProvider()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [selectedProvider, setSelectedProvider] = useState<WebSearchProvider | undefined>(defaultProvider)
|
const [selectedProvider, setSelectedProvider] = useState<WebSearchProvider | undefined>(defaultProvider)
|
||||||
const { theme: themeMode } = useTheme()
|
const { theme: themeMode } = useTheme()
|
||||||
|
|
||||||
useEffect(() => {
|
const isLocalProvider = selectedProvider?.id.startsWith('local')
|
||||||
defaultWebSearchProviders.map((p) => addWebSearchProvider(p))
|
|
||||||
})
|
|
||||||
|
|
||||||
function updateSelectedWebSearchProvider(providerId: string) {
|
function updateSelectedWebSearchProvider(providerId: string) {
|
||||||
const provider = providers.find((p) => p.id === providerId)
|
const provider = providers.find((p) => p.id === providerId)
|
||||||
@ -45,19 +42,19 @@ const WebSearchSettings: FC = () => {
|
|||||||
style={{ width: '200px' }}
|
style={{ width: '200px' }}
|
||||||
onChange={(value: string) => updateSelectedWebSearchProvider(value)}
|
onChange={(value: string) => updateSelectedWebSearchProvider(value)}
|
||||||
placeholder={t('settings.websearch.search_provider_placeholder')}
|
placeholder={t('settings.websearch.search_provider_placeholder')}
|
||||||
options={providers
|
options={providers.map((p) => ({
|
||||||
.toSorted((p1, p2) => p1.name.localeCompare(p2.name))
|
value: p.id,
|
||||||
.map((p) => ({
|
label: `${p.name} (${hasObjectKey(p, 'apiKey') ? t('settings.websearch.apikey') : t('settings.websearch.free')})`
|
||||||
value: p.id,
|
}))}
|
||||||
label: `${p.name} (${hasObjectKey(p, 'apiKey') ? 'ApiKey' : 'Free'})`
|
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
<SettingGroup theme={themeMode}>
|
{!isLocalProvider && (
|
||||||
{selectedProvider && <WebSearchProviderSetting provider={selectedProvider} />}
|
<SettingGroup theme={themeMode}>
|
||||||
</SettingGroup>
|
{selectedProvider && <WebSearchProviderSetting provider={selectedProvider} />}
|
||||||
|
</SettingGroup>
|
||||||
|
)}
|
||||||
<BasicSettings />
|
<BasicSettings />
|
||||||
<BlacklistSettings />
|
<BlacklistSettings />
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
|
|||||||
@ -83,7 +83,7 @@ export default class LocalSearchProvider extends BaseWebSearchProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
protected parseValidUrls(htmlContent: string): SearchItem[] {
|
protected parseValidUrls(_htmlContent: string): SearchItem[] {
|
||||||
throw new Error('Not implemented')
|
throw new Error('Not implemented')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ const persistedReducer = persistReducer(
|
|||||||
{
|
{
|
||||||
key: 'cherry-studio',
|
key: 'cherry-studio',
|
||||||
storage,
|
storage,
|
||||||
version: 94,
|
version: 95,
|
||||||
blacklist: ['runtime', 'messages'],
|
blacklist: ['runtime', 'messages'],
|
||||||
migrate
|
migrate
|
||||||
},
|
},
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { RootState } from '.'
|
|||||||
import { INITIAL_PROVIDERS, moveProvider } from './llm'
|
import { INITIAL_PROVIDERS, moveProvider } from './llm'
|
||||||
import { mcpSlice } from './mcp'
|
import { mcpSlice } from './mcp'
|
||||||
import { DEFAULT_SIDEBAR_ICONS, initialState as settingsInitialState } from './settings'
|
import { DEFAULT_SIDEBAR_ICONS, initialState as settingsInitialState } from './settings'
|
||||||
|
import { defaultWebSearchProviders } from './websearch'
|
||||||
|
|
||||||
// remove logo base64 data to reduce the size of the state
|
// remove logo base64 data to reduce the size of the state
|
||||||
function removeMiniAppIconsFromState(state: RootState) {
|
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 = {
|
const migrateConfig = {
|
||||||
'2': (state: RootState) => {
|
'2': (state: RootState) => {
|
||||||
try {
|
try {
|
||||||
@ -985,21 +997,9 @@ const migrateConfig = {
|
|||||||
},
|
},
|
||||||
'77': (state: RootState) => {
|
'77': (state: RootState) => {
|
||||||
try {
|
try {
|
||||||
|
addWebSearchProvider(state, 'searxng')
|
||||||
|
addWebSearchProvider(state, 'exa')
|
||||||
if (state.websearch) {
|
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) => {
|
state.websearch.providers.forEach((p) => {
|
||||||
// @ts-ignore eslint-disable-next-line
|
// @ts-ignore eslint-disable-next-line
|
||||||
delete p.enabled
|
delete p.enabled
|
||||||
@ -1192,6 +1192,16 @@ const migrateConfig = {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'95': (state: RootState) => {
|
||||||
|
try {
|
||||||
|
addWebSearchProvider(state, 'local-google')
|
||||||
|
addWebSearchProvider(state, 'local-bing')
|
||||||
|
addWebSearchProvider(state, 'local-baidu')
|
||||||
|
return state
|
||||||
|
} catch (error) {
|
||||||
|
return state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user