feat: support to change proxy layout column
This commit is contained in:
parent
5c5177ec57
commit
4d2b35e09d
@ -68,6 +68,9 @@ pub struct IVerge {
|
|||||||
|
|
||||||
/// 是否使用内部的脚本支持,默认为真
|
/// 是否使用内部的脚本支持,默认为真
|
||||||
pub enable_builtin_enhanced: Option<bool>,
|
pub enable_builtin_enhanced: Option<bool>,
|
||||||
|
|
||||||
|
/// proxy 页面布局 列数
|
||||||
|
pub proxy_layout_column: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
#[derive(Default, Debug, Clone, Deserialize, Serialize)]
|
||||||
@ -117,6 +120,7 @@ impl IVerge {
|
|||||||
proxy_guard_duration: Some(30),
|
proxy_guard_duration: Some(30),
|
||||||
auto_close_connection: Some(true),
|
auto_close_connection: Some(true),
|
||||||
enable_builtin_enhanced: Some(true),
|
enable_builtin_enhanced: Some(true),
|
||||||
|
proxy_layout_column: Some(1),
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +163,7 @@ impl IVerge {
|
|||||||
patch!(auto_close_connection);
|
patch!(auto_close_connection);
|
||||||
patch!(default_latency_test);
|
patch!(default_latency_test);
|
||||||
patch!(enable_builtin_enhanced);
|
patch!(enable_builtin_enhanced);
|
||||||
|
patch!(proxy_layout_column);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 在初始化前尝试拿到单例端口的值
|
/// 在初始化前尝试拿到单例端口的值
|
||||||
|
@ -68,8 +68,10 @@ export const ProxyGroups = (props: Props) => {
|
|||||||
// 测全部延迟
|
// 测全部延迟
|
||||||
const handleCheckAll = useLockFn(async (groupName: string) => {
|
const handleCheckAll = useLockFn(async (groupName: string) => {
|
||||||
const proxies = renderList
|
const proxies = renderList
|
||||||
.filter((e) => e.type === 2 && e.group?.name === groupName)
|
.filter(
|
||||||
.map((e) => e.proxy!)
|
(e) => e.group?.name === groupName && (e.type === 2 || e.type === 4)
|
||||||
|
)
|
||||||
|
.flatMap((e) => e.proxyCol || e.proxy!)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
const providers = new Set(proxies.map((p) => p!.provider!).filter(Boolean));
|
const providers = new Set(proxies.map((p) => p!.provider!).filter(Boolean));
|
||||||
@ -92,7 +94,10 @@ export const ProxyGroups = (props: Props) => {
|
|||||||
const { name, now } = group;
|
const { name, now } = group;
|
||||||
|
|
||||||
const index = renderList.findIndex(
|
const index = renderList.findIndex(
|
||||||
(e) => e.type === 2 && e.group?.name === name && e.proxy?.name === now
|
(e) =>
|
||||||
|
e.group?.name === name &&
|
||||||
|
((e.type === 2 && e.proxy?.name === now) ||
|
||||||
|
(e.type === 4 && e.proxyCol?.some((p) => p.name === now)))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
|
182
src/components/proxy/proxy-item-mini.tsx
Normal file
182
src/components/proxy/proxy-item-mini.tsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useLockFn } from "ahooks";
|
||||||
|
import { CheckCircleOutlineRounded } from "@mui/icons-material";
|
||||||
|
import {
|
||||||
|
alpha,
|
||||||
|
Box,
|
||||||
|
ListItemButton,
|
||||||
|
ListItemIcon,
|
||||||
|
ListItemText,
|
||||||
|
styled,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { BaseLoading } from "@/components/base";
|
||||||
|
import delayManager from "@/services/delay";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
groupName: string;
|
||||||
|
proxy: IProxyItem;
|
||||||
|
selected: boolean;
|
||||||
|
showType?: boolean;
|
||||||
|
onClick?: (name: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多列布局
|
||||||
|
export const ProxyItemMini = (props: Props) => {
|
||||||
|
const { groupName, proxy, selected, showType = true, onClick } = props;
|
||||||
|
|
||||||
|
// -1/<=0 为 不显示
|
||||||
|
// -2 为 loading
|
||||||
|
const [delay, setDelay] = useState(-1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
delayManager.setListener(proxy.name, groupName, setDelay);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
delayManager.removeListener(proxy.name, groupName);
|
||||||
|
};
|
||||||
|
}, [proxy.name, groupName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!proxy) return;
|
||||||
|
setDelay(delayManager.getDelayFix(proxy, groupName));
|
||||||
|
}, [proxy]);
|
||||||
|
|
||||||
|
const onDelay = useLockFn(async () => {
|
||||||
|
setDelay(-2);
|
||||||
|
setDelay(await delayManager.checkDelay(proxy.name, groupName));
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItemButton
|
||||||
|
dense
|
||||||
|
selected={selected}
|
||||||
|
onClick={() => onClick?.(proxy.name)}
|
||||||
|
sx={[
|
||||||
|
{ borderRadius: 1, pl: 1.5, pr: 1 },
|
||||||
|
({ palette: { mode, primary } }) => {
|
||||||
|
const bgcolor =
|
||||||
|
mode === "light"
|
||||||
|
? alpha(primary.main, 0.15)
|
||||||
|
: alpha(primary.main, 0.35);
|
||||||
|
const color = mode === "light" ? primary.main : primary.light;
|
||||||
|
const showDelay = delay > 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"&:hover .the-check": { display: !showDelay ? "block" : "none" },
|
||||||
|
"&:hover .the-delay": { display: showDelay ? "block" : "none" },
|
||||||
|
"&:hover .the-icon": { display: "none" },
|
||||||
|
"&.Mui-selected": { bgcolor },
|
||||||
|
"&.Mui-selected .MuiListItemText-secondary": { color },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
title={proxy.name}
|
||||||
|
secondary={
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
wordBreak: "break-all",
|
||||||
|
overflow: "hidden",
|
||||||
|
whiteSpace: showType ? "nowrap" : "inherit",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{proxy.name}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showType && (
|
||||||
|
<>
|
||||||
|
{!!proxy.provider && (
|
||||||
|
<TypeBox component="span">{proxy.provider}</TypeBox>
|
||||||
|
)}
|
||||||
|
<TypeBox component="span">{proxy.type}</TypeBox>
|
||||||
|
{proxy.udp && <TypeBox component="span">UDP</TypeBox>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ListItemIcon sx={{ justifyContent: "flex-end", color: "primary.main" }}>
|
||||||
|
{delay === -2 && (
|
||||||
|
<Widget>
|
||||||
|
<BaseLoading />
|
||||||
|
</Widget>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!proxy.provider && delay !== -2 && (
|
||||||
|
// provider的节点不支持检测
|
||||||
|
<Widget
|
||||||
|
className="the-check"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onDelay();
|
||||||
|
}}
|
||||||
|
sx={({ palette }) => ({
|
||||||
|
display: "none", // hover才显示
|
||||||
|
":hover": { bgcolor: alpha(palette.primary.main, 0.15) },
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
Check
|
||||||
|
</Widget>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{delay > 0 && (
|
||||||
|
// 显示延迟
|
||||||
|
<Widget
|
||||||
|
className="the-delay"
|
||||||
|
onClick={(e) => {
|
||||||
|
if (proxy.provider) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onDelay();
|
||||||
|
}}
|
||||||
|
color={
|
||||||
|
delay > 500
|
||||||
|
? "error.main"
|
||||||
|
: delay < 100
|
||||||
|
? "success.main"
|
||||||
|
: "text.secondary"
|
||||||
|
}
|
||||||
|
sx={({ palette }) =>
|
||||||
|
!proxy.provider
|
||||||
|
? { ":hover": { bgcolor: alpha(palette.primary.main, 0.15) } }
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{delay > 1e5 ? "Error" : delay > 3000 ? "Timeout" : `${delay}`}
|
||||||
|
</Widget>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{delay !== -2 && delay <= 0 && selected && (
|
||||||
|
// 展示已选择的icon
|
||||||
|
<CheckCircleOutlineRounded
|
||||||
|
className="the-icon"
|
||||||
|
sx={{ fontSize: 16 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ListItemIcon>
|
||||||
|
</ListItemButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Widget = styled(Box)(() => ({
|
||||||
|
padding: "3px 6px",
|
||||||
|
fontSize: 14,
|
||||||
|
borderRadius: "4px",
|
||||||
|
}));
|
||||||
|
|
||||||
|
const TypeBox = styled(Box)(({ theme }) => ({
|
||||||
|
display: "inline-block",
|
||||||
|
border: "1px solid #ccc",
|
||||||
|
borderColor: alpha(theme.palette.text.secondary, 0.36),
|
||||||
|
color: alpha(theme.palette.text.secondary, 0.42),
|
||||||
|
borderRadius: 4,
|
||||||
|
fontSize: 10,
|
||||||
|
marginRight: "4px",
|
||||||
|
padding: "0 2px",
|
||||||
|
lineHeight: 1.25,
|
||||||
|
}));
|
@ -14,6 +14,7 @@ import {
|
|||||||
import { HeadState } from "./use-head-state";
|
import { HeadState } from "./use-head-state";
|
||||||
import { ProxyHead } from "./proxy-head";
|
import { ProxyHead } from "./proxy-head";
|
||||||
import { ProxyItem } from "./proxy-item";
|
import { ProxyItem } from "./proxy-item";
|
||||||
|
import { ProxyItemMini } from "./proxy-item-mini";
|
||||||
import type { IRenderItem } from "./use-render-list";
|
import type { IRenderItem } from "./use-render-list";
|
||||||
|
|
||||||
interface RenderProps {
|
interface RenderProps {
|
||||||
@ -28,7 +29,7 @@ interface RenderProps {
|
|||||||
export const ProxyRender = (props: RenderProps) => {
|
export const ProxyRender = (props: RenderProps) => {
|
||||||
const { indent, item, onLocation, onCheckAll, onHeadState, onChangeProxy } =
|
const { indent, item, onLocation, onCheckAll, onHeadState, onChangeProxy } =
|
||||||
props;
|
props;
|
||||||
const { type, group, headState, proxy } = item;
|
const { type, group, headState, proxy, proxyCol } = item;
|
||||||
|
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
return (
|
return (
|
||||||
@ -105,6 +106,32 @@ export const ProxyRender = (props: RenderProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 4) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "grid",
|
||||||
|
gap: 1,
|
||||||
|
pl: indent ? 4 : 2,
|
||||||
|
pr: 2,
|
||||||
|
pb: 1,
|
||||||
|
gridTemplateColumns: `repeat(${item.col! || 2}, 1fr)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{proxyCol?.map((proxy) => (
|
||||||
|
<ProxyItemMini
|
||||||
|
key={item.key + proxy.name}
|
||||||
|
groupName={group.name}
|
||||||
|
proxy={proxy!}
|
||||||
|
selected={group.now === proxy?.name}
|
||||||
|
showType={headState?.showType}
|
||||||
|
onClick={() => onChangeProxy(group, proxy!)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { getProxies } from "@/services/api";
|
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
|
import { getProxies } from "@/services/api";
|
||||||
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { filterSort } from "./use-filter-sort";
|
import { filterSort } from "./use-filter-sort";
|
||||||
import {
|
import {
|
||||||
useHeadStateNew,
|
useHeadStateNew,
|
||||||
@ -9,10 +10,13 @@ import {
|
|||||||
} from "./use-head-state";
|
} from "./use-head-state";
|
||||||
|
|
||||||
export interface IRenderItem {
|
export interface IRenderItem {
|
||||||
type: 0 | 1 | 2 | 3; // 组 | head | item | empty
|
// 组 | head | item | empty | item col
|
||||||
|
type: 0 | 1 | 2 | 3 | 4;
|
||||||
key: string;
|
key: string;
|
||||||
group: IProxyGroupItem;
|
group: IProxyGroupItem;
|
||||||
proxy?: IProxyItem;
|
proxy?: IProxyItem;
|
||||||
|
col?: number;
|
||||||
|
proxyCol?: IProxyItem[];
|
||||||
headState?: HeadState;
|
headState?: HeadState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +27,9 @@ export const useRenderList = (mode: string) => {
|
|||||||
{ refreshInterval: 45000 }
|
{ refreshInterval: 45000 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { verge } = useVerge();
|
||||||
|
const col = verge?.proxy_layout_column || 1;
|
||||||
|
|
||||||
const [headStates, setHeadState] = useHeadStateNew();
|
const [headStates, setHeadState] = useHeadStateNew();
|
||||||
|
|
||||||
// make sure that fetch the proxies successfully
|
// make sure that fetch the proxies successfully
|
||||||
@ -62,10 +69,24 @@ export const useRenderList = (mode: string) => {
|
|||||||
headState.sortType
|
headState.sortType
|
||||||
);
|
);
|
||||||
|
|
||||||
ret.push({ type: 1, key: `head${group.name}`, group, headState });
|
ret.push({ type: 1, key: `head-${group.name}`, group, headState });
|
||||||
|
|
||||||
if (!proxies.length) {
|
if (!proxies.length) {
|
||||||
ret.push({ type: 3, key: `empty${group.name}`, group, headState });
|
ret.push({ type: 3, key: `empty-${group.name}`, group, headState });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持多列布局
|
||||||
|
if (col > 1) {
|
||||||
|
return ret.concat(
|
||||||
|
groupList(proxies, col).map((proxyCol) => ({
|
||||||
|
type: 4,
|
||||||
|
key: `col-${group.name}`,
|
||||||
|
group,
|
||||||
|
headState,
|
||||||
|
col,
|
||||||
|
proxyCol,
|
||||||
|
}))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret.concat(
|
return ret.concat(
|
||||||
@ -83,7 +104,7 @@ export const useRenderList = (mode: string) => {
|
|||||||
|
|
||||||
if (!useRule) return retList.slice(1);
|
if (!useRule) return retList.slice(1);
|
||||||
return retList;
|
return retList;
|
||||||
}, [headStates, proxiesData, mode]);
|
}, [headStates, proxiesData, mode, col]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
renderList,
|
renderList,
|
||||||
@ -91,3 +112,18 @@ export const useRenderList = (mode: string) => {
|
|||||||
onHeadState: setHeadState,
|
onHeadState: setHeadState,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function groupList<T = any>(list: T[], size: number): T[][] {
|
||||||
|
return list.reduce((p, n) => {
|
||||||
|
if (!p.length) return [[n]];
|
||||||
|
|
||||||
|
const i = p.length - 1;
|
||||||
|
if (p[i].length < size) {
|
||||||
|
p[i].push(n);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.push([n]);
|
||||||
|
return p;
|
||||||
|
}, [] as T[][]);
|
||||||
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import { forwardRef, useImperativeHandle, useState } from "react";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { List, ListItem, ListItemText, Switch, TextField } from "@mui/material";
|
import {
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
MenuItem,
|
||||||
|
Select,
|
||||||
|
Switch,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
||||||
|
|
||||||
@ -12,6 +20,8 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [values, setValues] = useState({
|
const [values, setValues] = useState({
|
||||||
autoCloseConnection: false,
|
autoCloseConnection: false,
|
||||||
|
enableBuiltinEnhanced: true,
|
||||||
|
proxyLayoutColumn: 1,
|
||||||
defaultLatencyTest: "",
|
defaultLatencyTest: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -19,7 +29,9 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
open: () => {
|
open: () => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
setValues({
|
setValues({
|
||||||
autoCloseConnection: verge?.auto_close_connection || false,
|
autoCloseConnection: verge?.auto_close_connection ?? false,
|
||||||
|
enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true,
|
||||||
|
proxyLayoutColumn: verge?.proxy_layout_column || 1,
|
||||||
defaultLatencyTest: verge?.default_latency_test || "",
|
defaultLatencyTest: verge?.default_latency_test || "",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -30,6 +42,8 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
try {
|
try {
|
||||||
await patchVerge({
|
await patchVerge({
|
||||||
auto_close_connection: values.autoCloseConnection,
|
auto_close_connection: values.autoCloseConnection,
|
||||||
|
enable_builtin_enhanced: values.enableBuiltinEnhanced,
|
||||||
|
proxy_layout_column: values.proxyLayoutColumn,
|
||||||
default_latency_test: values.defaultLatencyTest,
|
default_latency_test: values.defaultLatencyTest,
|
||||||
});
|
});
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
@ -42,7 +56,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
<BaseDialog
|
<BaseDialog
|
||||||
open={open}
|
open={open}
|
||||||
title={t("Miscellaneous")}
|
title={t("Miscellaneous")}
|
||||||
contentSx={{ width: 420 }}
|
contentSx={{ width: 450 }}
|
||||||
okBtn={t("Save")}
|
okBtn={t("Save")}
|
||||||
cancelBtn={t("Cancel")}
|
cancelBtn={t("Cancel")}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
@ -61,6 +75,38 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem sx={{ padding: "5px 2px" }}>
|
||||||
|
<ListItemText primary="Enable Builtin Enhanced" />
|
||||||
|
<Switch
|
||||||
|
edge="end"
|
||||||
|
checked={values.enableBuiltinEnhanced}
|
||||||
|
onChange={(_, c) =>
|
||||||
|
setValues((v) => ({ ...v, enableBuiltinEnhanced: c }))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
|
<ListItem sx={{ padding: "5px 2px" }}>
|
||||||
|
<ListItemText primary="Proxy Layout Column" />
|
||||||
|
<Select
|
||||||
|
size="small"
|
||||||
|
sx={{ width: 100, "> div": { py: "7.5px" } }}
|
||||||
|
value={values.proxyLayoutColumn}
|
||||||
|
onChange={(e) => {
|
||||||
|
setValues((v) => ({
|
||||||
|
...v,
|
||||||
|
proxyLayoutColumn: e.target.value as number,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{[1, 2, 3, 4, 5].map((i) => (
|
||||||
|
<MenuItem value={i} key={i}>
|
||||||
|
{i}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</ListItem>
|
||||||
|
|
||||||
<ListItem sx={{ padding: "5px 2px" }}>
|
<ListItem sx={{ padding: "5px 2px" }}>
|
||||||
<ListItemText primary="Default Latency Test" />
|
<ListItemText primary="Default Latency Test" />
|
||||||
<TextField
|
<TextField
|
||||||
@ -69,7 +115,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
sx={{ width: 200 }}
|
sx={{ width: 250 }}
|
||||||
value={values.defaultLatencyTest}
|
value={values.defaultLatencyTest}
|
||||||
placeholder="http://www.gstatic.com/generate_204"
|
placeholder="http://www.gstatic.com/generate_204"
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
|
2
src/services/types.d.ts
vendored
2
src/services/types.d.ts
vendored
@ -163,6 +163,8 @@ interface IVergeConfig {
|
|||||||
};
|
};
|
||||||
auto_close_connection?: boolean;
|
auto_close_connection?: boolean;
|
||||||
default_latency_test?: string;
|
default_latency_test?: string;
|
||||||
|
enable_builtin_enhanced?: boolean;
|
||||||
|
proxy_layout_column?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type IClashConfigValue = any;
|
type IClashConfigValue = any;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user