mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-28 13:31:32 +08:00
feat(ProviderSettings): resizable provider settings (#9004)
* feat(ProviderSettings): 添加Splitter组件实现左右面板布局
移除固定的最小宽度限制,使用Splitter组件实现可调整的左右面板布局
* style(ProviderSetting): 优化提供商名称显示样式
将 ProviderName 组件从 span 改为 Typography.Text 以支持文本溢出省略
添加 flex 布局属性确保标题区域正确布局
* feat(ProviderSetting): 添加中间省略文本组件并优化HostPreview显示
在ProviderSetting页面中引入EllipsisMiddle组件,用于处理长文本的中间省略显示
重构hostPreview为HostPreview组件,使用EllipsisMiddle优化长URL的展示效果
* fix(ProviderSettings): 修复Splitter.Panel默认大小未设置问题
* Revert "feat(ProviderSetting): 添加中间省略文本组件并优化HostPreview显示"
This reverts commit bfbba8f5db.
* refactor: improve splitter style
* refactor: improve dragger divider size
---------
Co-authored-by: one <wangan.cs@gmail.com>
This commit is contained in:
parent
46c247149e
commit
5713a278cd
@ -184,3 +184,26 @@
|
||||
box-shadow: 0 1px 4px 0px rgb(128 128 128 / 50%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-splitter-bar {
|
||||
.ant-splitter-bar-dragger {
|
||||
&::before {
|
||||
background-color: var(--color-border) !important;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
}
|
||||
&:hover {
|
||||
&::before {
|
||||
width: 4px !important;
|
||||
background-color: var(--color-primary) !important;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-splitter-bar-dragger-active {
|
||||
&::before {
|
||||
width: 4px !important;
|
||||
background-color: var(--color-primary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,6 +99,11 @@ const AntdProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
},
|
||||
Divider: {
|
||||
colorSplit: 'rgba(128,128,128,0.15)'
|
||||
},
|
||||
Splitter: {
|
||||
splitBarDraggableSize: 0,
|
||||
splitBarSize: 0.5,
|
||||
splitTriggerSize: 10
|
||||
}
|
||||
},
|
||||
token: {
|
||||
|
||||
@ -13,7 +13,7 @@ import { isProviderSupportAuth } from '@renderer/services/ProviderService'
|
||||
import { ApiKeyConnectivity, HealthStatus } from '@renderer/types/healthCheck'
|
||||
import { formatApiHost, formatApiKeys, getFancyProviderName, isOpenAIProvider } from '@renderer/utils'
|
||||
import { formatErrorMessage } from '@renderer/utils/error'
|
||||
import { Button, Divider, Flex, Input, Space, Switch, Tooltip } from 'antd'
|
||||
import { Button, Divider, Flex, Input, Space, Switch, Tooltip, Typography } from 'antd'
|
||||
import Link from 'antd/es/typography/Link'
|
||||
import { debounce, isEmpty } from 'lodash'
|
||||
import { Check, Settings2, SquareArrowOutUpRight, TriangleAlert } from 'lucide-react'
|
||||
@ -229,8 +229,8 @@ const ProviderSetting: FC<Props> = ({ providerId }) => {
|
||||
return (
|
||||
<SettingContainer theme={theme} style={{ background: 'var(--color-background)' }}>
|
||||
<SettingTitle>
|
||||
<Flex align="center" gap={5}>
|
||||
<ProviderName>{fancyProviderName}</ProviderName>
|
||||
<Flex align="center" gap={5} flex={1} style={{ overflow: 'hidden' }}>
|
||||
<ProviderName ellipsis>{fancyProviderName}</ProviderName>
|
||||
{officialWebsite && (
|
||||
<Link target="_blank" href={providerConfig.websites.official} style={{ display: 'flex' }}>
|
||||
<Button type="text" size="small" icon={<SquareArrowOutUpRight size={14} />} />
|
||||
@ -372,7 +372,7 @@ const ProviderSetting: FC<Props> = ({ providerId }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const ProviderName = styled.span`
|
||||
const ProviderName = styled(Typography.Text)`
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-right: -2px;
|
||||
|
||||
@ -15,7 +15,7 @@ import {
|
||||
matchKeywordsInProvider,
|
||||
uuid
|
||||
} from '@renderer/utils'
|
||||
import { Avatar, Button, Card, Dropdown, Input, MenuProps, Tag } from 'antd'
|
||||
import { Avatar, Button, Card, Dropdown, Input, MenuProps, Splitter, Tag } from 'antd'
|
||||
import { Eye, EyeOff, GripVertical, PlusIcon, Search, UserPen } from 'lucide-react'
|
||||
import { FC, startTransition, useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -455,70 +455,77 @@ const ProvidersList: FC = () => {
|
||||
|
||||
return (
|
||||
<Container className="selectable">
|
||||
<ProviderListContainer>
|
||||
<AddButtonWrapper>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t('settings.provider.search')}
|
||||
value={searchText}
|
||||
style={{ borderRadius: 'var(--list-item-border-radius)', height: 35 }}
|
||||
suffix={<Search size={14} />}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape') {
|
||||
setSearchText('')
|
||||
}
|
||||
}}
|
||||
allowClear
|
||||
disabled={dragging}
|
||||
/>
|
||||
</AddButtonWrapper>
|
||||
<DraggableVirtualList
|
||||
list={filteredProviders}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
estimateSize={useCallback(() => 40, [])}
|
||||
itemKey={itemKey}
|
||||
overscan={3}
|
||||
style={{
|
||||
height: `calc(100% - 2 * ${BUTTON_WRAPPER_HEIGHT}px)`
|
||||
}}
|
||||
scrollerStyle={{
|
||||
padding: 8,
|
||||
paddingRight: 5
|
||||
}}
|
||||
itemContainerStyle={{ paddingBottom: 5 }}>
|
||||
{(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>
|
||||
<AddButtonWrapper>
|
||||
<Button
|
||||
style={{ width: '100%', borderRadius: 'var(--list-item-border-radius)' }}
|
||||
icon={<PlusIcon size={16} />}
|
||||
onClick={onAddProvider}
|
||||
disabled={dragging}>
|
||||
{t('button.add')}
|
||||
</Button>
|
||||
</AddButtonWrapper>
|
||||
</ProviderListContainer>
|
||||
<ProviderSetting providerId={selectedProvider.id} key={selectedProvider.id} />
|
||||
<Splitter>
|
||||
<Splitter.Panel min={250} defaultSize={250}>
|
||||
<ProviderListContainer>
|
||||
<AddButtonWrapper>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={t('settings.provider.search')}
|
||||
value={searchText}
|
||||
style={{ borderRadius: 'var(--list-item-border-radius)', height: 35 }}
|
||||
suffix={<Search size={14} />}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Escape') {
|
||||
setSearchText('')
|
||||
}
|
||||
}}
|
||||
allowClear
|
||||
disabled={dragging}
|
||||
/>
|
||||
</AddButtonWrapper>
|
||||
<DraggableVirtualList
|
||||
list={filteredProviders}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
estimateSize={useCallback(() => 40, [])}
|
||||
itemKey={itemKey}
|
||||
overscan={3}
|
||||
style={{
|
||||
height: `calc(100% - 2 * ${BUTTON_WRAPPER_HEIGHT}px)`
|
||||
}}
|
||||
scrollerStyle={{
|
||||
padding: 8,
|
||||
paddingRight: 5
|
||||
}}
|
||||
itemContainerStyle={{ paddingBottom: 5 }}>
|
||||
{(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>
|
||||
<AddButtonWrapper>
|
||||
<Button
|
||||
style={{ width: '100%', borderRadius: 'var(--list-item-border-radius)' }}
|
||||
icon={<PlusIcon size={16} />}
|
||||
onClick={onAddProvider}
|
||||
disabled={dragging}>
|
||||
{t('button.add')}
|
||||
</Button>
|
||||
</AddButtonWrapper>
|
||||
</ProviderListContainer>
|
||||
</Splitter.Panel>
|
||||
|
||||
<Splitter.Panel min={'50%'}>
|
||||
<ProviderSetting providerId={selectedProvider.id} key={selectedProvider.id} />
|
||||
</Splitter.Panel>
|
||||
</Splitter>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@ -533,9 +540,7 @@ const Container = styled.div`
|
||||
const ProviderListContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: calc(var(--settings-width) + 10px);
|
||||
height: calc(100vh - var(--navbar-height));
|
||||
border-right: 0.5px solid var(--color-border);
|
||||
`
|
||||
|
||||
const ProviderListItem = styled.div`
|
||||
|
||||
Loading…
Reference in New Issue
Block a user