import { LanRounded, SettingsRounded } from '@mui/icons-material' import { MenuItem, Select, TextField, Typography } from '@mui/material' import { useLockFn } from 'ahooks' import { useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { updateGeo, type LogLevel } from 'tauri-plugin-mihomo-api' import { BaseDialog, DialogRef, Switch, TooltipIcon } from '@/components/base' import { useClash } from '@/hooks/use-clash' import { useClashLog } from '@/hooks/use-clash-log' import { useDisplayedMixedPort } from '@/hooks/use-displayed-mixed-port' import { useProfiles } from '@/hooks/use-profiles' import { useVerge } from '@/hooks/use-verge' import { invoke_uwp_tool, setDnsOverride } from '@/services/cmds' import { mutate } from '@/services/mutate' 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, mutateVerge } = useVerge() const { current: currentProfile } = useProfiles() const dnsEnabled = currentProfile ? (verge?.profile_dns_settings?.[currentProfile.uid]?.enabled ?? verge?.enable_dns_settings ?? false) : false const displayedMixedPort = useDisplayedMixedPort() const [, setClashLog] = useClashLog() const { ipv6, 'allow-lan': allowLan, 'log-level': logLevel, 'unified-delay': unifiedDelay, } = clash ?? {} const [dnsConfirmation, setDnsConfirmation] = useState<{ profileUid: string source: string } | null>(null) const [dnsUpdating, setDnsUpdating] = useState(false) const webRef = useRef(null) const portRef = useRef(null) const ctrlRef = useRef(null) const coreRef = useRef(null) const networkRef = useRef(null) const dnsRef = useRef(null) const corsRef = useRef(null) const tunnelRef = useRef(null) const onSwitchFormat = (_e: any, value: boolean) => value const onChangeData = (patch: Partial) => { mutateClash((old) => ({ ...old!, ...patch }), false) } const onUpdateGeo = async () => { try { await mutate(() => updateGeo(), { id: 'update-geo', errorNotice: false, }) showNotice.success('settings.feedback.notifications.clash.geoDataUpdated') } catch (err: any) { showNotice.error(err) } } const handleDnsToggle = useLockFn( async ( enable: boolean, confirmation?: string, profileUid = currentProfile?.uid, ) => { if (!profileUid) return setDnsUpdating(true) try { const result = await mutate( () => setDnsOverride(profileUid, enable, confirmation), { id: 'set-dns-override', errorNotice: false, }, ) if (!result.ok) return const outcome = result.value if (outcome.status === 'confirmation_required') { setDnsConfirmation({ profileUid, source: outcome.source }) } else { setDnsConfirmation(null) } mutateVerge() mutateClash() } catch (err: any) { showNotice.error(err) } finally { setDnsUpdating(false) } }, ) const closeDnsConfirmation = () => { if (!dnsUpdating) setDnsConfirmation(null) } return ( { if (dnsConfirmation) void handleDnsToggle( true, dnsConfirmation.source, dnsConfirmation.profileUid, ) }} > {t('settings.modals.dns.protection.message')} { networkRef.current?.open() }} /> } > onChangeData({ 'allow-lan': e })} onGuard={(e) => patchClash({ 'allow-lan': e })} > dnsRef.current?.open()} /> } > handleDnsToggle(checked)} /> onChangeData({ ipv6: e })} onGuard={(e) => patchClash({ ipv6: e })} > } > onChangeData({ 'unified-delay': e })} onGuard={(e) => patchClash({ 'unified-delay': e })} > } > e.target.value} onChange={(e) => onChangeData({ 'log-level': e })} onGuard={(e) => { setClashLog((pre) => ({ ...pre!, logLevel: e.toUpperCase() as LogLevel, })) return patchClash({ 'log-level': e }) }} > { portRef.current?.open() ;(e.target as HTMLElement).blur() }} /> { e.stopPropagation() corsRef.current?.open() }} /> } onClick={() => { ctrlRef.current?.open() }} /> webRef.current?.open()} label={t('settings.sections.clash.form.fields.webUI')} /> coreRef.current?.open()} /> } > {version} {isWIN && ( } /> )} tunnelRef.current?.open()} /> ) } export default SettingClash