code formatting with prettier

This commit is contained in:
coolcoala
2025-07-14 05:23:32 +03:00
parent eb1e4fe0c3
commit 5cdc5075f8
58 changed files with 5163 additions and 1846 deletions

View File

@@ -1,4 +1,11 @@
import { forwardRef, useImperativeHandle, useState, useMemo, useEffect, ReactNode } from "react";
import {
forwardRef,
useImperativeHandle,
useState,
useMemo,
useEffect,
ReactNode,
} from "react";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
import useSWR, { mutate } from "swr";
@@ -21,12 +28,29 @@ import {
import { showNotice } from "@/services/noticeService";
import getSystem from "@/utils/get-system";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose } from "@/components/ui/dialog";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
DialogClose,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Command,
CommandEmpty,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { Check, ChevronsUpDown, Edit, Loader2 } from "lucide-react";
import { cn } from "@root/lib/utils";
import { TooltipIcon } from "@/components/base/base-tooltip-icon";
@@ -34,7 +58,8 @@ import { TooltipIcon } from "@/components/base/base-tooltip-icon";
// --- Вся ваша оригинальная логика, константы и хелперы ---
const DEFAULT_PAC = `function FindProxyForURL(url, host) { return "PROXY %proxy_host%:%mixed-port%; SOCKS5 %proxy_host%:%mixed-port%; DIRECT;"; }`;
const ipv4_part = String.raw`\d{1,3}`;
const rDomainSimple = String.raw`(?:[a-z0-9\-\*]+\.|\*)*` + String.raw`(?:\w{2,64}\*?|\*)`;
const rDomainSimple =
String.raw`(?:[a-z0-9\-\*]+\.|\*)*` + String.raw`(?:\w{2,64}\*?|\*)`;
const ipv6_part = "(?:[a-fA-F0-9:])+";
const rLocal = `localhost|<local>|localdomain`;
const getValidReg = (isWindows: boolean) => {
@@ -49,40 +74,78 @@ const getValidReg = (isWindows: boolean) => {
};
// --- Компонент Combobox для замены Autocomplete ---
const Combobox = ({ options, value, onValueChange, placeholder }: { options: string[], value: string, onValueChange: (value: string) => void, placeholder?: string }) => {
const [open, setOpen] = useState(false);
return (
<Popover open={open} onOpenChange={setOpen} modal={true}>
<PopoverTrigger asChild>
<Button variant="outline" role="combobox" aria-expanded={open} className="w-48 h-8 justify-between font-normal">
{value || placeholder || "Select..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[--radix-popover-trigger-width] p-0">
<Command onValueChange={onValueChange}>
<CommandInput placeholder="Search or type..." />
<CommandEmpty>No results found.</CommandEmpty>
<CommandList>
{options.map((option) => (
<CommandItem key={option} value={option} onSelect={(currentValue) => { onValueChange(options.find(opt => opt.toLowerCase() === currentValue) || ''); setOpen(false); }}>
<Check className={cn("mr-2 h-4 w-4", value === option ? "opacity-100" : "opacity-0")} />
{option}
</CommandItem>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
const Combobox = ({
options,
value,
onValueChange,
placeholder,
}: {
options: string[];
value: string;
onValueChange: (value: string) => void;
placeholder?: string;
}) => {
const [open, setOpen] = useState(false);
return (
<Popover open={open} onOpenChange={setOpen} modal={true}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-48 h-8 justify-between font-normal"
>
{value || placeholder || "Select..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[--radix-popover-trigger-width] p-0">
<Command onValueChange={onValueChange}>
<CommandInput placeholder="Search or type..." />
<CommandEmpty>No results found.</CommandEmpty>
<CommandList>
{options.map((option) => (
<CommandItem
key={option}
value={option}
onSelect={(currentValue) => {
onValueChange(
options.find((opt) => opt.toLowerCase() === currentValue) ||
"",
);
setOpen(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
value === option ? "opacity-100" : "opacity-0",
)}
/>
{option}
</CommandItem>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};
// --- Наш переиспользуемый компонент для строки настроек ---
const SettingRow = ({ label, children }: { label: React.ReactNode; children?: React.ReactNode; }) => (
<div className="flex items-center justify-between py-2">
<Label className="text-sm text-muted-foreground flex items-center gap-2">{label}</Label>
<div>{children}</div>
</div>
const SettingRow = ({
label,
children,
}: {
label: React.ReactNode;
children?: React.ReactNode;
}) => (
<div className="flex items-center justify-between py-2">
<Label className="text-sm text-muted-foreground flex items-center gap-2">
{label}
</Label>
<div>{children}</div>
</div>
);
export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
@@ -102,25 +165,50 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
type AutoProxy = Awaited<ReturnType<typeof getAutotemProxy>>;
const [autoproxy, setAutoproxy] = useState<AutoProxy>();
const { enable_system_proxy: enabled, proxy_auto_config, pac_file_content, enable_proxy_guard, use_default_bypass, system_proxy_bypass, proxy_guard_duration, proxy_host } = verge ?? {};
const {
enable_system_proxy: enabled,
proxy_auto_config,
pac_file_content,
enable_proxy_guard,
use_default_bypass,
system_proxy_bypass,
proxy_guard_duration,
proxy_host,
} = verge ?? {};
const [value, setValue] = useState({
guard: enable_proxy_guard, bypass: system_proxy_bypass, duration: proxy_guard_duration ?? 10,
use_default: use_default_bypass ?? true, pac: proxy_auto_config, pac_content: pac_file_content ?? DEFAULT_PAC,
guard: enable_proxy_guard,
bypass: system_proxy_bypass,
duration: proxy_guard_duration ?? 10,
use_default: use_default_bypass ?? true,
pac: proxy_auto_config,
pac_content: pac_file_content ?? DEFAULT_PAC,
proxy_host: proxy_host ?? "127.0.0.1",
});
const defaultBypass = () => {
if (isWindows) return "localhost;127.*;192.168.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;<local>";
if (getSystem() === "linux") return "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,::1";
if (isWindows)
return "localhost;127.*;192.168.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;<local>";
if (getSystem() === "linux")
return "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,::1";
return "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,localhost,*.local,*.crashlytics.com,<local>";
};
const { data: clashConfig } = useSWR("getClashConfig", getClashConfig, { revalidateOnFocus: false, revalidateIfStale: true, dedupingInterval: 1000, errorRetryInterval: 5000 });
const [prevMixedPort, setPrevMixedPort] = useState(clashConfig?.["mixed-port"]);
const { data: clashConfig } = useSWR("getClashConfig", getClashConfig, {
revalidateOnFocus: false,
revalidateIfStale: true,
dedupingInterval: 1000,
errorRetryInterval: 5000,
});
const [prevMixedPort, setPrevMixedPort] = useState(
clashConfig?.["mixed-port"],
);
useEffect(() => {
if (clashConfig?.["mixed-port"] && clashConfig?.["mixed-port"] !== prevMixedPort) {
if (
clashConfig?.["mixed-port"] &&
clashConfig?.["mixed-port"] !== prevMixedPort
) {
setPrevMixedPort(clashConfig?.["mixed-port"]);
resetSystemProxy();
}
@@ -134,9 +222,14 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
await patchVergeConfig({ enable_system_proxy: false });
await new Promise((resolve) => setTimeout(resolve, 200));
await patchVergeConfig({ enable_system_proxy: true });
await Promise.all([ mutate("getSystemProxy"), mutate("getAutotemProxy") ]);
await Promise.all([
mutate("getSystemProxy"),
mutate("getAutotemProxy"),
]);
}
} catch (err: any) { showNotice("error", err.toString()); }
} catch (err: any) {
showNotice("error", err.toString());
}
};
const { systemProxyAddress } = useAppData();
@@ -151,7 +244,13 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
} else {
return systemProxyAddress;
}
}, [value.pac, value.proxy_host, verge?.verge_mixed_port, clashConfig, systemProxyAddress]);
}, [
value.pac,
value.proxy_host,
verge?.verge_mixed_port,
clashConfig,
systemProxyAddress,
]);
const getCurrentPacUrl = useMemo(() => {
const host = value.proxy_host || "127.0.0.1";
@@ -175,7 +274,9 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
if (hostname && hostname !== "localhost" && hostname !== "127.0.0.1") {
hostname = hostname + ".local";
}
} catch (err) { console.error("Failed to get hostname:", err); }
} catch (err) {
console.error("Failed to get hostname:", err);
}
const options = ["127.0.0.1", "localhost"];
if (hostname) options.push(hostname);
options.push(...ipAddresses);
@@ -190,8 +291,12 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
open: () => {
setOpen(true);
setValue({
guard: enable_proxy_guard, bypass: system_proxy_bypass, duration: proxy_guard_duration ?? 10,
use_default: use_default_bypass ?? true, pac: proxy_auto_config, pac_content: pac_file_content ?? DEFAULT_PAC,
guard: enable_proxy_guard,
bypass: system_proxy_bypass,
duration: proxy_guard_duration ?? 10,
use_default: use_default_bypass ?? true,
pac: proxy_auto_config,
pac_content: pac_file_content ?? DEFAULT_PAC,
proxy_host: proxy_host ?? "127.0.0.1",
});
getSystemProxy().then(setSysproxy);
@@ -340,65 +445,170 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
<>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="sm:max-w-lg">
<DialogHeader><DialogTitle>{t("System Proxy Setting")}</DialogTitle></DialogHeader>
<DialogHeader>
<DialogTitle>{t("System Proxy Setting")}</DialogTitle>
</DialogHeader>
<div className="max-h-[70vh] overflow-y-auto space-y-4 py-4 px-1">
<BaseFieldset label={t("Current System Proxy")}>
<div className="text-sm space-y-2">
<div className="flex justify-between"><span className="text-muted-foreground">{t("Enable status")}</span><span>{value.pac ? (autoproxy?.enable ? t("Enabled") : t("Disabled")) : (sysproxy?.enable ? t("Enabled") : t("Disabled"))}</span></div>
{!value.pac && <div className="flex justify-between"><span className="text-muted-foreground">{t("Server Addr")}</span><span className="font-mono">{getSystemProxyAddress}</span></div>}
{value.pac && <div className="flex justify-between"><span className="text-muted-foreground">{t("PAC URL")}</span><span className="font-mono">{getCurrentPacUrl || "-"}</span></div>}
<div className="text-sm space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground">
{t("Enable status")}
</span>
<span>
{value.pac
? autoproxy?.enable
? t("Enabled")
: t("Disabled")
: sysproxy?.enable
? t("Enabled")
: t("Disabled")}
</span>
</div>
{!value.pac && (
<div className="flex justify-between">
<span className="text-muted-foreground">
{t("Server Addr")}
</span>
<span className="font-mono">{getSystemProxyAddress}</span>
</div>
)}
{value.pac && (
<div className="flex justify-between">
<span className="text-muted-foreground">
{t("PAC URL")}
</span>
<span className="font-mono">{getCurrentPacUrl || "-"}</span>
</div>
)}
</div>
</BaseFieldset>
<SettingRow label={t("Proxy Host")}>
<Combobox options={hostOptions} value={value.proxy_host} onValueChange={(val) => setValue(v => ({...v, proxy_host: val}))} placeholder="127.0.0.1" />
<Combobox
options={hostOptions}
value={value.proxy_host}
onValueChange={(val) =>
setValue((v) => ({ ...v, proxy_host: val }))
}
placeholder="127.0.0.1"
/>
</SettingRow>
<SettingRow label={t("Use PAC Mode")}>
<Switch disabled={!enabled} checked={value.pac} onCheckedChange={(e) => setValue((v) => ({ ...v, pac: e }))} />
<Switch
disabled={!enabled}
checked={value.pac}
onCheckedChange={(e) => setValue((v) => ({ ...v, pac: e }))}
/>
</SettingRow>
<SettingRow label={<>{t("Proxy Guard")} <TooltipIcon tooltip={t("Proxy Guard Info")} /></>}>
<Switch disabled={!enabled} checked={value.guard} onCheckedChange={(e) => setValue((v) => ({ ...v, guard: e }))} />
<SettingRow
label={
<>
{t("Proxy Guard")}{" "}
<TooltipIcon tooltip={t("Proxy Guard Info")} />
</>
}
>
<Switch
disabled={!enabled}
checked={value.guard}
onCheckedChange={(e) => setValue((v) => ({ ...v, guard: e }))}
/>
</SettingRow>
<SettingRow label={t("Guard Duration")}>
<div className="flex items-center gap-2">
<Input disabled={!enabled} type="number" className="w-24 h-8" value={value.duration} onChange={(e) => setValue((v) => ({ ...v, duration: +e.target.value.replace(/\D/, "") }))}/>
<span className="text-sm text-muted-foreground">s</span>
</div>
<div className="flex items-center gap-2">
<Input
disabled={!enabled}
type="number"
className="w-24 h-8"
value={value.duration}
onChange={(e) =>
setValue((v) => ({
...v,
duration: +e.target.value.replace(/\D/, ""),
}))
}
/>
<span className="text-sm text-muted-foreground">s</span>
</div>
</SettingRow>
{!value.pac && (
<SettingRow label={t("Always use Default Bypass")}>
<Switch disabled={!enabled} checked={value.use_default} onCheckedChange={(e) => setValue((v) => ({...v, use_default: e, bypass: !e && !v.bypass ? defaultBypass() : v.bypass}))}/>
</SettingRow>
<SettingRow label={t("Always use Default Bypass")}>
<Switch
disabled={!enabled}
checked={value.use_default}
onCheckedChange={(e) =>
setValue((v) => ({
...v,
use_default: e,
bypass: !e && !v.bypass ? defaultBypass() : v.bypass,
}))
}
/>
</SettingRow>
)}
{!value.pac && !value.use_default && (
<div className="space-y-2">
<Label>{t("Proxy Bypass")}</Label>
<Textarea
id="proxy-bypass"
disabled={!enabled}
rows={4}
value={value.bypass}
onChange={(e) => setValue((v) => ({ ...v, bypass: e.target.value }))}
// Вместо пропса `error` используем условные классы
className={cn(
(value.bypass && !validReg.test(value.bypass)) && "border-destructive focus-visible:ring-destructive"
)}
/>
</div>
<div className="space-y-2">
<Label>{t("Proxy Bypass")}</Label>
<Textarea
id="proxy-bypass"
disabled={!enabled}
rows={4}
value={value.bypass}
onChange={(e) =>
setValue((v) => ({ ...v, bypass: e.target.value }))
}
// Вместо пропса `error` используем условные классы
className={cn(
value.bypass &&
!validReg.test(value.bypass) &&
"border-destructive focus-visible:ring-destructive",
)}
/>
</div>
)}
{value.pac && (
<SettingRow label={t("PAC Script Content")}>
<Button variant="outline" size="sm" onClick={() => setEditorOpen(true)}><Edit className="mr-2 h-4 w-4"/>{t("Edit")} PAC</Button>
</SettingRow>
<SettingRow label={t("PAC Script Content")}>
<Button
variant="outline"
size="sm"
onClick={() => setEditorOpen(true)}
>
<Edit className="mr-2 h-4 w-4" />
{t("Edit")} PAC
</Button>
</SettingRow>
)}
</div>
<DialogFooter>
<DialogClose asChild><Button type="button" variant="outline">{t("Cancel")}</Button></DialogClose>
<Button type="button" onClick={onSave} disabled={saving}>{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin"/>}{t("Save")}</Button>
<DialogClose asChild>
<Button type="button" variant="outline">
{t("Cancel")}
</Button>
</DialogClose>
<Button type="button" onClick={onSave} disabled={saving}>
{saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
{t("Save")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{editorOpen && <EditorViewer open={true} title={`${t("Edit")} PAC`} initialData={Promise.resolve(value.pac_content ?? "")} language="javascript" onSave={(_prev, curr) => { let pac = DEFAULT_PAC; if (curr && curr.trim().length > 0) { pac = curr; } setValue((v) => ({ ...v, pac_content: pac })); }} onClose={() => setEditorOpen(false)} />}
{editorOpen && (
<EditorViewer
open={true}
title={`${t("Edit")} PAC`}
initialData={Promise.resolve(value.pac_content ?? "")}
language="javascript"
onSave={(_prev, curr) => {
let pac = DEFAULT_PAC;
if (curr && curr.trim().length > 0) {
pac = curr;
}
setValue((v) => ({ ...v, pac_content: pac }));
}}
onClose={() => setEditorOpen(false)}
/>
)}
</>
);
});