import { DirectionsRounded, LanguageRounded, MultipleStopRounded, } from '@mui/icons-material' import { Box, Paper, Stack, Typography } from '@mui/material' import { useLockFn } from 'ahooks' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { closeAllConnections } from 'tauri-plugin-mihomo-api' import { useVerge } from '@/hooks/use-verge' import { useAppData } from '@/providers/app-data-context' import { patchClashMode } from '@/services/cmds' import type { TranslationKey } from '@/types/generated/i18n-keys' const CLASH_MODES = ['rule', 'global', 'direct'] as const type ClashMode = (typeof CLASH_MODES)[number] const isClashMode = (mode: string): mode is ClashMode => (CLASH_MODES as readonly string[]).includes(mode) const MODE_META: Record< ClashMode, { label: TranslationKey; description: TranslationKey } > = { rule: { label: 'home.components.clashMode.labels.rule', description: 'home.components.clashMode.descriptions.rule', }, global: { label: 'home.components.clashMode.labels.global', description: 'home.components.clashMode.descriptions.global', }, direct: { label: 'home.components.clashMode.labels.direct', description: 'home.components.clashMode.descriptions.direct', }, } export const ClashModeCard = () => { const { t } = useTranslation() const { verge } = useVerge() const { clashConfig, refreshClashConfig } = useAppData() // 支持的模式列表 const modeList = CLASH_MODES // 直接使用API返回的模式,不维护本地状态 const currentMode = clashConfig?.mode?.toLowerCase() const currentModeKey = typeof currentMode === 'string' && isClashMode(currentMode) ? currentMode : undefined const modeDescription = useMemo(() => { if (currentModeKey) { return t(MODE_META[currentModeKey].description) } return t('home.components.clashMode.errors.communication') }, [currentModeKey, t]) // 模式图标映射 const modeIcons = useMemo( () => ({ rule: , global: , direct: , }), [], ) // 切换模式的处理函数 const onChangeMode = useLockFn(async (mode: ClashMode) => { if (mode === currentModeKey) return if (verge?.auto_close_connection) { closeAllConnections() } try { await patchClashMode(mode) // 使用共享的刷新方法 refreshClashConfig() } catch (error) { console.error('Failed to change mode:', error) } }) // 按钮样式 const buttonStyles = (mode: ClashMode) => ({ cursor: 'pointer', px: 2, py: 1.2, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 1, bgcolor: mode === currentModeKey ? 'primary.main' : 'background.paper', color: mode === currentModeKey ? 'primary.contrastText' : 'text.primary', borderRadius: 1.5, transition: 'all 0.2s ease-in-out', position: 'relative', overflow: 'visible', '&:hover': { transform: 'translateY(-1px)', boxShadow: 1, }, '&:active': { transform: 'translateY(1px)', }, '&::after': mode === currentModeKey ? { content: '""', position: 'absolute', bottom: -16, left: '50%', width: 2, height: 16, bgcolor: 'primary.main', transform: 'translateX(-50%)', } : {}, }) // 描述样式 const descriptionStyles = { width: '95%', textAlign: 'center', color: 'text.secondary', p: 0.8, borderRadius: 1, borderColor: 'primary.main', borderWidth: 1, borderStyle: 'solid', backgroundColor: 'background.paper', wordBreak: 'break-word', hyphens: 'auto', } return ( {/* 模式选择按钮组 */} {modeList.map((mode) => ( onChangeMode(mode)} sx={buttonStyles(mode)} > {modeIcons[mode]} {t(MODE_META[mode].label)} ))} {/* 说明文本区域 */} {modeDescription} ) }