mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-16 11:42:19 +08:00
support custom sidebar
This commit is contained in:
parent
9bd4841b6e
commit
79b3da1da6
|
@ -2,6 +2,12 @@
|
|||
|
||||
- 1.2.x YAML覆写语法有所变动,请更新后参考文档进行修改
|
||||
|
||||
### New Features
|
||||
|
||||
- 支持自定义侧边栏卡片大小
|
||||
- 支持隐藏侧边栏卡片
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- 修复Ubuntu下每次开启Tun都需要密码的问题
|
||||
- 修复Sub-Store无法读取剪切板的问题
|
||||
|
|
|
@ -126,14 +126,14 @@ async function migration(): Promise<void> {
|
|||
'tun',
|
||||
'profile',
|
||||
'proxy',
|
||||
'mihomo',
|
||||
'connection',
|
||||
'dns',
|
||||
'sniff',
|
||||
'log',
|
||||
'rule',
|
||||
'resource',
|
||||
'override',
|
||||
'connection',
|
||||
'mihomo',
|
||||
'dns',
|
||||
'sniff',
|
||||
'log',
|
||||
'substore'
|
||||
],
|
||||
useSubStore = true
|
||||
|
|
|
@ -18,19 +18,18 @@ export const defaultConfig: IAppConfig = {
|
|||
controlSniff: true,
|
||||
nameserverPolicy: {},
|
||||
siderOrder: [
|
||||
'mode',
|
||||
'sysproxy',
|
||||
'tun',
|
||||
'profile',
|
||||
'proxy',
|
||||
'mihomo',
|
||||
'connection',
|
||||
'dns',
|
||||
'sniff',
|
||||
'log',
|
||||
'rule',
|
||||
'resource',
|
||||
'override',
|
||||
'connection',
|
||||
'mihomo',
|
||||
'dns',
|
||||
'sniff',
|
||||
'log',
|
||||
'substore'
|
||||
],
|
||||
sysProxy: { enable: false, mode: 'manual' }
|
||||
|
|
|
@ -37,23 +37,20 @@ const App: React.FC = () => {
|
|||
const { appConfig, patchAppConfig } = useAppConfig()
|
||||
const {
|
||||
appTheme = 'system',
|
||||
controlDns = true,
|
||||
controlSniff = true,
|
||||
useSubStore = true,
|
||||
useWindowFrame = false,
|
||||
siderOrder = [
|
||||
'sysproxy',
|
||||
'tun',
|
||||
'profile',
|
||||
'proxy',
|
||||
'mihomo',
|
||||
'connection',
|
||||
'dns',
|
||||
'sniff',
|
||||
'log',
|
||||
'rule',
|
||||
'resource',
|
||||
'override',
|
||||
'connection',
|
||||
'mihomo',
|
||||
'dns',
|
||||
'sniff',
|
||||
'log',
|
||||
'substore'
|
||||
]
|
||||
} = appConfig || {}
|
||||
|
@ -169,9 +166,6 @@ const App: React.FC = () => {
|
|||
})}
|
||||
>
|
||||
{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]
|
||||
})}
|
||||
</SortableContext>
|
||||
|
|
78
src/renderer/src/components/settings/sider-config.tsx
Normal file
78
src/renderer/src/components/settings/sider-config.tsx
Normal 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
|
|
@ -20,7 +20,7 @@ let drawing = false
|
|||
const ConnCard: React.FC = () => {
|
||||
const { theme = 'system', systemTheme = 'dark' } = useTheme()
|
||||
const { appConfig } = useAppConfig()
|
||||
const { showTraffic } = appConfig || {}
|
||||
const { showTraffic, connectionCardStatus = 'col-span-2' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/connections')
|
||||
|
@ -172,52 +172,86 @@ const ConnCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-2"
|
||||
className={connectionCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/connections')}
|
||||
>
|
||||
<CardBody className="pb-0 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]`}
|
||||
/>
|
||||
</Button>
|
||||
<div className={`p-2 w-full ${match ? 'text-white' : 'text-foreground'} `}>
|
||||
<div className="flex justify-between">
|
||||
<div className="w-full text-right mr-2">{calcTraffic(upload)}/s</div>
|
||||
<FaCircleArrowUp className="h-[24px] leading-[24px]" />
|
||||
{connectionCardStatus === 'col-span-2' ? (
|
||||
<>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/connections')}
|
||||
>
|
||||
<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]`}
|
||||
/>
|
||||
</Button>
|
||||
<div className={`p-2 w-full ${match ? 'text-white' : 'text-foreground'} `}>
|
||||
<div className="flex justify-between">
|
||||
<div className="w-full text-right mr-2">{calcTraffic(upload)}/s</div>
|
||||
<FaCircleArrowUp className="h-[24px] leading-[24px]" />
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="w-full text-right mr-2">{calcTraffic(download)}/s</div>
|
||||
<FaCircleArrowDown className="h-[24px] leading-[24px]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<div className="w-full text-right mr-2">{calcTraffic(download)}/s</div>
|
||||
<FaCircleArrowDown className="h-[24px] leading-[24px]" />
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
|
||||
连接
|
||||
</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<div className="w-full h-full absolute top-0 left-0 pointer-events-none rounded-[14px] overflow-hidden">
|
||||
<Chart
|
||||
options={getApexChartOptions()}
|
||||
series={[{ name: 'Total', data: series }]}
|
||||
height={'100%'}
|
||||
width={'100%'}
|
||||
type="area"
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>连接</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<div className="w-full h-full absolute top-0 left-0 pointer-events-none rounded-[14px] overflow-hidden">
|
||||
<Chart
|
||||
options={getApexChartOptions()}
|
||||
series={[{ name: 'Total', data: series }]}
|
||||
height={'100%'}
|
||||
width={'100%'}
|
||||
type="area"
|
||||
/>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
|||
import { patchMihomoConfig } from '@renderer/utils/ipc'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
const DNSCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { dnsCardStatus = 'col-span-1', controlDns = true } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/dns')
|
||||
|
@ -37,7 +40,7 @@ const DNSCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={`${dnsCardStatus} ${!controlDns ? 'hidden' : ''}`}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
|
|
|
@ -3,7 +3,10 @@ import { IoJournalOutline } from 'react-icons/io5'
|
|||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
const LogCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { logCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/logs')
|
||||
|
@ -26,11 +29,11 @@ const LogCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={logCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/logs')}
|
||||
>
|
||||
|
|
|
@ -8,8 +8,12 @@ import { CSS } from '@dnd-kit/utilities'
|
|||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import PubSub from 'pubsub-js'
|
||||
import useSWR from 'swr'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
import { LuCpu } from 'react-icons/lu'
|
||||
|
||||
const MihomoCoreCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { mihomoCoreCardStatus = 'col-span-2' } = appConfig || {}
|
||||
const { data: version, mutate } = useSWR('mihomoVersion', mihomoVersion)
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
@ -48,55 +52,87 @@ const MihomoCoreCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-2"
|
||||
className={mihomoCoreCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
isPressable
|
||||
onPress={() => navigate('/mihomo')}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
>
|
||||
<CardBody>
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="flex justify-between h-[32px]"
|
||||
>
|
||||
<h3
|
||||
className={`text-md font-bold leading-[32px] ${match ? 'text-white' : 'text-foreground'} `}
|
||||
{mihomoCoreCardStatus === 'col-span-2' ? (
|
||||
<Card
|
||||
fullWidth
|
||||
isPressable
|
||||
onPress={() => navigate('/mihomo')}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
>
|
||||
<CardBody>
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="flex justify-between h-[32px]"
|
||||
>
|
||||
{version?.version ?? '-'}
|
||||
</h3>
|
||||
<h3
|
||||
className={`text-md font-bold leading-[32px] ${match ? 'text-white' : 'text-foreground'} `}
|
||||
>
|
||||
{version?.version ?? '-'}
|
||||
</h3>
|
||||
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
variant="light"
|
||||
color="default"
|
||||
onPress={async () => {
|
||||
try {
|
||||
await restartCore()
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
} finally {
|
||||
mutate()
|
||||
}
|
||||
}}
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
variant="light"
|
||||
color="default"
|
||||
onPress={async () => {
|
||||
try {
|
||||
await restartCore()
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
} finally {
|
||||
mutate()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IoMdRefresh
|
||||
className={`${match ? 'text-white' : 'text-foreground'} text-[24px]`}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<div
|
||||
className={`flex justify-between w-full text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}
|
||||
>
|
||||
<IoMdRefresh className={`${match ? 'text-white' : 'text-foreground'} text-[24px]`} />
|
||||
</Button>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<div
|
||||
className={`flex justify-between w-full text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}
|
||||
>
|
||||
<h4>内核设置</h4>
|
||||
<h4>{calcTraffic(mem)}</h4>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<h4>内核设置</h4>
|
||||
<h4>{calcTraffic(mem)}</h4>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@ import { MdFormatOverline } from 'react-icons/md'
|
|||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
|
||||
const OverrideCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { overrideCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/override')
|
||||
|
@ -28,11 +31,11 @@ const OverrideCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={overrideCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/override')}
|
||||
>
|
||||
|
|
|
@ -11,11 +11,15 @@ import 'dayjs/locale/zh-cn'
|
|||
import dayjs from 'dayjs'
|
||||
import { useState } from 'react'
|
||||
import ConfigViewer from './config-viewer'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
import { TiFolder } from 'react-icons/ti'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
dayjs.locale('zh-cn')
|
||||
|
||||
const ProfileCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { profileCardStatus = 'col-span-2' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/profiles')
|
||||
|
@ -52,98 +56,128 @@ const ProfileCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-2"
|
||||
className={profileCardStatus}
|
||||
>
|
||||
{showRuntimeConfig && <ConfigViewer onClose={() => setShowRuntimeConfig(false)} />}
|
||||
<Card
|
||||
fullWidth
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/profiles')}
|
||||
>
|
||||
<CardBody className="pb-1">
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="flex justify-between h-[32px]"
|
||||
>
|
||||
<h3
|
||||
title={info?.name}
|
||||
className={`text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px] ${match ? 'text-white' : 'text-foreground'} `}
|
||||
{profileCardStatus === 'col-span-2' ? (
|
||||
<Card
|
||||
fullWidth
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/profiles')}
|
||||
>
|
||||
<CardBody className="pb-1">
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
className="flex justify-between h-[32px]"
|
||||
>
|
||||
{info?.name}
|
||||
</h3>
|
||||
<div className="flex">
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
title="查看当前运行时配置"
|
||||
variant="light"
|
||||
color="default"
|
||||
onPress={() => {
|
||||
setShowRuntimeConfig(true)
|
||||
}}
|
||||
<h3
|
||||
title={info?.name}
|
||||
className={`text-ellipsis whitespace-nowrap overflow-hidden text-md font-bold leading-[32px] ${match ? 'text-white' : 'text-foreground'} `}
|
||||
>
|
||||
<CgLoadbarDoc
|
||||
className={`text-[24px] ${match ? 'text-white' : 'text-foreground'}`}
|
||||
/>
|
||||
</Button>
|
||||
{info.type === 'remote' && (
|
||||
{info?.name}
|
||||
</h3>
|
||||
<div className="flex">
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
title={dayjs(info.updated).fromNow()}
|
||||
disabled={updating}
|
||||
title="查看当前运行时配置"
|
||||
variant="light"
|
||||
color="default"
|
||||
onPress={async () => {
|
||||
setUpdating(true)
|
||||
await addProfileItem(info)
|
||||
setUpdating(false)
|
||||
onPress={() => {
|
||||
setShowRuntimeConfig(true)
|
||||
}}
|
||||
>
|
||||
<IoMdRefresh
|
||||
className={`text-[24px] ${match ? 'text-white' : 'text-foreground'} ${updating ? 'animate-spin' : ''}`}
|
||||
<CgLoadbarDoc
|
||||
className={`text-[24px] ${match ? 'text-white' : 'text-foreground'}`}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
{info.type === 'remote' && (
|
||||
<Button
|
||||
isIconOnly
|
||||
size="sm"
|
||||
title={dayjs(info.updated).fromNow()}
|
||||
disabled={updating}
|
||||
variant="light"
|
||||
color="default"
|
||||
onPress={async () => {
|
||||
setUpdating(true)
|
||||
await addProfileItem(info)
|
||||
setUpdating(false)
|
||||
}}
|
||||
>
|
||||
<IoMdRefresh
|
||||
className={`text-[24px] ${match ? 'text-white' : 'text-foreground'} ${updating ? 'animate-spin' : ''}`}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{info.type === 'remote' && extra && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${match ? 'text-white' : 'text-foreground'} `}
|
||||
>
|
||||
<small>{`${calcTraffic(usage)}/${calcTraffic(total)}`}</small>
|
||||
<small>
|
||||
{extra.expire ? dayjs.unix(extra.expire).format('YYYY-MM-DD') : '长期有效'}
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
{info.type === 'local' && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${match ? 'text-white' : 'text-foreground'}`}
|
||||
>
|
||||
<Chip
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className={`${match ? 'text-white border-white' : 'border-primary text-primary'}`}
|
||||
{info.type === 'remote' && extra && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${match ? 'text-white' : 'text-foreground'} `}
|
||||
>
|
||||
本地
|
||||
</Chip>
|
||||
<small>{`${calcTraffic(usage)}/${calcTraffic(total)}`}</small>
|
||||
<small>
|
||||
{extra.expire ? dayjs.unix(extra.expire).format('YYYY-MM-DD') : '长期有效'}
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
{info.type === 'local' && (
|
||||
<div
|
||||
className={`mt-2 flex justify-between ${match ? 'text-white' : 'text-foreground'}`}
|
||||
>
|
||||
<Chip
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className={`${match ? 'text-white border-white' : 'border-primary text-primary'}`}
|
||||
>
|
||||
本地
|
||||
</Chip>
|
||||
</div>
|
||||
)}
|
||||
</CardBody>
|
||||
<CardFooter className="pt-0">
|
||||
{extra && (
|
||||
<Progress
|
||||
className="w-full"
|
||||
classNames={{ indicator: match ? 'bg-white' : 'bg-foreground' }}
|
||||
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
|
||||
/>
|
||||
)}
|
||||
</CardFooter>
|
||||
</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-0">
|
||||
{extra && (
|
||||
<Progress
|
||||
className="w-full"
|
||||
classNames={{ indicator: match ? 'bg-white' : 'bg-foreground' }}
|
||||
value={calcPercent(extra?.upload, extra?.download, extra?.total)}
|
||||
/>
|
||||
)}
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className={`text-md font-bold ${match ? 'text-white' : 'text-foreground'}`}>
|
||||
订阅管理
|
||||
</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,11 @@ import { CSS } from '@dnd-kit/utilities'
|
|||
import { LuGroup } from 'react-icons/lu'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { useGroups } from '@renderer/hooks/use-groups'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
|
||||
const ProxyCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { proxyCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/proxies')
|
||||
|
@ -30,7 +33,7 @@ const ProxyCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-2"
|
||||
className={proxyCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
|
|
|
@ -4,7 +4,10 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
|||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { IoLayersOutline } from 'react-icons/io5'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
const ResourceCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { resourceCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/resources')
|
||||
|
@ -27,11 +30,11 @@ const ResourceCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={resourceCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/resources')}
|
||||
>
|
||||
|
|
|
@ -4,8 +4,11 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
|||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { useRules } from '@renderer/hooks/use-rules'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
|
||||
const RuleCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { ruleCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/rules')
|
||||
|
@ -29,11 +32,11 @@ const RuleCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={ruleCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/rules')}
|
||||
>
|
||||
|
|
|
@ -6,8 +6,11 @@ import { patchMihomoConfig } from '@renderer/utils/ipc'
|
|||
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
|
||||
const SniffCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { sniffCardStatus = 'col-span-1', controlSniff = true } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/sniffer')
|
||||
|
@ -38,11 +41,11 @@ const SniffCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={`${sniffCardStatus} ${!controlSniff ? 'hidden' : ''}`}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/sniffer')}
|
||||
>
|
||||
|
|
|
@ -3,7 +3,10 @@ import { useLocation, useNavigate } from 'react-router-dom'
|
|||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import SubStoreIcon from '../base/substore-icon'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
const SubStoreCard: React.FC = () => {
|
||||
const { appConfig } = useAppConfig()
|
||||
const { substoreCardStatus = 'col-span-1', useSubStore = true } = appConfig || {}
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/substore')
|
||||
|
@ -26,11 +29,11 @@ const SubStoreCard: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={`${substoreCardStatus} ${!useSubStore ? 'hidden' : ''}`}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
className={`col-span-1 ${match ? 'bg-primary' : ''}`}
|
||||
className={`${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/substore')}
|
||||
>
|
||||
|
|
|
@ -13,7 +13,7 @@ const SysproxySwitcher: React.FC = () => {
|
|||
const location = useLocation()
|
||||
const match = location.pathname.includes('/sysproxy')
|
||||
const { appConfig, patchAppConfig } = useAppConfig()
|
||||
const { sysProxy } = appConfig || {}
|
||||
const { sysProxy, sysproxyCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const { enable } = sysProxy || {}
|
||||
const {
|
||||
attributes,
|
||||
|
@ -44,7 +44,7 @@ const SysproxySwitcher: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1 "
|
||||
className={sysproxyCardStatus}
|
||||
>
|
||||
<Card
|
||||
fullWidth
|
||||
|
|
|
@ -17,6 +17,7 @@ const TunSwitcher: React.FC = () => {
|
|||
const match = location.pathname.includes('/tun') || false
|
||||
const [openPasswordModal, setOpenPasswordModal] = useState(false)
|
||||
const { appConfig, patchAppConfig } = useAppConfig()
|
||||
const { tunCardStatus = 'col-span-1' } = appConfig || {}
|
||||
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
|
||||
const { tun } = controledMihomoConfig || {}
|
||||
const { enable } = tun || {}
|
||||
|
@ -62,7 +63,7 @@ const TunSwitcher: React.FC = () => {
|
|||
transition,
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
className="col-span-1"
|
||||
className={tunCardStatus}
|
||||
>
|
||||
{openPasswordModal && (
|
||||
<BasePasswordModal
|
||||
|
|
|
@ -96,7 +96,13 @@ const Connections: React.FC = () => {
|
|||
↓ {calcTraffic(connectionsInfo?.downloadTotal ?? 0)}{' '}
|
||||
</span>
|
||||
</div>
|
||||
<Badge color="primary" variant="flat" content={`${filteredConnections.length}`}>
|
||||
<Badge
|
||||
className="mt-2"
|
||||
color="primary"
|
||||
variant="flat"
|
||||
showOutline={false}
|
||||
content={`${filteredConnections.length}`}
|
||||
>
|
||||
<Button
|
||||
className="app-nodrag ml-1"
|
||||
title="关闭全部连接"
|
||||
|
|
|
@ -8,6 +8,7 @@ import MihomoConfig from '@renderer/components/settings/mihomo-config'
|
|||
import Actions from '@renderer/components/settings/actions'
|
||||
import ShortcutConfig from '@renderer/components/settings/shortcut-config'
|
||||
import { FaTelegramPlane } from 'react-icons/fa'
|
||||
import SiderConfig from '@renderer/components/settings/sider-config'
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
return (
|
||||
|
@ -55,6 +56,7 @@ const Settings: React.FC = () => {
|
|||
}
|
||||
>
|
||||
<GeneralConfig />
|
||||
<SiderConfig />
|
||||
<WebdavConfig />
|
||||
<MihomoConfig />
|
||||
<ShortcutConfig />
|
||||
|
|
14
src/shared/types.d.ts
vendored
14
src/shared/types.d.ts
vendored
|
@ -1,6 +1,7 @@
|
|||
type OutboundMode = 'rule' | 'global' | 'direct'
|
||||
type LogLevel = 'info' | 'debug' | 'warning' | 'error' | 'silent'
|
||||
type SysProxyMode = 'auto' | 'manual'
|
||||
type CardStatus = 'col-span-2' | 'col-span-1' | 'hidden'
|
||||
type AppTheme =
|
||||
| 'system'
|
||||
| 'light'
|
||||
|
@ -219,6 +220,19 @@ interface IAppConfig {
|
|||
proxyCols: 'auto' | '1' | '2' | '3' | '4'
|
||||
connectionDirection: 'asc' | 'desc'
|
||||
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
|
||||
useCustomSubStore?: boolean
|
||||
customSubStoreUrl?: string
|
||||
|
|
Loading…
Reference in New Issue
Block a user