mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-25 05:02:07 +08:00
* chore: update biome config and ignore zed settings dir * chore: remove trailing slash from .vfox directory in .gitignore
296 lines
9.2 KiB
TypeScript
296 lines
9.2 KiB
TypeScript
import { LanRounded, SettingsRounded } from '@mui/icons-material'
|
|
import { MenuItem, Select, TextField, Typography } from '@mui/material'
|
|
import { invoke } from '@tauri-apps/api/core'
|
|
import { useLockFn } from 'ahooks'
|
|
import { useRef, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { updateGeo, type LogLevel } from 'tauri-plugin-mihomo-api'
|
|
|
|
import { DialogRef, Switch, TooltipIcon } from '@/components/base'
|
|
import { useClash } from '@/hooks/use-clash'
|
|
import { useClashLog } from '@/hooks/use-clash-log'
|
|
import { useVerge } from '@/hooks/use-verge'
|
|
import { invoke_uwp_tool } from '@/services/cmds'
|
|
import { showNotice } from '@/services/notice-service'
|
|
import getSystem from '@/utils/get-system'
|
|
|
|
import { ClashCoreViewer } from './mods/clash-core-viewer'
|
|
import { ClashPortViewer } from './mods/clash-port-viewer'
|
|
import { ControllerViewer } from './mods/controller-viewer'
|
|
import { DnsViewer } from './mods/dns-viewer'
|
|
import { HeaderConfiguration } from './mods/external-controller-cors'
|
|
import { GuardState } from './mods/guard-state'
|
|
import { NetworkInterfaceViewer } from './mods/network-interface-viewer'
|
|
import { SettingItem, SettingList } from './mods/setting-comp'
|
|
import { TunnelsViewer } from './mods/tunnels-viewer'
|
|
import { WebUIViewer } from './mods/web-ui-viewer'
|
|
|
|
const isWIN = getSystem() === 'windows'
|
|
|
|
interface Props {
|
|
onError: (err: Error) => void
|
|
}
|
|
|
|
const SettingClash = ({ onError }: Props) => {
|
|
const { t } = useTranslation()
|
|
|
|
const { clash, version, mutateClash, patchClash } = useClash()
|
|
const { verge, patchVerge } = useVerge()
|
|
const [, setClashLog] = useClashLog()
|
|
|
|
const {
|
|
ipv6,
|
|
'allow-lan': allowLan,
|
|
'log-level': logLevel,
|
|
'unified-delay': unifiedDelay,
|
|
} = clash ?? {}
|
|
|
|
const { verge_mixed_port } = verge ?? {}
|
|
|
|
// 独立跟踪DNS设置开关状态
|
|
const [dnsSettingsEnabled, setDnsSettingsEnabled] = useState(() => {
|
|
return verge?.enable_dns_settings ?? false
|
|
})
|
|
|
|
const webRef = useRef<DialogRef>(null)
|
|
const portRef = useRef<DialogRef>(null)
|
|
const ctrlRef = useRef<DialogRef>(null)
|
|
const coreRef = useRef<DialogRef>(null)
|
|
const networkRef = useRef<DialogRef>(null)
|
|
const dnsRef = useRef<DialogRef>(null)
|
|
const corsRef = useRef<DialogRef>(null)
|
|
const tunnelRef = useRef<DialogRef>(null)
|
|
|
|
const onSwitchFormat = (_e: any, value: boolean) => value
|
|
const onChangeData = (patch: Partial<IConfigData>) => {
|
|
mutateClash((old) => ({ ...old!, ...patch }), false)
|
|
}
|
|
const onUpdateGeo = async () => {
|
|
try {
|
|
await updateGeo()
|
|
showNotice.success('settings.feedback.notifications.clash.geoDataUpdated')
|
|
} catch (err: any) {
|
|
showNotice.error(err)
|
|
}
|
|
}
|
|
|
|
// 实现DNS设置开关处理函数
|
|
const handleDnsToggle = useLockFn(async (enable: boolean) => {
|
|
try {
|
|
setDnsSettingsEnabled(enable)
|
|
await patchVerge({ enable_dns_settings: enable })
|
|
await invoke('apply_dns_config', { apply: enable })
|
|
setTimeout(() => {
|
|
mutateClash()
|
|
}, 500)
|
|
} catch (err: any) {
|
|
setDnsSettingsEnabled(!enable)
|
|
showNotice.error(err)
|
|
await patchVerge({ enable_dns_settings: !enable }).catch(() => {})
|
|
throw err
|
|
}
|
|
})
|
|
|
|
return (
|
|
<SettingList title={t('settings.sections.clash.title')}>
|
|
<WebUIViewer ref={webRef} />
|
|
<ClashPortViewer ref={portRef} />
|
|
<ControllerViewer ref={ctrlRef} />
|
|
<ClashCoreViewer ref={coreRef} />
|
|
<NetworkInterfaceViewer ref={networkRef} />
|
|
<DnsViewer ref={dnsRef} />
|
|
<HeaderConfiguration ref={corsRef} />
|
|
<TunnelsViewer ref={tunnelRef} />
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.allowLan')}
|
|
extra={
|
|
<TooltipIcon
|
|
title={t('settings.sections.clash.form.tooltips.networkInterface')}
|
|
color={'inherit'}
|
|
icon={LanRounded}
|
|
onClick={() => {
|
|
networkRef.current?.open()
|
|
}}
|
|
/>
|
|
}
|
|
>
|
|
<GuardState
|
|
value={allowLan ?? false}
|
|
valueProps="checked"
|
|
onCatch={onError}
|
|
onFormat={onSwitchFormat}
|
|
onChange={(e) => onChangeData({ 'allow-lan': e })}
|
|
onGuard={(e) => patchClash({ 'allow-lan': e })}
|
|
>
|
|
<Switch edge="end" />
|
|
</GuardState>
|
|
</SettingItem>
|
|
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.dnsOverwrite')}
|
|
extra={
|
|
<TooltipIcon
|
|
icon={SettingsRounded}
|
|
onClick={() => dnsRef.current?.open()}
|
|
/>
|
|
}
|
|
>
|
|
<Switch
|
|
edge="end"
|
|
checked={dnsSettingsEnabled}
|
|
onChange={(_, checked) => handleDnsToggle(checked)}
|
|
/>
|
|
</SettingItem>
|
|
|
|
<SettingItem label={t('settings.sections.clash.form.fields.ipv6')}>
|
|
<GuardState
|
|
value={ipv6 ?? false}
|
|
valueProps="checked"
|
|
onCatch={onError}
|
|
onFormat={onSwitchFormat}
|
|
onChange={(e) => onChangeData({ ipv6: e })}
|
|
onGuard={(e) => patchClash({ ipv6: e })}
|
|
>
|
|
<Switch edge="end" />
|
|
</GuardState>
|
|
</SettingItem>
|
|
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.unifiedDelay')}
|
|
extra={
|
|
<TooltipIcon
|
|
title={t('settings.sections.clash.form.tooltips.unifiedDelay')}
|
|
sx={{ opacity: '0.7' }}
|
|
/>
|
|
}
|
|
>
|
|
<GuardState
|
|
value={unifiedDelay ?? false}
|
|
valueProps="checked"
|
|
onCatch={onError}
|
|
onFormat={onSwitchFormat}
|
|
onChange={(e) => onChangeData({ 'unified-delay': e })}
|
|
onGuard={(e) => patchClash({ 'unified-delay': e })}
|
|
>
|
|
<Switch edge="end" />
|
|
</GuardState>
|
|
</SettingItem>
|
|
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.logLevel')}
|
|
extra={
|
|
<TooltipIcon
|
|
title={t('settings.sections.clash.form.tooltips.logLevel')}
|
|
sx={{ opacity: '0.7' }}
|
|
/>
|
|
}
|
|
>
|
|
<GuardState
|
|
value={logLevel === 'warn' ? 'warning' : (logLevel ?? 'info')}
|
|
onCatch={onError}
|
|
onFormat={(e: any) => e.target.value}
|
|
onChange={(e) => onChangeData({ 'log-level': e })}
|
|
onGuard={(e) => {
|
|
setClashLog((pre) => ({
|
|
...pre!,
|
|
logLevel: e.toUpperCase() as LogLevel,
|
|
}))
|
|
return patchClash({ 'log-level': e })
|
|
}}
|
|
>
|
|
<Select size="small" sx={{ width: 100, '> div': { py: '7.5px' } }}>
|
|
<MenuItem value="debug">
|
|
{t('settings.sections.clash.form.options.logLevel.debug')}
|
|
</MenuItem>
|
|
<MenuItem value="info">
|
|
{t('settings.sections.clash.form.options.logLevel.info')}
|
|
</MenuItem>
|
|
<MenuItem value="warning">
|
|
{t('settings.sections.clash.form.options.logLevel.warning')}
|
|
</MenuItem>
|
|
<MenuItem value="error">
|
|
{t('settings.sections.clash.form.options.logLevel.error')}
|
|
</MenuItem>
|
|
<MenuItem value="silent">
|
|
{t('settings.sections.clash.form.options.logLevel.silent')}
|
|
</MenuItem>
|
|
</Select>
|
|
</GuardState>
|
|
</SettingItem>
|
|
|
|
<SettingItem label={t('settings.sections.clash.form.fields.portConfig')}>
|
|
<TextField
|
|
autoComplete="new-password"
|
|
disabled={false}
|
|
size="small"
|
|
value={verge_mixed_port ?? 7897}
|
|
sx={{ width: 100, input: { py: '7.5px', cursor: 'pointer' } }}
|
|
onClick={(e) => {
|
|
portRef.current?.open()
|
|
;(e.target as HTMLElement).blur()
|
|
}}
|
|
/>
|
|
</SettingItem>
|
|
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.external')}
|
|
extra={
|
|
<TooltipIcon
|
|
title={t('settings.sections.externalCors.tooltips.open')}
|
|
icon={SettingsRounded}
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
corsRef.current?.open()
|
|
}}
|
|
/>
|
|
}
|
|
onClick={() => {
|
|
ctrlRef.current?.open()
|
|
}}
|
|
/>
|
|
|
|
<SettingItem
|
|
onClick={() => webRef.current?.open()}
|
|
label={t('settings.sections.clash.form.fields.webUI')}
|
|
/>
|
|
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.clashCore')}
|
|
extra={
|
|
<TooltipIcon
|
|
icon={SettingsRounded}
|
|
onClick={() => coreRef.current?.open()}
|
|
/>
|
|
}
|
|
>
|
|
<Typography sx={{ py: '7px', pr: 1 }}>{version}</Typography>
|
|
</SettingItem>
|
|
|
|
{isWIN && (
|
|
<SettingItem
|
|
onClick={invoke_uwp_tool}
|
|
label={t('settings.sections.clash.form.fields.openUwpTool')}
|
|
extra={
|
|
<TooltipIcon
|
|
title={t('settings.sections.clash.form.tooltips.openUwpTool')}
|
|
sx={{ opacity: '0.7' }}
|
|
/>
|
|
}
|
|
/>
|
|
)}
|
|
|
|
<SettingItem
|
|
onClick={onUpdateGeo}
|
|
label={t('settings.sections.clash.form.fields.updateGeoData')}
|
|
/>
|
|
|
|
<SettingItem
|
|
label={t('settings.sections.clash.form.fields.tunnels.title')}
|
|
onClick={() => tunnelRef.current?.open()}
|
|
/>
|
|
</SettingList>
|
|
)
|
|
}
|
|
|
|
export default SettingClash
|