support custom sidebar

This commit is contained in:
pompurin404 2024-09-06 17:44:26 +08:00
parent 9bd4841b6e
commit 79b3da1da6
No known key found for this signature in database
21 changed files with 433 additions and 205 deletions

View File

@ -2,6 +2,12 @@
- 1.2.x YAML覆写语法有所变动请更新后参考文档进行修改 - 1.2.x YAML覆写语法有所变动请更新后参考文档进行修改
### New Features
- 支持自定义侧边栏卡片大小
- 支持隐藏侧边栏卡片
### Bug Fixes ### Bug Fixes
- 修复Ubuntu下每次开启Tun都需要密码的问题 - 修复Ubuntu下每次开启Tun都需要密码的问题
- 修复Sub-Store无法读取剪切板的问题

View File

@ -126,14 +126,14 @@ async function migration(): Promise<void> {
'tun', 'tun',
'profile', 'profile',
'proxy', 'proxy',
'mihomo',
'connection',
'dns',
'sniff',
'log',
'rule', 'rule',
'resource', 'resource',
'override', 'override',
'connection',
'mihomo',
'dns',
'sniff',
'log',
'substore' 'substore'
], ],
useSubStore = true useSubStore = true

View File

@ -18,19 +18,18 @@ export const defaultConfig: IAppConfig = {
controlSniff: true, controlSniff: true,
nameserverPolicy: {}, nameserverPolicy: {},
siderOrder: [ siderOrder: [
'mode',
'sysproxy', 'sysproxy',
'tun', 'tun',
'profile', 'profile',
'proxy', 'proxy',
'mihomo',
'connection',
'dns',
'sniff',
'log',
'rule', 'rule',
'resource', 'resource',
'override', 'override',
'connection',
'mihomo',
'dns',
'sniff',
'log',
'substore' 'substore'
], ],
sysProxy: { enable: false, mode: 'manual' } sysProxy: { enable: false, mode: 'manual' }

View File

@ -37,23 +37,20 @@ const App: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { const {
appTheme = 'system', appTheme = 'system',
controlDns = true,
controlSniff = true,
useSubStore = true,
useWindowFrame = false, useWindowFrame = false,
siderOrder = [ siderOrder = [
'sysproxy', 'sysproxy',
'tun', 'tun',
'profile', 'profile',
'proxy', 'proxy',
'mihomo',
'connection',
'dns',
'sniff',
'log',
'rule', 'rule',
'resource', 'resource',
'override', 'override',
'connection',
'mihomo',
'dns',
'sniff',
'log',
'substore' 'substore'
] ]
} = appConfig || {} } = appConfig || {}
@ -169,9 +166,6 @@ const App: React.FC = () => {
})} })}
> >
{order.map((key: string) => { {order.map((key: string) => {
if (key === 'dns' && controlDns === false) return null
if (key === 'sniff' && controlSniff === false) return null
if (key === 'substore' && useSubStore === false) return null
return componentMap[key] return componentMap[key]
})} })}
</SortableContext> </SortableContext>

View File

@ -0,0 +1,78 @@
import React from 'react'
import SettingCard from '../base/base-setting-card'
import SettingItem from '../base/base-setting-item'
import { RadioGroup, Radio } from '@nextui-org/react'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const titleMap = {
sysproxyCardStatus: '系统代理',
tunCardStatus: '虚拟网卡',
profileCardStatus: '订阅管理',
proxyCardStatus: '代理组',
ruleCardStatus: '规则',
resourceCardStatus: '外部资源',
overrideCardStatus: '覆写',
connectionCardStatus: '连接',
mihomoCoreCardStatus: '内核',
dnsCardStatus: 'DNS',
sniffCardStatus: '域名嗅探',
logCardStatus: '日志',
substoreCardStatus: 'Sub-Store'
}
const SiderConfig: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig()
const {
sysproxyCardStatus = 'col-span-1',
tunCardStatus = 'col-span-1',
profileCardStatus = 'col-span-2',
proxyCardStatus = 'col-span-1',
ruleCardStatus = 'col-span-1',
resourceCardStatus = 'col-span-1',
overrideCardStatus = 'col-span-1',
connectionCardStatus = 'col-span-2',
mihomoCoreCardStatus = 'col-span-2',
dnsCardStatus = 'col-span-1',
sniffCardStatus = 'col-span-1',
logCardStatus = 'col-span-1',
substoreCardStatus = 'col-span-1'
} = appConfig || {}
const cardStatus = {
sysproxyCardStatus,
tunCardStatus,
profileCardStatus,
proxyCardStatus,
ruleCardStatus,
resourceCardStatus,
overrideCardStatus,
connectionCardStatus,
mihomoCoreCardStatus,
dnsCardStatus,
sniffCardStatus,
logCardStatus,
substoreCardStatus
}
return (
<SettingCard>
{Object.keys(cardStatus).map((key, index, array) => {
return (
<SettingItem title={titleMap[key]} key={key} divider={index !== array.length - 1}>
<RadioGroup
orientation="horizontal"
value={cardStatus[key]}
onValueChange={(v) => {
patchAppConfig({ [key]: v as CardStatus })
}}
>
<Radio value="col-span-2"></Radio>
<Radio value="col-span-1"></Radio>
<Radio value="hidden"></Radio>
</RadioGroup>
</SettingItem>
)
})}
</SettingCard>
)
}
export default SiderConfig

