From 4ff186497c34dd74512a134364253339186cb009 Mon Sep 17 00:00:00 2001 From: Sline Date: Sat, 22 Nov 2025 18:58:57 +0800 Subject: [PATCH] feat(tun): auto-redirect GUI support for linux (#5565) * feat(tun): auto-redirect GUI support for linux * refactor(tun-viewer): unify Linux-only auto-redirect row layout --- src/components/setting/mods/tun-viewer.tsx | 57 ++++++++++++++++++++-- src/locales/ar/settings.json | 6 ++- src/locales/de/settings.json | 6 ++- src/locales/en/settings.json | 6 ++- src/locales/es/settings.json | 6 ++- src/locales/fa/settings.json | 6 ++- src/locales/id/settings.json | 6 ++- src/locales/jp/settings.json | 6 ++- src/locales/ko/settings.json | 6 ++- src/locales/ru/settings.json | 6 ++- src/locales/tr/settings.json | 6 ++- src/locales/tt/settings.json | 6 ++- src/locales/zh/settings.json | 6 ++- src/locales/zhtw/settings.json | 6 ++- src/types/generated/i18n-keys.ts | 2 + src/types/generated/i18n-resources.ts | 2 + src/types/types.d.ts | 1 + 17 files changed, 110 insertions(+), 30 deletions(-) diff --git a/src/components/setting/mods/tun-viewer.tsx b/src/components/setting/mods/tun-viewer.tsx index 98aab374d..02bc984b1 100644 --- a/src/components/setting/mods/tun-viewer.tsx +++ b/src/components/setting/mods/tun-viewer.tsx @@ -13,6 +13,7 @@ import { useImperativeHandle, useState } from "react"; import { useTranslation } from "react-i18next"; import { BaseDialog, DialogRef, Switch } from "@/components/base"; +import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { useClash } from "@/hooks/use-clash"; import { enhanceProfiles } from "@/services/cmds"; import { showNotice } from "@/services/noticeService"; @@ -32,6 +33,7 @@ export function TunViewer({ ref }: { ref?: Ref }) { stack: "mixed", device: OS === "macos" ? "utun1024" : "Mihomo", autoRoute: true, + autoRedirect: OS === "linux", autoDetectInterface: true, dnsHijack: ["any:53"], strictRoute: false, @@ -41,10 +43,15 @@ export function TunViewer({ ref }: { ref?: Ref }) { useImperativeHandle(ref, () => ({ open: () => { setOpen(true); + const nextAutoRoute = clash?.tun["auto-route"] ?? true; + const rawAutoRedirect = clash?.tun["auto-redirect"] ?? true; + const computedAutoRedirect = + OS === "linux" ? (nextAutoRoute ? rawAutoRedirect : false) : false; setValues({ stack: clash?.tun.stack ?? "gvisor", device: clash?.tun.device ?? (OS === "macos" ? "utun1024" : "Mihomo"), - autoRoute: clash?.tun["auto-route"] ?? true, + autoRoute: nextAutoRoute, + autoRedirect: computedAutoRedirect, autoDetectInterface: clash?.tun["auto-detect-interface"] ?? true, dnsHijack: clash?.tun["dns-hijack"] ?? ["any:53"], strictRoute: clash?.tun["strict-route"] ?? false, @@ -56,7 +63,7 @@ export function TunViewer({ ref }: { ref?: Ref }) { const onSave = useLockFn(async () => { try { - const tun = { + const tun: IConfigData["tun"] = { stack: values.stack, device: values.device === "" @@ -65,6 +72,11 @@ export function TunViewer({ ref }: { ref?: Ref }) { : "Mihomo" : values.device, "auto-route": values.autoRoute, + ...(OS === "linux" + ? { + "auto-redirect": values.autoRedirect, + } + : {}), "auto-detect-interface": values.autoDetectInterface, "dns-hijack": values.dnsHijack[0] === "" ? [] : values.dnsHijack, "strict-route": values.strictRoute, @@ -100,10 +112,15 @@ export function TunViewer({ ref }: { ref?: Ref }) { variant="outlined" size="small" onClick={async () => { - const tun = { + const tun: IConfigData["tun"] = { stack: "gvisor", device: OS === "macos" ? "utun1024" : "Mihomo", "auto-route": true, + ...(OS === "linux" + ? { + "auto-redirect": true, + } + : {}), "auto-detect-interface": true, "dns-hijack": ["any:53"], "strict-route": false, @@ -113,6 +130,7 @@ export function TunViewer({ ref }: { ref?: Ref }) { stack: "gvisor", device: OS === "macos" ? "utun1024" : "Mihomo", autoRoute: true, + autoRedirect: OS === "linux" ? true : false, autoDetectInterface: true, dnsHijack: ["any:53"], strictRoute: false, @@ -175,10 +193,41 @@ export function TunViewer({ ref }: { ref?: Ref }) { setValues((v) => ({ ...v, autoRoute: c }))} + onChange={(_, c) => + setValues((v) => ({ + ...v, + autoRoute: c, + autoRedirect: c ? v.autoRedirect : false, + })) + } /> + {OS === "linux" && ( + + + + + setValues((v) => ({ + ...v, + autoRedirect: v.autoRoute ? c : v.autoRedirect, + })) + } + disabled={!values.autoRoute} + sx={{ marginLeft: "auto" }} + /> + + )} +