mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-22 00:13:09 +08:00
Revert "fix: complete PoeLogo rendering support across provider UI components (#9756)"
This reverts commit df7fd26907.
This commit is contained in:
parent
80f49aecd7
commit
6fb878d3b6
@ -1,9 +1,6 @@
|
|||||||
import { SearchOutlined } from '@ant-design/icons'
|
import { SearchOutlined } from '@ant-design/icons'
|
||||||
import { PROVIDER_LOGO_MAP } from '@renderer/config/providers'
|
import { PROVIDER_LOGO_MAP } from '@renderer/config/providers'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
import { useAppSelector } from '@renderer/store'
|
|
||||||
import { isSystemProvider } from '@renderer/types'
|
|
||||||
import { Input, Tooltip } from 'antd'
|
import { Input, Tooltip } from 'antd'
|
||||||
import { FC, useMemo, useState } from 'react'
|
import { FC, useMemo, useState } from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -15,21 +12,19 @@ interface Props {
|
|||||||
// 用于选择内置头像的提供商Logo选择器组件
|
// 用于选择内置头像的提供商Logo选择器组件
|
||||||
const ProviderLogoPicker: FC<Props> = ({ onProviderClick }) => {
|
const ProviderLogoPicker: FC<Props> = ({ onProviderClick }) => {
|
||||||
const [searchText, setSearchText] = useState('')
|
const [searchText, setSearchText] = useState('')
|
||||||
const providers = useAppSelector((state) => state.llm.providers)
|
|
||||||
|
|
||||||
const { ProviderAvatar } = useProviderAvatar()
|
|
||||||
const filteredProviders = useMemo(() => {
|
const filteredProviders = useMemo(() => {
|
||||||
const _providers = providers.filter(isSystemProvider).map((p) => ({
|
const providers = Object.entries(PROVIDER_LOGO_MAP).map(([id, logo]) => ({
|
||||||
id: p.id,
|
id,
|
||||||
name: p.name,
|
logo,
|
||||||
label: getProviderLabel(p.id),
|
name: getProviderLabel(id)
|
||||||
logo: PROVIDER_LOGO_MAP[p.id]
|
|
||||||
}))
|
}))
|
||||||
if (!searchText) return _providers
|
|
||||||
|
if (!searchText) return providers
|
||||||
|
|
||||||
const searchLower = searchText.toLowerCase()
|
const searchLower = searchText.toLowerCase()
|
||||||
return _providers.filter((p) => `${p.name} ${p.id} ${p.label}`.toLowerCase().includes(searchLower))
|
return providers.filter((p) => p.name.toLowerCase().includes(searchLower))
|
||||||
}, [providers, searchText])
|
}, [searchText])
|
||||||
|
|
||||||
const handleProviderClick = (event: React.MouseEvent, providerId: string) => {
|
const handleProviderClick = (event: React.MouseEvent, providerId: string) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@ -53,10 +48,10 @@ const ProviderLogoPicker: FC<Props> = ({ onProviderClick }) => {
|
|||||||
/>
|
/>
|
||||||
</SearchContainer>
|
</SearchContainer>
|
||||||
<LogoGrid>
|
<LogoGrid>
|
||||||
{filteredProviders.map(({ id, label }) => (
|
{filteredProviders.map(({ id, logo, name }) => (
|
||||||
<Tooltip key={id} title={label} placement="top" mouseLeaveDelay={0}>
|
<Tooltip key={id} title={name} placement="top" mouseLeaveDelay={0}>
|
||||||
<LogoItem onClick={(e) => handleProviderClick(e, id)}>
|
<LogoItem onClick={(e) => handleProviderClick(e, id)}>
|
||||||
<ProviderAvatar pid={id} size={32} />
|
<img src={logo} alt={name} draggable={false} />
|
||||||
</LogoItem>
|
</LogoItem>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -664,6 +664,10 @@ export const PROVIDER_LOGO_MAP: AtLeast<SystemProviderId, string> = {
|
|||||||
poe: 'svg' // use svg icon component
|
poe: 'svg' // use svg icon component
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
export function getProviderLogo(providerId: string) {
|
||||||
|
return PROVIDER_LOGO_MAP[providerId as keyof typeof PROVIDER_LOGO_MAP]
|
||||||
|
}
|
||||||
|
|
||||||
// export const SUPPORTED_REANK_PROVIDERS = ['silicon', 'jina', 'voyageai', 'dashscope', 'aihubmix']
|
// export const SUPPORTED_REANK_PROVIDERS = ['silicon', 'jina', 'voyageai', 'dashscope', 'aihubmix']
|
||||||
export const NOT_SUPPORTED_RERANK_PROVIDERS = ['ollama', 'lmstudio'] as const satisfies SystemProviderId[]
|
export const NOT_SUPPORTED_RERANK_PROVIDERS = ['ollama', 'lmstudio'] as const satisfies SystemProviderId[]
|
||||||
export const ONLY_SUPPORTED_DIMENSION_PROVIDERS = ['ollama', 'infini'] as const satisfies SystemProviderId[]
|
export const ONLY_SUPPORTED_DIMENSION_PROVIDERS = ['ollama', 'infini'] as const satisfies SystemProviderId[]
|
||||||
|
|||||||
@ -1,112 +0,0 @@
|
|||||||
import { loggerService } from '@logger'
|
|
||||||
import { PoeLogo } from '@renderer/components/Icons'
|
|
||||||
import { PROVIDER_LOGO_MAP } from '@renderer/config/providers'
|
|
||||||
import ImageStorage from '@renderer/services/ImageStorage'
|
|
||||||
import { getProviderById } from '@renderer/services/ProviderService'
|
|
||||||
import { useAppSelector } from '@renderer/store'
|
|
||||||
import { removeLogo, setLogo, setLogos } from '@renderer/store/llm'
|
|
||||||
import { isSystemProviderId, Provider } from '@renderer/types'
|
|
||||||
import { generateColorFromChar, getFirstCharacter, getForegroundColor } from '@renderer/utils'
|
|
||||||
import { Avatar, AvatarProps } from 'antd'
|
|
||||||
import { AvatarSize } from 'antd/es/avatar/AvatarContext'
|
|
||||||
import { isEmpty } from 'lodash'
|
|
||||||
import { useCallback, useEffect } from 'react'
|
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
import styled, { CSSProperties } from 'styled-components'
|
|
||||||
|
|
||||||
const logger = loggerService.withContext('useProviderLogo')
|
|
||||||
|
|
||||||
export const useProviderAvatar = () => {
|
|
||||||
const providers = useAppSelector((state) => state.llm.providers)
|
|
||||||
const logos = useAppSelector((state) => state.llm.logos)
|
|
||||||
const dispatch = useDispatch()
|
|
||||||
|
|
||||||
const saveLogo = useCallback(
|
|
||||||
async (logo: string, providerId: string) => {
|
|
||||||
ImageStorage.set(`provider-${providerId}`, logo)
|
|
||||||
dispatch(setLogo({ id: providerId, logo }))
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
)
|
|
||||||
|
|
||||||
const deleteLogo = useCallback(
|
|
||||||
async (id: string) => {
|
|
||||||
ImageStorage.remove(id).catch((e) => logger.error('Falied to remove image.', e as Error))
|
|
||||||
dispatch(removeLogo(id))
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const getLogos = async () => {
|
|
||||||
const _logos = {}
|
|
||||||
for (const p of providers) {
|
|
||||||
_logos[p.id] = await ImageStorage.get(`provider-${p.id}`)
|
|
||||||
}
|
|
||||||
dispatch(setLogos(_logos))
|
|
||||||
}
|
|
||||||
getLogos()
|
|
||||||
}, [dispatch, providers])
|
|
||||||
|
|
||||||
const ProviderAvatar = useCallback(
|
|
||||||
({
|
|
||||||
pid,
|
|
||||||
name,
|
|
||||||
size,
|
|
||||||
src,
|
|
||||||
style,
|
|
||||||
...rest
|
|
||||||
}: {
|
|
||||||
pid?: string
|
|
||||||
name?: string
|
|
||||||
size?: number
|
|
||||||
src?: string
|
|
||||||
} & AvatarProps) => {
|
|
||||||
if (src) {
|
|
||||||
return <ProviderLogo draggable="false" shape="square" src={src} size={size} style={style} {...rest} />
|
|
||||||
}
|
|
||||||
let provider: Provider | undefined
|
|
||||||
if (pid) {
|
|
||||||
// 特殊处理一下svg格式
|
|
||||||
if (isSystemProviderId(pid)) {
|
|
||||||
const logoSrc = PROVIDER_LOGO_MAP[pid]
|
|
||||||
switch (pid) {
|
|
||||||
case 'poe':
|
|
||||||
return <PoeLogo fontSize={typeof size === 'number' ? size : 18} style={style} />
|
|
||||||
default:
|
|
||||||
return <ProviderLogo draggable="false" shape="square" src={logoSrc} size={size} style={style} {...rest} />
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const customLogo = logos[pid]
|
|
||||||
if (customLogo) {
|
|
||||||
return <ProviderLogo draggable="false" shape="square" src={customLogo} size={size} style={style} {...rest} />
|
|
||||||
}
|
|
||||||
if (!name) {
|
|
||||||
// generate a avatar for custom provider
|
|
||||||
provider = getProviderById(pid)
|
|
||||||
if (!provider) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return <GeneratedAvatar name={name ?? provider?.name ?? 'P'} size={size} style={style} />
|
|
||||||
},
|
|
||||||
[logos]
|
|
||||||
)
|
|
||||||
|
|
||||||
function GeneratedAvatar({ name, size, style }: { name: string; size?: AvatarSize; style?: CSSProperties }) {
|
|
||||||
const backgroundColor = generateColorFromChar(name)
|
|
||||||
const color = name ? getForegroundColor(backgroundColor) : 'white'
|
|
||||||
return (
|
|
||||||
<ProviderLogo size={size} shape="square" style={{ backgroundColor, color, ...style }}>
|
|
||||||
{getFirstCharacter(!isEmpty(name) ? name : 'P')}
|
|
||||||
</ProviderLogo>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return { ProviderAvatar, GeneratedAvatar, saveLogo, deleteLogo, logos }
|
|
||||||
}
|
|
||||||
|
|
||||||
const ProviderLogo = styled(Avatar)`
|
|
||||||
border: 0.5px solid var(--color-border);
|
|
||||||
`
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时器管理 Hook,用于管理 setTimeout 和 setInterval 定时器,支持通过 key 来标识不同的定时器
|
* 定时器管理 Hook,用于管理 setTimeout 和 setInterval 定时器,支持通过 key 来标识不同的定时器
|
||||||
@ -65,12 +65,12 @@ export const useTimer = () => {
|
|||||||
* cleanup();
|
* cleanup();
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
const setTimeoutTimer = useCallback((key: string, ...args: Parameters<typeof setTimeout>) => {
|
const setTimeoutTimer = (key: string, ...args: Parameters<typeof setTimeout>) => {
|
||||||
clearTimeout(timeoutMapRef.current.get(key))
|
clearTimeout(timeoutMapRef.current.get(key))
|
||||||
const timer = setTimeout(...args)
|
const timer = setTimeout(...args)
|
||||||
timeoutMapRef.current.set(key, timer)
|
timeoutMapRef.current.set(key, timer)
|
||||||
return () => clearTimeoutTimer(key)
|
return () => clearTimeoutTimer(key)
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置一个 setInterval 定时器
|
* 设置一个 setInterval 定时器
|
||||||
@ -89,12 +89,12 @@ export const useTimer = () => {
|
|||||||
* cleanup();
|
* cleanup();
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
const setIntervalTimer = useCallback((key: string, ...args: Parameters<typeof setInterval>) => {
|
const setIntervalTimer = (key: string, ...args: Parameters<typeof setInterval>) => {
|
||||||
clearInterval(intervalMapRef.current.get(key))
|
clearInterval(intervalMapRef.current.get(key))
|
||||||
const timer = setInterval(...args)
|
const timer = setInterval(...args)
|
||||||
intervalMapRef.current.set(key, timer)
|
intervalMapRef.current.set(key, timer)
|
||||||
return () => clearIntervalTimer(key)
|
return () => clearIntervalTimer(key)
|
||||||
}, [])
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除指定 key 的 setTimeout 定时器
|
* 清除指定 key 的 setTimeout 定时器
|
||||||
|
|||||||
@ -7,11 +7,11 @@ import { HStack } from '@renderer/components/Layout'
|
|||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
import TranslateButton from '@renderer/components/TranslateButton'
|
import TranslateButton from '@renderer/components/TranslateButton'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { LanguagesEnum } from '@renderer/config/translate'
|
import { LanguagesEnum } from '@renderer/config/translate'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { usePaintings } from '@renderer/hooks/usePaintings'
|
import { usePaintings } from '@renderer/hooks/usePaintings'
|
||||||
import { useAllProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders } from '@renderer/hooks/useProvider'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
@ -22,7 +22,7 @@ import { setGenerating } from '@renderer/store/runtime'
|
|||||||
import type { FileMetadata } from '@renderer/types'
|
import type { FileMetadata } from '@renderer/types'
|
||||||
import type { PaintingAction, PaintingsState } from '@renderer/types'
|
import type { PaintingAction, PaintingsState } from '@renderer/types'
|
||||||
import { getErrorMessage, uuid } from '@renderer/utils'
|
import { getErrorMessage, uuid } from '@renderer/utils'
|
||||||
import { Button, Input, InputNumber, Radio, Segmented, Select, Slider, Switch, Tooltip, Upload } from 'antd'
|
import { Avatar, Button, Input, InputNumber, Radio, Segmented, Select, Slider, Switch, Tooltip, Upload } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import { Info } from 'lucide-react'
|
import { Info } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
@ -54,7 +54,6 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
aihubmix_image_edit,
|
aihubmix_image_edit,
|
||||||
aihubmix_image_upscale
|
aihubmix_image_upscale
|
||||||
} = usePaintings()
|
} = usePaintings()
|
||||||
const { ProviderAvatar } = useProviderAvatar()
|
|
||||||
|
|
||||||
const paintings = useMemo(() => {
|
const paintings = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
@ -848,7 +847,12 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
<SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle>
|
<SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle>
|
||||||
<SettingHelpLink target="_blank" href={aihubmixProvider.apiHost}>
|
<SettingHelpLink target="_blank" href={aihubmixProvider.apiHost}>
|
||||||
{t('paintings.learn_more')}
|
{t('paintings.learn_more')}
|
||||||
<ProviderAvatar pid={aihubmixProvider.id} size={16} style={{ marginLeft: 5 }} />
|
<ProviderLogo
|
||||||
|
shape="square"
|
||||||
|
src={getProviderLogo(aihubmixProvider.id)}
|
||||||
|
size={16}
|
||||||
|
style={{ marginLeft: 5 }}
|
||||||
|
/>
|
||||||
</SettingHelpLink>
|
</SettingHelpLink>
|
||||||
</ProviderTitleContainer>
|
</ProviderTitleContainer>
|
||||||
|
|
||||||
@ -856,7 +860,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
{providerOptions.map((provider) => (
|
{providerOptions.map((provider) => (
|
||||||
<Select.Option value={provider.value} key={provider.value}>
|
<Select.Option value={provider.value} key={provider.value}>
|
||||||
<SelectOptionContainer>
|
<SelectOptionContainer>
|
||||||
<ProviderAvatar pid={provider.value ?? ''} size={16} />
|
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} />
|
||||||
{provider.label}
|
{provider.label}
|
||||||
</SelectOptionContainer>
|
</SelectOptionContainer>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@ -1025,6 +1029,10 @@ const StyledInputNumber = styled(InputNumber)`
|
|||||||
width: 70px;
|
width: 70px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ProviderLogo = styled(Avatar)`
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
`
|
||||||
|
|
||||||
// 添加新的样式组件
|
// 添加新的样式组件
|
||||||
const ModeSegmentedContainer = styled.div`
|
const ModeSegmentedContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navb
|
|||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { usePaintings } from '@renderer/hooks/usePaintings'
|
import { usePaintings } from '@renderer/hooks/usePaintings'
|
||||||
import { useAllProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders } from '@renderer/hooks/useProvider'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
import FileManager from '@renderer/services/FileManager'
|
import FileManager from '@renderer/services/FileManager'
|
||||||
@ -15,7 +15,7 @@ import { setGenerating } from '@renderer/store/runtime'
|
|||||||
import type { FileMetadata } from '@renderer/types'
|
import type { FileMetadata } from '@renderer/types'
|
||||||
import { convertToBase64, uuid } from '@renderer/utils'
|
import { convertToBase64, uuid } from '@renderer/utils'
|
||||||
import { DmxapiPainting } from '@types'
|
import { DmxapiPainting } from '@types'
|
||||||
import { Button, Input, InputNumber, Segmented, Select, Switch, Tooltip } from 'antd'
|
import { Avatar, Button, Input, InputNumber, Segmented, Select, Switch, Tooltip } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import { Info } from 'lucide-react'
|
import { Info } from 'lucide-react'
|
||||||
import React, { FC, useEffect, useRef, useState } from 'react'
|
import React, { FC, useEffect, useRef, useState } from 'react'
|
||||||
@ -46,7 +46,6 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
const [painting, setPainting] = useState<DmxapiPainting>(dmxapi_paintings?.[0] || DEFAULT_PAINTING)
|
const [painting, setPainting] = useState<DmxapiPainting>(dmxapi_paintings?.[0] || DEFAULT_PAINTING)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const providers = useAllProviders()
|
const providers = useAllProviders()
|
||||||
const { ProviderAvatar } = useProviderAvatar()
|
|
||||||
const providerOptions = Options.map((option) => {
|
const providerOptions = Options.map((option) => {
|
||||||
const provider = providers.find((p) => p.id === option)
|
const provider = providers.find((p) => p.id === option)
|
||||||
if (provider) {
|
if (provider) {
|
||||||
@ -815,15 +814,19 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
<SettingHelpLink target="_blank" href={TOP_UP_URL}>
|
<SettingHelpLink target="_blank" href={TOP_UP_URL}>
|
||||||
{t('paintings.top_up')}
|
{t('paintings.top_up')}
|
||||||
</SettingHelpLink>
|
</SettingHelpLink>
|
||||||
<ProviderAvatar pid={dmxapiProvider.id} size={16} style={{ marginLeft: 5 }} />
|
<ProviderLogo
|
||||||
|
shape="square"
|
||||||
|
src={getProviderLogo(dmxapiProvider.id)}
|
||||||
|
size={16}
|
||||||
|
style={{ marginLeft: 5 }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ProviderTitleContainer>
|
</ProviderTitleContainer>
|
||||||
<Select value={providerOptions[3].value} onChange={handleProviderChange} style={{ marginBottom: 15 }}>
|
<Select value={providerOptions[3].value} onChange={handleProviderChange} style={{ marginBottom: 15 }}>
|
||||||
{providerOptions.map((provider) => (
|
{providerOptions.map((provider) => (
|
||||||
<Select.Option value={provider.value} key={provider.value}>
|
<Select.Option value={provider.value} key={provider.value}>
|
||||||
<SelectOptionContainer>
|
<SelectOptionContainer>
|
||||||
<ProviderAvatar pid={provider.value || ''} size={16} />
|
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} />
|
||||||
|
|
||||||
{provider.label}
|
{provider.label}
|
||||||
</SelectOptionContainer>
|
</SelectOptionContainer>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@ -1035,6 +1038,9 @@ const ProviderTitleContainer = styled.div`
|
|||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ProviderLogo = styled(Avatar)`
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
`
|
||||||
const SelectOptionContainer = styled.div`
|
const SelectOptionContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -6,11 +6,11 @@ import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navb
|
|||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
import TranslateButton from '@renderer/components/TranslateButton'
|
import TranslateButton from '@renderer/components/TranslateButton'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { LanguagesEnum } from '@renderer/config/translate'
|
import { LanguagesEnum } from '@renderer/config/translate'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { usePaintings } from '@renderer/hooks/usePaintings'
|
import { usePaintings } from '@renderer/hooks/usePaintings'
|
||||||
import { useAllProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders } from '@renderer/hooks/useProvider'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import {
|
import {
|
||||||
@ -29,7 +29,7 @@ import { setGenerating } from '@renderer/store/runtime'
|
|||||||
import type { PaintingAction, PaintingsState } from '@renderer/types'
|
import type { PaintingAction, PaintingsState } from '@renderer/types'
|
||||||
import { FileMetadata } from '@renderer/types'
|
import { FileMetadata } from '@renderer/types'
|
||||||
import { getErrorMessage, uuid } from '@renderer/utils'
|
import { getErrorMessage, uuid } from '@renderer/utils'
|
||||||
import { Button, Empty, InputNumber, Segmented, Select, Upload } from 'antd'
|
import { Avatar, Button, Empty, InputNumber, Segmented, Select, Upload } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import React, { FC } from 'react'
|
import React, { FC } from 'react'
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
@ -47,7 +47,6 @@ const logger = loggerService.withContext('NewApiPage')
|
|||||||
const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||||
const [mode, setMode] = useState<keyof PaintingsState>('openai_image_generate')
|
const [mode, setMode] = useState<keyof PaintingsState>('openai_image_generate')
|
||||||
const { addPainting, removePainting, updatePainting, openai_image_generate, openai_image_edit } = usePaintings()
|
const { addPainting, removePainting, updatePainting, openai_image_generate, openai_image_edit } = usePaintings()
|
||||||
const { ProviderAvatar } = useProviderAvatar()
|
|
||||||
|
|
||||||
const newApiPaintings = useMemo(() => {
|
const newApiPaintings = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
@ -510,7 +509,12 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
<SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle>
|
<SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle>
|
||||||
<SettingHelpLink target="_blank" href={'https://docs.newapi.pro/apps/cherry-studio/'}>
|
<SettingHelpLink target="_blank" href={'https://docs.newapi.pro/apps/cherry-studio/'}>
|
||||||
{t('paintings.learn_more')}
|
{t('paintings.learn_more')}
|
||||||
<ProviderAvatar pid={newApiProvider.id} size={16} style={{ marginLeft: 5 }} />
|
<ProviderLogo
|
||||||
|
shape="square"
|
||||||
|
src={getProviderLogo(newApiProvider.id)}
|
||||||
|
size={16}
|
||||||
|
style={{ marginLeft: 5 }}
|
||||||
|
/>
|
||||||
</SettingHelpLink>
|
</SettingHelpLink>
|
||||||
</ProviderTitleContainer>
|
</ProviderTitleContainer>
|
||||||
|
|
||||||
@ -521,7 +525,7 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
{providerOptions.map((provider) => (
|
{providerOptions.map((provider) => (
|
||||||
<Select.Option value={provider.value} key={provider.value}>
|
<Select.Option value={provider.value} key={provider.value}>
|
||||||
<SelectOptionContainer>
|
<SelectOptionContainer>
|
||||||
<ProviderAvatar pid={provider.value} size={16} />
|
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} />
|
||||||
{provider.label}
|
{provider.label}
|
||||||
</SelectOptionContainer>
|
</SelectOptionContainer>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@ -790,6 +794,10 @@ const ToolbarMenu = styled.div`
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ProviderLogo = styled(Avatar)`
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
`
|
||||||
|
|
||||||
// 添加新的样式组件
|
// 添加新的样式组件
|
||||||
const ModeSegmentedContainer = styled.div`
|
const ModeSegmentedContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -4,10 +4,10 @@ import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navb
|
|||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
import TranslateButton from '@renderer/components/TranslateButton'
|
import TranslateButton from '@renderer/components/TranslateButton'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { LanguagesEnum } from '@renderer/config/translate'
|
import { LanguagesEnum } from '@renderer/config/translate'
|
||||||
import { usePaintings } from '@renderer/hooks/usePaintings'
|
import { usePaintings } from '@renderer/hooks/usePaintings'
|
||||||
import { useAllProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders } from '@renderer/hooks/useProvider'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
@ -17,7 +17,7 @@ import { useAppDispatch } from '@renderer/store'
|
|||||||
import { setGenerating } from '@renderer/store/runtime'
|
import { setGenerating } from '@renderer/store/runtime'
|
||||||
import type { TokenFluxPainting } from '@renderer/types'
|
import type { TokenFluxPainting } from '@renderer/types'
|
||||||
import { getErrorMessage, uuid } from '@renderer/utils'
|
import { getErrorMessage, uuid } from '@renderer/utils'
|
||||||
import { Button, Select, Tooltip } from 'antd'
|
import { Avatar, Button, Select, Tooltip } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import { Info } from 'lucide-react'
|
import { Info } from 'lucide-react'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
@ -50,7 +50,6 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
const { t, i18n } = useTranslation()
|
const { t, i18n } = useTranslation()
|
||||||
const providers = useAllProviders()
|
const providers = useAllProviders()
|
||||||
const { addPainting, removePainting, updatePainting, tokenflux_paintings } = usePaintings()
|
const { addPainting, removePainting, updatePainting, tokenflux_paintings } = usePaintings()
|
||||||
const { ProviderAvatar } = useProviderAvatar()
|
|
||||||
const tokenFluxPaintings = tokenflux_paintings
|
const tokenFluxPaintings = tokenflux_paintings
|
||||||
const [painting, setPainting] = useState<TokenFluxPainting>(
|
const [painting, setPainting] = useState<TokenFluxPainting>(
|
||||||
tokenFluxPaintings[0] || { ...DEFAULT_TOKENFLUX_PAINTING, id: uuid() }
|
tokenFluxPaintings[0] || { ...DEFAULT_TOKENFLUX_PAINTING, id: uuid() }
|
||||||
@ -384,7 +383,7 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
<SettingTitle style={{ marginBottom: 8 }}>{t('common.provider')}</SettingTitle>
|
<SettingTitle style={{ marginBottom: 8 }}>{t('common.provider')}</SettingTitle>
|
||||||
<SettingHelpLink target="_blank" href="https://tokenflux.ai">
|
<SettingHelpLink target="_blank" href="https://tokenflux.ai">
|
||||||
{t('paintings.learn_more')}
|
{t('paintings.learn_more')}
|
||||||
<ProviderAvatar pid={'tokenflux'} size={16} style={{ marginLeft: 5 }} />
|
<ProviderLogo shape="square" src={getProviderLogo('tokenflux')} size={16} style={{ marginLeft: 5 }} />
|
||||||
</SettingHelpLink>
|
</SettingHelpLink>
|
||||||
</ProviderTitleContainer>
|
</ProviderTitleContainer>
|
||||||
|
|
||||||
@ -395,7 +394,7 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
{providerOptions.map((provider) => (
|
{providerOptions.map((provider) => (
|
||||||
<Select.Option value={provider.value} key={provider.value}>
|
<Select.Option value={provider.value} key={provider.value}>
|
||||||
<SelectOptionContainer>
|
<SelectOptionContainer>
|
||||||
<ProviderAvatar pid={provider.value} size={16} />
|
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} />
|
||||||
{provider.label}
|
{provider.label}
|
||||||
</SelectOptionContainer>
|
</SelectOptionContainer>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@ -766,6 +765,10 @@ const InfoIcon = styled(Info)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ProviderLogo = styled(Avatar)`
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
`
|
||||||
|
|
||||||
const ProviderTitleContainer = styled.div`
|
const ProviderTitleContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@ -4,16 +4,16 @@ import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navb
|
|||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import Scrollbar from '@renderer/components/Scrollbar'
|
import Scrollbar from '@renderer/components/Scrollbar'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { usePaintings } from '@renderer/hooks/usePaintings'
|
import { usePaintings } from '@renderer/hooks/usePaintings'
|
||||||
import { useAllProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders } from '@renderer/hooks/useProvider'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
import { getProviderLabel } from '@renderer/i18n/label'
|
import { getProviderLabel } from '@renderer/i18n/label'
|
||||||
import FileManager from '@renderer/services/FileManager'
|
import FileManager from '@renderer/services/FileManager'
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { setGenerating } from '@renderer/store/runtime'
|
import { setGenerating } from '@renderer/store/runtime'
|
||||||
import { getErrorMessage, uuid } from '@renderer/utils'
|
import { getErrorMessage, uuid } from '@renderer/utils'
|
||||||
import { Button, InputNumber, Radio, Select } from 'antd'
|
import { Avatar, Button, InputNumber, Radio, Select } from 'antd'
|
||||||
import TextArea from 'antd/es/input/TextArea'
|
import TextArea from 'antd/es/input/TextArea'
|
||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -39,7 +39,6 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
const [painting, setPainting] = useState<any>(zhipu_paintings?.[0] || DEFAULT_PAINTING)
|
const [painting, setPainting] = useState<any>(zhipu_paintings?.[0] || DEFAULT_PAINTING)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const providers = useAllProviders()
|
const providers = useAllProviders()
|
||||||
const { ProviderAvatar } = useProviderAvatar()
|
|
||||||
|
|
||||||
// 确保painting使用智谱的cogview系列模型
|
// 确保painting使用智谱的cogview系列模型
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -369,14 +368,19 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
|||||||
<SettingHelpLink target="_blank" href={COURSE_URL}>
|
<SettingHelpLink target="_blank" href={COURSE_URL}>
|
||||||
{t('paintings.paint_course')}
|
{t('paintings.paint_course')}
|
||||||
</SettingHelpLink>
|
</SettingHelpLink>
|
||||||
<ProviderAvatar pid={zhipuProvider.id} size={16} style={{ marginLeft: 5 }} />
|
<ProviderLogo
|
||||||
|
shape="square"
|
||||||
|
src={getProviderLogo(zhipuProvider.id)}
|
||||||
|
size={16}
|
||||||
|
style={{ marginLeft: 5 }}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ProviderTitleContainer>
|
</ProviderTitleContainer>
|
||||||
<Select value={providerOptions[0].value} onChange={handleProviderChange} style={{ marginBottom: 15 }}>
|
<Select value={providerOptions[0].value} onChange={handleProviderChange} style={{ marginBottom: 15 }}>
|
||||||
{providerOptions.map((provider) => (
|
{providerOptions.map((provider) => (
|
||||||
<Select.Option value={provider.value} key={provider.value}>
|
<Select.Option value={provider.value} key={provider.value}>
|
||||||
<SelectOptionContainer>
|
<SelectOptionContainer>
|
||||||
<ProviderAvatar pid={provider.value} size={16} />
|
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} />
|
||||||
{provider.label}
|
{provider.label}
|
||||||
</SelectOptionContainer>
|
</SelectOptionContainer>
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
@ -577,4 +581,8 @@ const SelectOptionContainer = styled.div`
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ProviderLogo = styled(Avatar)`
|
||||||
|
border-radius: 4px;
|
||||||
|
`
|
||||||
|
|
||||||
export default ZhipuPage
|
export default ZhipuPage
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
|
import { loggerService } from '@logger'
|
||||||
import { Center, VStack } from '@renderer/components/Layout'
|
import { Center, VStack } from '@renderer/components/Layout'
|
||||||
import ProviderLogoPicker from '@renderer/components/ProviderLogoPicker'
|
import ProviderLogoPicker from '@renderer/components/ProviderLogoPicker'
|
||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { PROVIDER_LOGO_MAP } from '@renderer/config/providers'
|
import { PROVIDER_LOGO_MAP } from '@renderer/config/providers'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import ImageStorage from '@renderer/services/ImageStorage'
|
import ImageStorage from '@renderer/services/ImageStorage'
|
||||||
import { Provider, ProviderType } from '@renderer/types'
|
import { Provider, ProviderType } from '@renderer/types'
|
||||||
import { compressImage } from '@renderer/utils'
|
import { compressImage, generateColorFromChar, getForegroundColor } from '@renderer/utils'
|
||||||
import { Divider, Dropdown, Form, Input, Modal, Popover, Select, Upload } from 'antd'
|
import { Divider, Dropdown, Form, Input, Modal, Popover, Select, Upload } from 'antd'
|
||||||
import { ItemType } from 'antd/es/menu/interface'
|
import { ItemType } from 'antd/es/menu/interface'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
// const logger = loggerService.withContext('AddProviderPopup')
|
const logger = loggerService.withContext('AddProviderPopup')
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
provider?: Provider
|
provider?: Provider
|
||||||
@ -23,20 +23,27 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const [name, setName] = useState(provider?.name || '')
|
const [name, setName] = useState(provider?.name || '')
|
||||||
const [type, setType] = useState<ProviderType>(provider?.type || 'openai')
|
const [type, setType] = useState<ProviderType>(provider?.type || 'openai')
|
||||||
|
const [logo, setLogo] = useState<string | null>(null)
|
||||||
const [logoPickerOpen, setLogoPickerOpen] = useState(false)
|
const [logoPickerOpen, setLogoPickerOpen] = useState(false)
|
||||||
const [dropdownOpen, setDropdownOpen] = useState(false)
|
const [dropdownOpen, setDropdownOpen] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const uploadRef = useRef<HTMLDivElement>(null)
|
const uploadRef = useRef<HTMLDivElement>(null)
|
||||||
const [logo, setLogo] = useState<string>()
|
|
||||||
|
|
||||||
const { ProviderAvatar, logos, saveLogo } = useProviderAvatar()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (provider) {
|
if (provider?.id) {
|
||||||
const logo = logos[provider.id]
|
const loadLogo = async () => {
|
||||||
setLogo(logo)
|
try {
|
||||||
|
const logoData = await ImageStorage.get(`provider-${provider.id}`)
|
||||||
|
if (logoData) {
|
||||||
|
setLogo(logoData)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to load logo', error as Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadLogo()
|
||||||
}
|
}
|
||||||
}, [provider, logos, setLogo])
|
}, [provider])
|
||||||
|
|
||||||
const onOk = async () => {
|
const onOk = async () => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
@ -67,9 +74,12 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
const logoUrl = PROVIDER_LOGO_MAP[providerId]
|
const logoUrl = PROVIDER_LOGO_MAP[providerId]
|
||||||
|
|
||||||
if (provider?.id) {
|
if (provider?.id) {
|
||||||
saveLogo(logoUrl, provider.id)
|
await ImageStorage.set(`provider-${provider.id}`, logoUrl)
|
||||||
|
const savedLogo = await ImageStorage.get(`provider-${provider.id}`)
|
||||||
|
setLogo(savedLogo)
|
||||||
|
} else {
|
||||||
|
setLogo(logoUrl)
|
||||||
}
|
}
|
||||||
setLogo(logoUrl)
|
|
||||||
|
|
||||||
setLogoPickerOpen(false)
|
setLogoPickerOpen(false)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@ -79,9 +89,10 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
|
|
||||||
const handleReset = async () => {
|
const handleReset = async () => {
|
||||||
try {
|
try {
|
||||||
|
setLogo(null)
|
||||||
|
|
||||||
if (provider?.id) {
|
if (provider?.id) {
|
||||||
saveLogo('', provider.id)
|
await ImageStorage.set(`provider-${provider.id}`, '')
|
||||||
ImageStorage.set(`provider-${provider.id}`, '')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDropdownOpen(false)
|
setDropdownOpen(false)
|
||||||
@ -90,6 +101,10 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getInitials = () => {
|
||||||
|
return name.charAt(0) || 'P'
|
||||||
|
}
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
key: 'upload',
|
key: 'upload',
|
||||||
@ -118,7 +133,7 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
await ImageStorage.set(`provider-${provider.id}`, logoData)
|
await ImageStorage.set(`provider-${provider.id}`, logoData)
|
||||||
}
|
}
|
||||||
const savedLogo = await ImageStorage.get(`provider-${provider.id}`)
|
const savedLogo = await ImageStorage.get(`provider-${provider.id}`)
|
||||||
saveLogo(savedLogo, provider.id)
|
setLogo(savedLogo)
|
||||||
} else {
|
} else {
|
||||||
// 临时保存在内存中,等创建 provider 后会在调用方保存
|
// 临时保存在内存中,等创建 provider 后会在调用方保存
|
||||||
const tempUrl = await new Promise<string>((resolve) => {
|
const tempUrl = await new Promise<string>((resolve) => {
|
||||||
@ -156,6 +171,10 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
}
|
}
|
||||||
] satisfies ItemType[]
|
] satisfies ItemType[]
|
||||||
|
|
||||||
|
// for logo
|
||||||
|
const backgroundColor = generateColorFromChar(name)
|
||||||
|
const color = name ? getForegroundColor(backgroundColor) : 'white'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
@ -195,9 +214,13 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
placement="bottom">
|
placement="bottom">
|
||||||
<ProviderLogo>
|
{logo ? (
|
||||||
<ProviderAvatar pid={provider?.id} name={name} src={logo} size={60} style={{ fontSize: 32 }} />
|
<ProviderLogo src={logo} />
|
||||||
</ProviderLogo>
|
) : (
|
||||||
|
<ProviderInitialsLogo style={name ? { backgroundColor, color } : undefined}>
|
||||||
|
{getInitials()}
|
||||||
|
</ProviderInitialsLogo>
|
||||||
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</VStack>
|
</VStack>
|
||||||
@ -235,6 +258,39 @@ const PopupContainer: React.FC<Props> = ({ provider, resolve }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ProviderLogo = styled.img`
|
||||||
|
cursor: pointer;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 12px;
|
||||||
|
object-fit: contain;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
padding: 5px;
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ProviderInitialsLogo = styled.div`
|
||||||
|
cursor: pointer;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
background-color: var(--color-background-soft);
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const MenuItem = styled.div`
|
const MenuItem = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -265,15 +321,3 @@ export default class AddProviderPopup {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProviderLogo = styled.div`
|
|
||||||
cursor: pointer;
|
|
||||||
object-fit: contain;
|
|
||||||
border-radius: 12px;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
background-color: var(--color-background-soft);
|
|
||||||
border: 0.5px solid var(--color-border);
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|||||||
@ -5,13 +5,22 @@ import {
|
|||||||
type DraggableVirtualListRef,
|
type DraggableVirtualListRef,
|
||||||
useDraggableReorder
|
useDraggableReorder
|
||||||
} from '@renderer/components/DraggableList'
|
} from '@renderer/components/DraggableList'
|
||||||
import { DeleteIcon, EditIcon } from '@renderer/components/Icons'
|
import { DeleteIcon, EditIcon, PoeLogo } from '@renderer/components/Icons'
|
||||||
|
import { getProviderLogo } from '@renderer/config/providers'
|
||||||
import { useAllProviders, useProviders } from '@renderer/hooks/useProvider'
|
import { useAllProviders, useProviders } from '@renderer/hooks/useProvider'
|
||||||
import { useProviderAvatar } from '@renderer/hooks/useProviderLogo'
|
|
||||||
import { useTimer } from '@renderer/hooks/useTimer'
|
import { useTimer } from '@renderer/hooks/useTimer'
|
||||||
|
import ImageStorage from '@renderer/services/ImageStorage'
|
||||||
import { isSystemProvider, Provider, ProviderType } from '@renderer/types'
|
import { isSystemProvider, Provider, ProviderType } from '@renderer/types'
|
||||||
import { getFancyProviderName, matchKeywordsInModel, matchKeywordsInProvider, uuid } from '@renderer/utils'
|
import {
|
||||||
import { Button, Dropdown, Input, MenuProps, Tag } from 'antd'
|
generateColorFromChar,
|
||||||
|
getFancyProviderName,
|
||||||
|
getFirstCharacter,
|
||||||
|
getForegroundColor,
|
||||||
|
matchKeywordsInModel,
|
||||||
|
matchKeywordsInProvider,
|
||||||
|
uuid
|
||||||
|
} from '@renderer/utils'
|
||||||
|
import { Avatar, Button, Dropdown, Input, MenuProps, Tag } from 'antd'
|
||||||
import { GripVertical, PlusIcon, Search, UserPen } from 'lucide-react'
|
import { GripVertical, PlusIcon, Search, UserPen } from 'lucide-react'
|
||||||
import { FC, startTransition, useCallback, useEffect, useRef, useState } from 'react'
|
import { FC, startTransition, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -36,13 +45,34 @@ const ProviderList: FC = () => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [searchText, setSearchText] = useState<string>('')
|
const [searchText, setSearchText] = useState<string>('')
|
||||||
const [dragging, setDragging] = useState(false)
|
const [dragging, setDragging] = useState(false)
|
||||||
|
const [providerLogos, setProviderLogos] = useState<Record<string, string>>({})
|
||||||
const listRef = useRef<DraggableVirtualListRef>(null)
|
const listRef = useRef<DraggableVirtualListRef>(null)
|
||||||
const { ProviderAvatar, saveLogo, deleteLogo } = useProviderAvatar()
|
|
||||||
|
|
||||||
const setSelectedProvider = useCallback((provider: Provider) => {
|
const setSelectedProvider = useCallback((provider: Provider) => {
|
||||||
startTransition(() => _setSelectedProvider(provider))
|
startTransition(() => _setSelectedProvider(provider))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadAllLogos = async () => {
|
||||||
|
const logos: Record<string, string> = {}
|
||||||
|
for (const provider of providers) {
|
||||||
|
if (provider.id) {
|
||||||
|
try {
|
||||||
|
const logoData = await ImageStorage.get(`provider-${provider.id}`)
|
||||||
|
if (logoData) {
|
||||||
|
logos[provider.id] = logoData
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to load logo for provider ${provider.id}`, error as Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setProviderLogos(logos)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAllLogos()
|
||||||
|
}, [providers])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchParams.get('id')) {
|
if (searchParams.get('id')) {
|
||||||
const providerId = searchParams.get('id')
|
const providerId = searchParams.get('id')
|
||||||
@ -132,10 +162,17 @@ const ProviderList: FC = () => {
|
|||||||
models: [],
|
models: [],
|
||||||
enabled: true,
|
enabled: true,
|
||||||
isSystem: false
|
isSystem: false
|
||||||
} satisfies Provider
|
} as Provider
|
||||||
|
|
||||||
|
let updatedLogos = { ...providerLogos }
|
||||||
if (logo) {
|
if (logo) {
|
||||||
try {
|
try {
|
||||||
saveLogo(logo, provider.id)
|
await ImageStorage.set(`provider-${provider.id}`, logo)
|
||||||
|
updatedLogos = {
|
||||||
|
...updatedLogos,
|
||||||
|
[provider.id]: logo
|
||||||
|
}
|
||||||
|
setProviderLogos(updatedLogos)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Failed to save logo', error as Error)
|
logger.error('Failed to save logo', error as Error)
|
||||||
window.message.error('保存Provider Logo失败')
|
window.message.error('保存Provider Logo失败')
|
||||||
@ -146,91 +183,132 @@ const ProviderList: FC = () => {
|
|||||||
setSelectedProvider(provider)
|
setSelectedProvider(provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getDropdownMenus = useCallback(
|
const getDropdownMenus = (provider: Provider): MenuProps['items'] => {
|
||||||
(provider: Provider): MenuProps['items'] => {
|
const noteMenu = {
|
||||||
const noteMenu = {
|
label: t('settings.provider.notes.title'),
|
||||||
label: t('settings.provider.notes.title'),
|
key: 'notes',
|
||||||
key: 'notes',
|
icon: <UserPen size={14} />,
|
||||||
icon: <UserPen size={14} />,
|
onClick: () => ModelNotesPopup.show({ provider })
|
||||||
onClick: () => ModelNotesPopup.show({ provider })
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const editMenu = {
|
const editMenu = {
|
||||||
label: t('common.edit'),
|
label: t('common.edit'),
|
||||||
key: 'edit',
|
key: 'edit',
|
||||||
icon: <EditIcon size={14} />,
|
icon: <EditIcon size={14} />,
|
||||||
async onClick() {
|
async onClick() {
|
||||||
const { name, type, logoFile, logo } = await AddProviderPopup.show(provider)
|
const { name, type, logoFile, logo } = await AddProviderPopup.show(provider)
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
updateProvider({ ...provider, name, type })
|
updateProvider({ ...provider, name, type })
|
||||||
if (provider.id) {
|
if (provider.id) {
|
||||||
if (logo) {
|
if (logo) {
|
||||||
try {
|
try {
|
||||||
saveLogo(logo, provider.id)
|
await ImageStorage.set(`provider-${provider.id}`, logo)
|
||||||
} catch (error) {
|
setProviderLogos((prev) => ({
|
||||||
logger.error('Failed to save logo', error as Error)
|
...prev,
|
||||||
window.message.error('更新Provider Logo失败')
|
[provider.id]: logo
|
||||||
}
|
}))
|
||||||
} else if (logo === undefined && logoFile === undefined) {
|
} catch (error) {
|
||||||
try {
|
logger.error('Failed to save logo', error as Error)
|
||||||
deleteLogo(provider.id)
|
window.message.error('更新Provider Logo失败')
|
||||||
} catch (error) {
|
}
|
||||||
logger.error('Failed to reset logo', error as Error)
|
} else if (logo === undefined && logoFile === undefined) {
|
||||||
}
|
try {
|
||||||
|
await ImageStorage.set(`provider-${provider.id}`, '')
|
||||||
|
setProviderLogos((prev) => {
|
||||||
|
const newLogos = { ...prev }
|
||||||
|
delete newLogos[provider.id]
|
||||||
|
return newLogos
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to reset logo', error as Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteMenu = {
|
const deleteMenu = {
|
||||||
label: t('common.delete'),
|
label: t('common.delete'),
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
icon: <DeleteIcon size={14} className="lucide-custom" />,
|
icon: <DeleteIcon size={14} className="lucide-custom" />,
|
||||||
danger: true,
|
danger: true,
|
||||||
async onClick() {
|
async onClick() {
|
||||||
window.modal.confirm({
|
window.modal.confirm({
|
||||||
title: t('settings.provider.delete.title'),
|
title: t('settings.provider.delete.title'),
|
||||||
content: t('settings.provider.delete.content'),
|
content: t('settings.provider.delete.content'),
|
||||||
okButtonProps: { danger: true },
|
okButtonProps: { danger: true },
|
||||||
okText: t('common.delete'),
|
okText: t('common.delete'),
|
||||||
centered: true,
|
centered: true,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
// 删除provider前先清理其logo
|
// 删除provider前先清理其logo
|
||||||
if (provider.id) {
|
if (provider.id) {
|
||||||
try {
|
try {
|
||||||
deleteLogo(provider.id)
|
await ImageStorage.remove(`provider-${provider.id}`)
|
||||||
} catch (error) {
|
setProviderLogos((prev) => {
|
||||||
logger.error('Failed to delete logo', error as Error)
|
const newLogos = { ...prev }
|
||||||
}
|
delete newLogos[provider.id]
|
||||||
|
return newLogos
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to delete logo', error as Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedProvider(providers.filter((p) => isSystemProvider(p))[0])
|
|
||||||
removeProvider(provider)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const menus = [editMenu, noteMenu, deleteMenu]
|
setSelectedProvider(providers.filter((p) => isSystemProvider(p))[0])
|
||||||
|
removeProvider(provider)
|
||||||
if (providers.filter((p) => p.id === provider.id).length > 1) {
|
}
|
||||||
return menus
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isSystemProvider(provider)) {
|
const menus = [editMenu, noteMenu, deleteMenu]
|
||||||
return [noteMenu]
|
|
||||||
} else if (provider.isSystem) {
|
if (providers.filter((p) => p.id === provider.id).length > 1) {
|
||||||
// 这里是处理数据中存在新版本删掉的系统提供商的情况
|
return menus
|
||||||
// 未来期望能重构一下,不要依赖isSystem字段
|
}
|
||||||
return [noteMenu, deleteMenu]
|
|
||||||
} else {
|
if (isSystemProvider(provider)) {
|
||||||
return menus
|
return [noteMenu]
|
||||||
|
} else if (provider.isSystem) {
|
||||||
|
// 这里是处理数据中存在新版本删掉的系统提供商的情况
|
||||||
|
// 未来期望能重构一下,不要依赖isSystem字段
|
||||||
|
return [noteMenu, deleteMenu]
|
||||||
|
} else {
|
||||||
|
return menus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProviderAvatar = (provider: Provider, size: number = 25) => {
|
||||||
|
// 特殊处理一下svg格式
|
||||||
|
if (isSystemProvider(provider)) {
|
||||||
|
switch (provider.id) {
|
||||||
|
case 'poe':
|
||||||
|
return <PoeLogo fontSize={size} />
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
[providers, deleteLogo, removeProvider, saveLogo, setSelectedProvider, t, updateProvider]
|
|
||||||
)
|
const logoSrc = getProviderLogo(provider.id)
|
||||||
|
if (logoSrc) {
|
||||||
|
return <ProviderLogo draggable="false" shape="circle" src={logoSrc} size={size} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const customLogo = providerLogos[provider.id]
|
||||||
|
if (customLogo) {
|
||||||
|
return <ProviderLogo draggable="false" shape="square" src={customLogo} size={size} />
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate color for custom provider
|
||||||
|
const backgroundColor = generateColorFromChar(provider.name)
|
||||||
|
const color = provider.name ? getForegroundColor(backgroundColor) : 'white'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProviderLogo size={size} shape="square" style={{ backgroundColor, color, minWidth: size }}>
|
||||||
|
{getFirstCharacter(provider.name)}
|
||||||
|
</ProviderLogo>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const filteredProviders = providers.filter((provider) => {
|
const filteredProviders = providers.filter((provider) => {
|
||||||
const keywords = searchText.toLowerCase().split(/\s+/).filter(Boolean)
|
const keywords = searchText.toLowerCase().split(/\s+/).filter(Boolean)
|
||||||
@ -258,31 +336,6 @@ const ProviderList: FC = () => {
|
|||||||
[handleReorder]
|
[handleReorder]
|
||||||
)
|
)
|
||||||
|
|
||||||
const providerRender = useCallback(
|
|
||||||
(provider: Provider) => {
|
|
||||||
return (
|
|
||||||
<Dropdown menu={{ items: getDropdownMenus(provider) }} trigger={['contextMenu']}>
|
|
||||||
<ProviderListItem
|
|
||||||
key={provider.id}
|
|
||||||
className={provider.id === selectedProvider?.id ? 'active' : ''}
|
|
||||||
onClick={() => setSelectedProvider(provider)}>
|
|
||||||
<DragHandle>
|
|
||||||
<GripVertical size={12} />
|
|
||||||
</DragHandle>
|
|
||||||
<ProviderAvatar pid={provider.id} name={provider.name} size={25} />
|
|
||||||
<ProviderItemName className="text-nowrap">{getFancyProviderName(provider)}</ProviderItemName>
|
|
||||||
{provider.enabled && (
|
|
||||||
<Tag color="green" style={{ marginLeft: 'auto', marginRight: 0, borderRadius: 16 }}>
|
|
||||||
ON
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</ProviderListItem>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
[ProviderAvatar, getDropdownMenus, selectedProvider?.id, setSelectedProvider]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="selectable">
|
<Container className="selectable">
|
||||||
<ProviderListContainer>
|
<ProviderListContainer>
|
||||||
@ -320,7 +373,25 @@ const ProviderList: FC = () => {
|
|||||||
paddingRight: 5
|
paddingRight: 5
|
||||||
}}
|
}}
|
||||||
itemContainerStyle={{ paddingBottom: 5 }}>
|
itemContainerStyle={{ paddingBottom: 5 }}>
|
||||||
{providerRender}
|
{(provider) => (
|
||||||
|
<Dropdown menu={{ items: getDropdownMenus(provider) }} trigger={['contextMenu']}>
|
||||||
|
<ProviderListItem
|
||||||
|
key={provider.id}
|
||||||
|
className={provider.id === selectedProvider?.id ? 'active' : ''}
|
||||||
|
onClick={() => setSelectedProvider(provider)}>
|
||||||
|
<DragHandle>
|
||||||
|
<GripVertical size={12} />
|
||||||
|
</DragHandle>
|
||||||
|
{getProviderAvatar(provider)}
|
||||||
|
<ProviderItemName className="text-nowrap">{getFancyProviderName(provider)}</ProviderItemName>
|
||||||
|
{provider.enabled && (
|
||||||
|
<Tag color="green" style={{ marginLeft: 'auto', marginRight: 0, borderRadius: 16 }}>
|
||||||
|
ON
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
</ProviderListItem>
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
</DraggableVirtualList>
|
</DraggableVirtualList>
|
||||||
<AddButtonWrapper>
|
<AddButtonWrapper>
|
||||||
<Button
|
<Button
|
||||||
@ -395,6 +466,10 @@ const DragHandle = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ProviderLogo = styled(Avatar)`
|
||||||
|
border: 0.5px solid var(--color-border);
|
||||||
|
`
|
||||||
|
|
||||||
const ProviderItemName = styled.div`
|
const ProviderItemName = styled.div`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export default class ImageStorage {
|
|||||||
db.settings.update(id, { value })
|
db.settings.update(id, { value })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await db.settings.put({ id, value })
|
await db.settings.add({ id, value })
|
||||||
} else {
|
} else {
|
||||||
// file image
|
// file image
|
||||||
const base64Image = await convertToBase64(value)
|
const base64Image = await convertToBase64(value)
|
||||||
@ -25,7 +25,7 @@ export default class ImageStorage {
|
|||||||
db.settings.update(id, { value: base64Image })
|
db.settings.update(id, { value: base64Image })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await db.settings.put({ id, value: base64Image })
|
await db.settings.add({ id, value: base64Image })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -233,8 +233,7 @@ vi.mock('@renderer/store/llm.ts', () => {
|
|||||||
secretAccessKey: '',
|
secretAccessKey: '',
|
||||||
region: ''
|
region: ''
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
logos: {}
|
|
||||||
} satisfies LlmState
|
} satisfies LlmState
|
||||||
|
|
||||||
const mockReducer = (state = mockInitialState) => {
|
const mockReducer = (state = mockInitialState) => {
|
||||||
|
|||||||
@ -67,7 +67,7 @@ const persistedReducer = persistReducer(
|
|||||||
{
|
{
|
||||||
key: 'cherry-studio',
|
key: 'cherry-studio',
|
||||||
storage,
|
storage,
|
||||||
version: 144,
|
version: 143,
|
||||||
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'],
|
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'],
|
||||||
migrate
|
migrate
|
||||||
},
|
},
|
||||||
|
|||||||
@ -39,7 +39,6 @@ export interface LlmState {
|
|||||||
translateModel: Model
|
translateModel: Model
|
||||||
quickAssistantId: string
|
quickAssistantId: string
|
||||||
settings: LlmSettings
|
settings: LlmSettings
|
||||||
logos: Record<string, string>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialState: LlmState = {
|
export const initialState: LlmState = {
|
||||||
@ -72,8 +71,7 @@ export const initialState: LlmState = {
|
|||||||
secretAccessKey: '',
|
secretAccessKey: '',
|
||||||
region: ''
|
region: ''
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
logos: {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 由于 isLocalAi 目前总是为false,该函数暂未被使用
|
// 由于 isLocalAi 目前总是为false,该函数暂未被使用
|
||||||
@ -221,17 +219,6 @@ const llmSlice = createSlice({
|
|||||||
provider.models[modelIndex] = action.payload.model
|
provider.models[modelIndex] = action.payload.model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
setLogos: (state, action: PayloadAction<Record<string, string>>) => {
|
|
||||||
state.logos = action.payload
|
|
||||||
},
|
|
||||||
setLogo: (state, action: PayloadAction<{ id: string; logo: string }>) => {
|
|
||||||
const { id, logo } = action.payload
|
|
||||||
state.logos[id] = logo
|
|
||||||
},
|
|
||||||
removeLogo: (state, action: PayloadAction<string>) => {
|
|
||||||
const id = action.payload
|
|
||||||
delete state.logos[id]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -257,10 +244,7 @@ export const {
|
|||||||
setAwsBedrockAccessKeyId,
|
setAwsBedrockAccessKeyId,
|
||||||
setAwsBedrockSecretAccessKey,
|
setAwsBedrockSecretAccessKey,
|
||||||
setAwsBedrockRegion,
|
setAwsBedrockRegion,
|
||||||
updateModel,
|
updateModel
|
||||||
setLogos,
|
|
||||||
setLogo,
|
|
||||||
removeLogo
|
|
||||||
} = llmSlice.actions
|
} = llmSlice.actions
|
||||||
|
|
||||||
export default llmSlice.reducer
|
export default llmSlice.reducer
|
||||||
|
|||||||
@ -2322,15 +2322,6 @@ const migrateConfig = {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// after 1.5.8
|
|
||||||
'144': (state: RootState) => {
|
|
||||||
try {
|
|
||||||
state.llm.logos = {}
|
|
||||||
return state
|
|
||||||
} catch (error) {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user