View File

@ -20,7 +20,7 @@ let drawing = false
const ConnCard: React.FC = () => { const ConnCard: React.FC = () => {
const { theme = 'system', systemTheme = 'dark' } = useTheme() const { theme = 'system', systemTheme = 'dark' } = useTheme()
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { showTraffic } = appConfig || {} const { showTraffic, connectionCardStatus = 'col-span-2' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/connections') const match = location.pathname.includes('/connections')
@ -172,15 +172,17 @@ const ConnCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-2" className={connectionCardStatus}
> >
{connectionCardStatus === 'col-span-2' ? (
<>
<Card <Card
fullWidth fullWidth
className={`${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/connections')} onPress={() => navigate('/connections')}
> >
<CardBody className="pb-0 pt-0 px-0"> <CardBody className="pb-1 pt-0 px-0">
<div ref={setNodeRef} {...attributes} {...listeners} className="flex justify-between"> <div ref={setNodeRef} {...attributes} {...listeners} className="flex justify-between">
<Button <Button
isIconOnly isIconOnly
@ -206,7 +208,9 @@ const ConnCard: React.FC = () => {
</div> </div>
</CardBody> </CardBody>
<CardFooter className="pt-1"> <CardFooter className="pt-1">
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}></h3> <h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
</h3>
</CardFooter> </CardFooter>
</Card> </Card>
<div className="w-full h-full absolute top-0 left-0 pointer-events-none rounded-[14px] overflow-hidden"> <div className="w-full h-full absolute top-0 left-0 pointer-events-none rounded-[14px] overflow-hidden">
@ -218,6 +222,36 @@ const ConnCard: React.FC = () => {
type="area" type="area"
/> />
</div> </div>
</>
) : (
<Card
fullWidth
className={`${match ? 'bg-primary' : ''}`}
isPressable
onPress={() => navigate('/logs')}
>
<CardBody className="pb-1 pt-0 px-0">
<div ref={setNodeRef} {...attributes} {...listeners} className="flex justify-between">
<Button
isIconOnly
className="bg-transparent pointer-events-none"
variant="flat"
color="default"
>
<IoLink
color="default"
className={`${match ? 'text-white' : 'text-foreground'} text-[24px] font-bold`}
/>
</Button>
</div>
</CardBody>
<CardFooter className="pt-1">
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
</h3>
</CardFooter>
</Card>
)}
</div> </div>
) )
} }

View File

@ -6,7 +6,10 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { patchMihomoConfig } from '@renderer/utils/ipc' import { patchMihomoConfig } from '@renderer/utils/ipc'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const DNSCard: React.FC = () => { const DNSCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { dnsCardStatus = 'col-span-1', controlDns = true } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/dns') const match = location.pathname.includes('/dns')
@ -37,7 +40,7 @@ const DNSCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={`${dnsCardStatus} ${!controlDns ? 'hidden' : ''}`}
> >
<Card <Card
fullWidth fullWidth

View File

@ -3,7 +3,10 @@ import { IoJournalOutline } from 'react-icons/io5'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const LogCard: React.FC = () => { const LogCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { logCardStatus = 'col-span-1' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/logs') const match = location.pathname.includes('/logs')
@ -26,11 +29,11 @@ const LogCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={logCardStatus}
> >
<Card <Card
fullWidth fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/logs')} onPress={() => navigate('/logs')}
> >

View File

@ -8,8 +8,12 @@ import { CSS } from '@dnd-kit/utilities'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import PubSub from 'pubsub-js' import PubSub from 'pubsub-js'
import useSWR from 'swr' import useSWR from 'swr'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import { LuCpu } from 'react-icons/lu'
const MihomoCoreCard: React.FC = () => { const MihomoCoreCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { mihomoCoreCardStatus = 'col-span-2' } = appConfig || {}
const { data: version, mutate } = useSWR('mihomoVersion', mihomoVersion) const { data: version, mutate } = useSWR('mihomoVersion', mihomoVersion)
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
@ -48,8 +52,9 @@ const MihomoCoreCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-2" className={mihomoCoreCardStatus}
> >
{mihomoCoreCardStatus === 'col-span-2' ? (
<Card <Card
fullWidth fullWidth
isPressable isPressable
@ -84,7 +89,9 @@ const MihomoCoreCard: React.FC = () => {
} }
}} }}
> >
<IoMdRefresh className={`${match ? 'text-white' : 'text-foreground'} text-[24px]`} /> <IoMdRefresh
className={`${match ? 'text-white' : 'text-foreground'} text-[24px]`}
/>
</Button> </Button>
</div> </div>
</CardBody> </CardBody>
@ -97,6 +104,35 @@ const MihomoCoreCard: React.FC = () => {
</div> </div>
</CardFooter> </CardFooter>
</Card> </Card>
) : (
<Card
fullWidth
className={`${match ? 'bg-primary' : ''}`}
isPressable
onPress={() => navigate('/mihomo')}
>
<CardBody className="pb-1 pt-0 px-0">
<div ref={setNodeRef} {...attributes} {...listeners} className="flex justify-between">
<Button
isIconOnly
className="bg-transparent pointer-events-none"
variant="flat"
color="default"
>
<LuCpu
color="default"
className={`${match ? 'text-white' : 'text-foreground'} text-[24px] font-bold`}
/>
</Button>
</div>
</CardBody>
<CardFooter className="pt-1">
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
</h3>
</CardFooter>
</Card>
)}
</div> </div>
) )
} }

View File

@ -4,8 +4,11 @@ import { MdFormatOverline } from 'react-icons/md'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const OverrideCard: React.FC = () => { const OverrideCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { overrideCardStatus = 'col-span-1' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/override') const match = location.pathname.includes('/override')
@ -28,11 +31,11 @@ const OverrideCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={overrideCardStatus}
> >
<Card <Card
fullWidth fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/override')} onPress={() => navigate('/override')}
> >

View File

@ -11,11 +11,15 @@ import 'dayjs/locale/zh-cn'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useState } from 'react' import { useState } from 'react'
import ConfigViewer from './config-viewer' import ConfigViewer from './config-viewer'
import { useAppConfig } from '@renderer/hooks/use-app-config'
import { TiFolder } from 'react-icons/ti'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
dayjs.locale('zh-cn') dayjs.locale('zh-cn')
const ProfileCard: React.FC = () => { const ProfileCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { profileCardStatus = 'col-span-2' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/profiles') const match = location.pathname.includes('/profiles')
@ -52,9 +56,10 @@ const ProfileCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-2" className={profileCardStatus}
> >
{showRuntimeConfig && <ConfigViewer onClose={() => setShowRuntimeConfig(false)} />} {showRuntimeConfig && <ConfigViewer onClose={() => setShowRuntimeConfig(false)} />}
{profileCardStatus === 'col-span-2' ? (
<Card <Card
fullWidth fullWidth
className={`${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
@ -144,6 +149,35 @@ const ProfileCard: React.FC = () => {
)} )}
</CardFooter> </CardFooter>
</Card> </Card>
) : (
<Card
fullWidth
className={`${match ? 'bg-primary' : ''}`}
isPressable
onPress={() => navigate('/profiles')}
>
<CardBody className="pb-1 pt-0 px-0">
<div ref={setNodeRef} {...attributes} {...listeners} className="flex justify-between">
<Button
isIconOnly
className="bg-transparent pointer-events-none"
variant="flat"
color="default"
>
<TiFolder
color="default"
className={`${match ? 'text-white' : 'text-foreground'} text-[24px]`}
/>
</Button>
</div>
</CardBody>
<CardFooter className="pt-1">
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
</h3>
</CardFooter>
</Card>
)}
</div> </div>
) )
} }

