support narrow sidebar style

This commit is contained in:
pompurin404 2024-10-19 12:13:20 +08:00
parent a5f825d5ca
commit f193dbe696
No known key found for this signature in database
14 changed files with 464 additions and 80 deletions

View File

@ -37,6 +37,7 @@ import { driver } from 'driver.js'
import 'driver.js/dist/driver.css' import 'driver.js/dist/driver.css'
let navigate: NavigateFunction let navigate: NavigateFunction
const narrowWidth = platform === 'darwin' ? 70 : 60
const App: React.FC = () => { const App: React.FC = () => {
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
@ -142,19 +143,19 @@ const App: React.FC = () => {
} }
const componentMap = { const componentMap = {
sysproxy: <SysproxySwitcher key="sysproxy" />, sysproxy: SysproxySwitcher,
tun: <TunSwitcher key="tun" />, tun: TunSwitcher,
profile: <ProfileCard key="profile" />, profile: ProfileCard,
proxy: <ProxyCard key="proxy" />, proxy: ProxyCard,
mihomo: <MihomoCoreCard key="mihomo" />, mihomo: MihomoCoreCard,
connection: <ConnCard key="connection" />, connection: ConnCard,
dns: <DNSCard key="dns" />, dns: DNSCard,
sniff: <SniffCard key="sniff" />, sniff: SniffCard,
log: <LogCard key="log" />, log: LogCard,
rule: <RuleCard key="rule" />, rule: RuleCard,
resource: <ResourceCard key="resource" />, resource: ResourceCard,
override: <OverrideCard key="override" />, override: OverrideCard,
substore: <SubStoreCard key="substore" /> substore: SubStoreCard
} }
return ( return (
@ -167,7 +168,9 @@ const App: React.FC = () => {
}} }}
onMouseMove={(e) => { onMouseMove={(e) => {
if (!resizing) return if (!resizing) return
if (e.clientX <= 250) { if (e.clientX <= 150) {
setSiderWidthValue(narrowWidth)
} else if (e.clientX <= 250) {
setSiderWidthValue(250) setSiderWidthValue(250)
} else if (e.clientX >= 400) { } else if (e.clientX >= 400) {
setSiderWidthValue(400) setSiderWidthValue(400)
@ -177,6 +180,38 @@ const App: React.FC = () => {
}} }}
className={`w-full h-[100vh] flex ${resizing ? 'cursor-ew-resize' : ''}`} className={`w-full h-[100vh] flex ${resizing ? 'cursor-ew-resize' : ''}`}
> >
{siderWidthValue === narrowWidth ? (
<div style={{ width: `${narrowWidth}px` }} className="side h-full">
<div className="app-drag flex justify-center items-center z-40 bg-transparent h-[49px]">
{platform !== 'darwin' && (
<MihomoIcon className="h-[32px] leading-[32px] text-lg mx-[1px]" />
)}
</div>
<div className="h-[calc(100%-110px)] overflow-y-auto no-scrollbar">
<div className="h-full w-full flex flex-col gap-2">
{order.map((key: string) => {
const Component = componentMap[key]
if (!Component) return null
return <Component key={key} iconOnly={true} />
})}
</div>
</div>
<div className="mt-2 flex justify-center items-center h-[48px]">
<Button
size="sm"
className="app-nodrag"
isIconOnly
color={location.pathname.includes('/settings') ? 'primary' : 'default'}
variant={location.pathname.includes('/settings') ? 'solid' : 'light'}
onPress={() => {
navigate('/settings')
}}
>
<IoSettings className="text-[20px]" />
</Button>
</div>
</div>
) : (
<div <div
style={{ width: `${siderWidthValue}px` }} style={{ width: `${siderWidthValue}px` }}
className="side h-full overflow-y-auto no-scrollbar" className="side h-full overflow-y-auto no-scrollbar"
@ -199,8 +234,9 @@ const App: React.FC = () => {
onPress={() => { onPress={() => {
navigate('/settings') navigate('/settings')
}} }}
startContent={<IoSettings className="text-[20px]" />} >
/> <IoSettings className="text-[20px]" />
</Button>
</div> </div>
</div> </div>
<div className="mt-2 mx-2"> <div className="mt-2 mx-2">
@ -210,12 +246,16 @@ const App: React.FC = () => {
<div className="grid grid-cols-2 gap-2 m-2"> <div className="grid grid-cols-2 gap-2 m-2">
<SortableContext items={order}> <SortableContext items={order}>
{order.map((key: string) => { {order.map((key: string) => {
return componentMap[key] const Component = componentMap[key]
if (!Component) return null
return <Component key={key} />
})} })}
</SortableContext> </SortableContext>
</div> </div>
</DndContext> </DndContext>
</div> </div>
)}
<div <div
onMouseDown={() => { onMouseDown={() => {
setResizing(true) setResizing(true)

View File

@ -1,8 +1,8 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import { FaCircleArrowDown, FaCircleArrowUp } from 'react-icons/fa6' import { FaCircleArrowDown, FaCircleArrowUp } from 'react-icons/fa6'
import { useLocation } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { calcTraffic } from '@renderer/utils/calc' import { calcTraffic } from '@renderer/utils/calc'
import { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
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 { IoLink } from 'react-icons/io5' import { IoLink } from 'react-icons/io5'
@ -16,11 +16,16 @@ let currentDownload: number | undefined = undefined
let hasShowTraffic = false let hasShowTraffic = false
let drawing = false let drawing = false
const ConnCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const ConnCard: React.FC<Props> = (props) => {
const { theme = 'system', systemTheme = 'dark' } = useTheme() const { theme = 'system', systemTheme = 'dark' } = useTheme()
const { iconOnly } = props
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { showTraffic = false, connectionCardStatus = 'col-span-2', customTheme } = appConfig || {} const { showTraffic = false, connectionCardStatus = 'col-span-2', customTheme } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/connections') const match = location.pathname.includes('/connections')
const [upload, setUpload] = useState(0) const [upload, setUpload] = useState(0)
@ -87,6 +92,26 @@ const ConnCard: React.FC = () => {
} }
}, [showTraffic]) }, [showTraffic])
if (iconOnly) {
return (
<div className={`${connectionCardStatus} flex justify-center`}>
<Tooltip content="连接" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/connections')
}}
>
<IoLink className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,16 +1,23 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { LuServer } from 'react-icons/lu' import { LuServer } from 'react-icons/lu'
import { useLocation } from 'react-router-dom' 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' import { useAppConfig } from '@renderer/hooks/use-app-config'
const DNSCard: React.FC = () => { import React from 'react'
interface Props {
iconOnly?: boolean
}
const DNSCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { dnsCardStatus = 'col-span-1', controlDns = true } = appConfig || {} const { dnsCardStatus = 'col-span-1', controlDns = true } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/dns') const match = location.pathname.includes('/dns')
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { dns, tun } = controledMihomoConfig || {} const { dns, tun } = controledMihomoConfig || {}
@ -31,6 +38,26 @@ const DNSCard: React.FC = () => {
await patchMihomoConfig({ dns: { enable } }) await patchMihomoConfig({ dns: { enable } })
} }
if (iconOnly) {
return (
<div className={`${dnsCardStatus} ${!controlDns ? 'hidden' : ''} flex justify-center`}>
<Tooltip content="DNS" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/dns')
}}
>
<LuServer className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,13 +1,21 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import { IoJournalOutline } from 'react-icons/io5' import { IoJournalOutline } from 'react-icons/io5'
import { useLocation } 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' import { useAppConfig } from '@renderer/hooks/use-app-config'
const LogCard: React.FC = () => { import React from 'react'
interface Props {
iconOnly?: boolean
}
const LogCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { logCardStatus = 'col-span-1' } = appConfig || {} const { logCardStatus = 'col-span-1' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/logs') const match = location.pathname.includes('/logs')
const { const {
attributes, attributes,
@ -20,6 +28,26 @@ const LogCard: React.FC = () => {
id: 'log' id: 'log'
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
if (iconOnly) {
return (
<div className={`${logCardStatus} flex justify-center`}>
<Tooltip content="日志" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/logs')
}}
>
<IoJournalOutline className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,21 +1,27 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import { calcTraffic } from '@renderer/utils/calc' import { calcTraffic } from '@renderer/utils/calc'
import { mihomoVersion, restartCore } from '@renderer/utils/ipc' import { mihomoVersion, restartCore } from '@renderer/utils/ipc'
import { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { IoMdRefresh } from 'react-icons/io' import { IoMdRefresh } from 'react-icons/io'
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 { useLocation } 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 { useAppConfig } from '@renderer/hooks/use-app-config'
import { LuCpu } from 'react-icons/lu' import { LuCpu } from 'react-icons/lu'
const MihomoCoreCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const MihomoCoreCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { mihomoCoreCardStatus = 'col-span-2' } = appConfig || {} const { mihomoCoreCardStatus = 'col-span-2' } = appConfig || {}
const { data: version, mutate } = useSWR('mihomoVersion', mihomoVersion) const { data: version, mutate } = useSWR('mihomoVersion', mihomoVersion)
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/mihomo') const match = location.pathname.includes('/mihomo')
const { const {
attributes, attributes,
@ -43,6 +49,26 @@ const MihomoCoreCard: React.FC = () => {
} }
}, []) }, [])
if (iconOnly) {
return (
<div className={`${mihomoCoreCardStatus} flex justify-center`}>
<Tooltip content="内核设置" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/mihomo')
}}
>
<LuCpu className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,15 +1,21 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import React from 'react' import React from 'react'
import { MdFormatOverline } from 'react-icons/md' import { MdFormatOverline } from 'react-icons/md'
import { useLocation } 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' import { useAppConfig } from '@renderer/hooks/use-app-config'
const OverrideCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const OverrideCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { overrideCardStatus = 'col-span-1' } = appConfig || {} const { overrideCardStatus = 'col-span-1' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/override') const match = location.pathname.includes('/override')
const { const {
attributes, attributes,
@ -22,6 +28,25 @@ const OverrideCard: React.FC = () => {
id: 'override' id: 'override'
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
if (iconOnly) {
return (
<div className={`${overrideCardStatus} flex justify-center`}>
<Tooltip content="覆写" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/override')
}}
>
<MdFormatOverline className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,6 +1,6 @@
import { Button, Card, CardBody, CardFooter, Chip, Progress, Tooltip } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Chip, Progress, Tooltip } from '@nextui-org/react'
import { useProfileConfig } from '@renderer/hooks/use-profile-config' import { useProfileConfig } from '@renderer/hooks/use-profile-config'
import { useLocation } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { calcTraffic, calcPercent } from '@renderer/utils/calc' import { calcTraffic, calcPercent } from '@renderer/utils/calc'
import { CgLoadbarDoc } from 'react-icons/cg' import { CgLoadbarDoc } from 'react-icons/cg'
import { IoMdRefresh } from 'react-icons/io' import { IoMdRefresh } from 'react-icons/io'
@ -9,7 +9,7 @@ import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useState } from 'react' import React, { useState } from 'react'
import ConfigViewer from './config-viewer' import ConfigViewer from './config-viewer'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { TiFolder } from 'react-icons/ti' import { TiFolder } from 'react-icons/ti'
@ -17,10 +17,16 @@ import { TiFolder } from 'react-icons/ti'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
dayjs.locale('zh-cn') dayjs.locale('zh-cn')
const ProfileCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const ProfileCard: React.FC<Props> = (props) => {
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { iconOnly } = props
const { profileCardStatus = 'col-span-2', profileDisplayDate = 'expire' } = appConfig || {} const { profileCardStatus = 'col-span-2', profileDisplayDate = 'expire' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/profiles') const match = location.pathname.includes('/profiles')
const [updating, setUpdating] = useState(false) const [updating, setUpdating] = useState(false)
const [showRuntimeConfig, setShowRuntimeConfig] = useState(false) const [showRuntimeConfig, setShowRuntimeConfig] = useState(false)
@ -47,6 +53,26 @@ const ProfileCard: React.FC = () => {
const usage = (extra?.upload ?? 0) + (extra?.download ?? 0) const usage = (extra?.upload ?? 0) + (extra?.download ?? 0)
const total = extra?.total ?? 0 const total = extra?.total ?? 0
if (iconOnly) {
return (
<div className={`${profileCardStatus} flex justify-center`}>
<Tooltip content="订阅管理" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/profiles')
}}
>
<TiFolder className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,15 +1,22 @@
import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Chip, Tooltip } from '@nextui-org/react'
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 { LuGroup } from 'react-icons/lu' import { LuGroup } from 'react-icons/lu'
import { useLocation } 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' import { useAppConfig } from '@renderer/hooks/use-app-config'
import React from 'react'
const ProxyCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const ProxyCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { proxyCardStatus = 'col-span-1' } = appConfig || {} const { proxyCardStatus = 'col-span-1' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/proxies') const match = location.pathname.includes('/proxies')
const { groups = [] } = useGroups() const { groups = [] } = useGroups()
const { const {
@ -24,6 +31,25 @@ const ProxyCard: React.FC = () => {
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
if (iconOnly) {
return (
<div className={`${proxyCardStatus} flex justify-center`}>
<Tooltip content="代理组" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/proxies')
}}
>
<LuGroup className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,14 +1,21 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import React from 'react' import React from 'react'
import { useLocation } 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 { IoLayersOutline } from 'react-icons/io5' import { IoLayersOutline } from 'react-icons/io5'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
const ResourceCard: React.FC = () => {
interface Props {
iconOnly?: boolean
}
const ResourceCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { resourceCardStatus = 'col-span-1' } = appConfig || {} const { resourceCardStatus = 'col-span-1' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/resources') const match = location.pathname.includes('/resources')
const { const {
attributes, attributes,
@ -21,6 +28,26 @@ const ResourceCard: React.FC = () => {
id: 'resource' id: 'resource'
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
if (iconOnly) {
return (
<div className={`${resourceCardStatus} flex justify-center`}>
<Tooltip content="外部资源" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/resources')
}}
>
<IoLayersOutline className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,15 +1,22 @@
import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Chip, Tooltip } from '@nextui-org/react'
import { MdOutlineAltRoute } from 'react-icons/md' import { MdOutlineAltRoute } from 'react-icons/md'
import { useLocation } 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 { useRules } from '@renderer/hooks/use-rules' import { useRules } from '@renderer/hooks/use-rules'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import React from 'react'
const RuleCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const RuleCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { ruleCardStatus = 'col-span-1' } = appConfig || {} const { ruleCardStatus = 'col-span-1' } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/rules') const match = location.pathname.includes('/rules')
const { rules } = useRules() const { rules } = useRules()
const { const {
@ -23,6 +30,26 @@ const RuleCard: React.FC = () => {
id: 'rule' id: 'rule'
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
if (iconOnly) {
return (
<div className={`${ruleCardStatus} flex justify-center`}>
<Tooltip content="规则" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/rules')
}}
>
<MdOutlineAltRoute className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,17 +1,23 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { RiScan2Fill } from 'react-icons/ri' import { RiScan2Fill } from 'react-icons/ri'
import { useLocation } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { patchMihomoConfig } from '@renderer/utils/ipc' 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' import { useAppConfig } from '@renderer/hooks/use-app-config'
import React from 'react'
const SniffCard: React.FC = () => { interface Props {
iconOnly?: boolean
}
const SniffCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { sniffCardStatus = 'col-span-1', controlSniff = true } = appConfig || {} const { sniffCardStatus = 'col-span-1', controlSniff = true } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/sniffer') const match = location.pathname.includes('/sniffer')
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig() const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig()
const { sniffer } = controledMihomoConfig || {} const { sniffer } = controledMihomoConfig || {}
@ -32,6 +38,26 @@ const SniffCard: React.FC = () => {
await patchMihomoConfig({ sniffer: { enable } }) await patchMihomoConfig({ sniffer: { enable } })
} }
if (iconOnly) {
return (
<div className={`${sniffCardStatus} ${!controlSniff ? 'hidden' : ''} flex justify-center`}>
<Tooltip content="域名嗅探" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/sniffer')
}}
>
<RiScan2Fill className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,13 +1,21 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import { useLocation } 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 SubStoreIcon from '../base/substore-icon' import SubStoreIcon from '../base/substore-icon'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
const SubStoreCard: React.FC = () => { import React from 'react'
interface Props {
iconOnly?: boolean
}
const SubStoreCard: React.FC<Props> = (props) => {
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { iconOnly } = props
const { substoreCardStatus = 'col-span-1', useSubStore = true } = appConfig || {} const { substoreCardStatus = 'col-span-1', useSubStore = true } = appConfig || {}
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/substore') const match = location.pathname.includes('/substore')
const { const {
attributes, attributes,
@ -20,6 +28,27 @@ const SubStoreCard: React.FC = () => {
id: 'substore' id: 'substore'
}) })
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
if (iconOnly) {
return (
<div className={`${substoreCardStatus} flex justify-center`}>
<Tooltip content="Sub-Store" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/substore')
}}
>
<SubStoreIcon className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,6 +1,6 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { useLocation } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
import { triggerSysProxy } from '@renderer/utils/ipc' import { triggerSysProxy } from '@renderer/utils/ipc'
import { AiOutlineGlobal } from 'react-icons/ai' import { AiOutlineGlobal } from 'react-icons/ai'
@ -8,8 +8,14 @@ import React from 'react'
import { useSortable } from '@dnd-kit/sortable' import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities' import { CSS } from '@dnd-kit/utilities'
const SysproxySwitcher: React.FC = () => { interface Props {
iconOnly?: boolean
}
const SysproxySwitcher: React.FC<Props> = (props) => {
const { iconOnly } = props
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/sysproxy') const match = location.pathname.includes('/sysproxy')
const { appConfig, patchAppConfig } = useAppConfig() const { appConfig, patchAppConfig } = useAppConfig()
const { sysProxy, sysproxyCardStatus = 'col-span-1' } = appConfig || {} const { sysProxy, sysproxyCardStatus = 'col-span-1' } = appConfig || {}
@ -36,6 +42,26 @@ const SysproxySwitcher: React.FC = () => {
} }
} }
if (iconOnly) {
return (
<div className={`${sysproxyCardStatus} flex justify-center`}>
<Tooltip content="系统代理" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/sysproxy')
}}
>
<AiOutlineGlobal className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{

View File

@ -1,16 +1,22 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import { Button, Card, CardBody, CardFooter, Tooltip } from '@nextui-org/react'
import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config' import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-config'
import BorderSwitch from '@renderer/components/base/border-swtich' import BorderSwitch from '@renderer/components/base/border-swtich'
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb' import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
import { useLocation } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { restartCore } from '@renderer/utils/ipc' import { restartCore } 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 React from 'react' import React from 'react'
import { useAppConfig } from '@renderer/hooks/use-app-config' import { useAppConfig } from '@renderer/hooks/use-app-config'
const TunSwitcher: React.FC = () => { interface Props {
iconOnly?: boolean
}
const TunSwitcher: React.FC<Props> = (props) => {
const { iconOnly } = props
const location = useLocation() const location = useLocation()
const navigate = useNavigate()
const match = location.pathname.includes('/tun') || false const match = location.pathname.includes('/tun') || false
const { appConfig } = useAppConfig() const { appConfig } = useAppConfig()
const { tunCardStatus = 'col-span-1' } = appConfig || {} const { tunCardStatus = 'col-span-1' } = appConfig || {}
@ -39,6 +45,26 @@ const TunSwitcher: React.FC = () => {
window.electron.ipcRenderer.send('updateTrayMenu') window.electron.ipcRenderer.send('updateTrayMenu')
} }
if (iconOnly) {
return (
<div className={`${tunCardStatus} flex justify-center`}>
<Tooltip content="虚拟网卡" placement="right">
<Button
size="sm"
isIconOnly
color={match ? 'primary' : 'default'}
variant={match ? 'solid' : 'light'}
onPress={() => {
navigate('/tun')
}}
>
<TbDeviceIpadHorizontalBolt className="text-[20px]" />
</Button>
</Tooltip>
</div>
)
}
return ( return (
<div <div
style={{ style={{