View File

@ -4,8 +4,11 @@ import { CSS } from '@dnd-kit/utilities'
import { LuGroup } from 'react-icons/lu' import { LuGroup } from 'react-icons/lu'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useGroups } from '@renderer/hooks/use-groups' import { useGroups } from '@renderer/hooks/use-groups'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const ProxyCard: React.FC = () => { const ProxyCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { proxyCardStatus = 'col-span-1' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/proxies') const match = location.pathname.includes('/proxies')
@ -30,7 +33,7 @@ const ProxyCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-2" className={proxyCardStatus}
> >
<Card <Card
fullWidth fullWidth

View File

@ -4,7 +4,10 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { IoLayersOutline } from 'react-icons/io5' import { IoLayersOutline } from 'react-icons/io5'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const ResourceCard: React.FC = () => { const ResourceCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { resourceCardStatus = 'col-span-1' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/resources') const match = location.pathname.includes('/resources')
@ -27,11 +30,11 @@ const ResourceCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={resourceCardStatus}
> >
<Card <Card
fullWidth fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/resources')} onPress={() => navigate('/resources')}
> >

View File

@ -4,8 +4,11 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { useRules } from '@renderer/hooks/use-rules' import { useRules } from '@renderer/hooks/use-rules'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const RuleCard: React.FC = () => { const RuleCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { ruleCardStatus = 'col-span-1' } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/rules') const match = location.pathname.includes('/rules')
@ -29,11 +32,11 @@ const RuleCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={ruleCardStatus}
> >
<Card <Card
fullWidth fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/rules')} onPress={() => navigate('/rules')}
> >

View File

@ -6,8 +6,11 @@ import { patchMihomoConfig } from '@renderer/utils/ipc'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const SniffCard: React.FC = () => { const SniffCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { sniffCardStatus = 'col-span-1', controlSniff = true } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/sniffer') const match = location.pathname.includes('/sniffer')
@ -38,11 +41,11 @@ const SniffCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={`${sniffCardStatus} ${!controlSniff ? 'hidden' : ''}`}
> >
<Card <Card
fullWidth fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/sniffer')} onPress={() => navigate('/sniffer')}
> >

View File

@ -3,7 +3,10 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import SubStoreIcon from '../base/substore-icon' import SubStoreIcon from '../base/substore-icon'
import { useAppConfig } from '@renderer/hooks/use-app-config'
const SubStoreCard: React.FC = () => { const SubStoreCard: React.FC = () => {
const { appConfig } = useAppConfig()
const { substoreCardStatus = 'col-span-1', useSubStore = true } = appConfig || {}
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/substore') const match = location.pathname.includes('/substore')
@ -26,11 +29,11 @@ const SubStoreCard: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={`${substoreCardStatus} ${!useSubStore ? 'hidden' : ''}`}
> >
<Card <Card
fullWidth fullWidth
className={`col-span-1 ${match ? 'bg-primary' : ''}`} className={`${match ? 'bg-primary' : ''}`}
isPressable isPressable
onPress={() => navigate('/substore')} onPress={() => navigate('/substore')}
> >

View File

@ -13,7 +13,7 @@ const SysproxySwitcher: React.FC = () => {
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/sysproxy') const match = location.pathname.includes('/sysproxy')
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { sysProxy } = appConfig || {} const { sysProxy, sysproxyCardStatus = 'col-span-1' } = appConfig || {}
const { enable } = sysProxy || {} const { enable } = sysProxy || {}
const { const {
attributes, attributes,
@ -44,7 +44,7 @@ const SysproxySwitcher: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1 " className={sysproxyCardStatus}
> >
<Card <Card
fullWidth fullWidth

View File

@ -17,6 +17,7 @@ const TunSwitcher: React.FC = () => {
const match = location.pathname.includes('/tun') || false const match = location.pathname.includes('/tun') || false
const [openPasswordModal, setOpenPasswordModal] = useState(false) const [openPasswordModal, setOpenPasswordModal] = useState(false)
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { tunCardStatus = 'col-span-1' } = appConfig || {}
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { tun } = controledMihomoConfig || {} const { tun } = controledMihomoConfig || {}
const { enable } = tun || {} const { enable } = tun || {}
@ -62,7 +63,7 @@ const TunSwitcher: React.FC = () => {
transition, transition,
zIndex: isDragging ? 'calc(infinity)' : undefined zIndex: isDragging ? 'calc(infinity)' : undefined
}} }}
className="col-span-1" className={tunCardStatus}
> >
{openPasswordModal && ( {openPasswordModal && (
<BasePasswordModal <BasePasswordModal

View File

@ -96,7 +96,13 @@ const Connections: React.FC = () => {
{calcTraffic(connectionsInfo?.downloadTotal ?? 0)}{' '} {calcTraffic(connectionsInfo?.downloadTotal ?? 0)}{' '}
</span> </span>
</div> </div>
<Badge color="primary" variant="flat" content={`${filteredConnections.length}`}> <Badge
className="mt-2"
color="primary"
variant="flat"
showOutline={false}
content={`${filteredConnections.length}`}
>
<Button <Button
className="app-nodrag ml-1" className="app-nodrag ml-1"
title="关闭全部连接" title="关闭全部连接"

View File

@ -8,6 +8,7 @@ import MihomoConfig from '@renderer/components/settings/mihomo-config'
import Actions from '@renderer/components/settings/actions' import Actions from '@renderer/components/settings/actions'
import ShortcutConfig from '@renderer/components/settings/shortcut-config' import ShortcutConfig from '@renderer/components/settings/shortcut-config'
import { FaTelegramPlane } from 'react-icons/fa' import { FaTelegramPlane } from 'react-icons/fa'
import SiderConfig from '@renderer/components/settings/sider-config'
const Settings: React.FC = () => { const Settings: React.FC = () => {
return ( return (
@ -55,6 +56,7 @@ const Settings: React.FC = () => {
} }
> >
<GeneralConfig /> <GeneralConfig />
<SiderConfig />
<WebdavConfig /> <WebdavConfig />
<MihomoConfig /> <MihomoConfig />
<ShortcutConfig /> <ShortcutConfig />

14
src/shared/types.d.ts vendored
View File

@ -1,6 +1,7 @@
type OutboundMode = 'rule' | 'global' | 'direct' type OutboundMode = 'rule' | 'global' | 'direct'
type LogLevel = 'info' | 'debug' | 'warning' | 'error' | 'silent' type LogLevel = 'info' | 'debug' | 'warning' | 'error' | 'silent'
type SysProxyMode = 'auto' | 'manual' type SysProxyMode = 'auto' | 'manual'
type CardStatus = 'col-span-2' | 'col-span-1' | 'hidden'
type AppTheme = type AppTheme =
| 'system' | 'system'
| 'light' | 'light'
@ -219,6 +220,19 @@ interface IAppConfig {
proxyCols: 'auto' | '1' | '2' | '3' | '4' proxyCols: 'auto' | '1' | '2' | '3' | '4'
connectionDirection: 'asc' | 'desc' connectionDirection: 'asc' | 'desc'
connectionOrderBy: 'time' | 'upload' | 'download' | 'uploadSpeed' | 'downloadSpeed' connectionOrderBy: 'time' | 'upload' | 'download' | 'uploadSpeed' | 'downloadSpeed'
connectionCardStatus?: CardStatus
dnsCardStatus?: CardStatus
logCardStatus?: CardStatus
mihomoCoreCardStatus?: CardStatus
overrideCardStatus?: CardStatus
profileCardStatus?: CardStatus
proxyCardStatus?: CardStatus
resourceCardStatus?: CardStatus
ruleCardStatus?: CardStatus
sniffCardStatus?: CardStatus
substoreCardStatus?: CardStatus
sysproxyCardStatus?: CardStatus
tunCardStatus?: CardStatus
useSubStore: boolean useSubStore: boolean
useCustomSubStore?: boolean useCustomSubStore?: boolean
customSubStoreUrl?: string customSubStoreUrl?: string