feat: add rustfmt configuration and CI workflow for code formatting
refactor: streamline formatting workflow by removing unused taplo steps and clarifying directory change refactor: remove unnecessary directory change step in formatting workflow
This commit is contained in:
@@ -127,7 +127,7 @@ export const EditorViewer = <T extends Language>(props: Props<T>) => {
|
||||
currData.current = value;
|
||||
onChange?.(prevData.current, currData.current);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -136,7 +136,7 @@ export const EditorViewer = <T extends Language>(props: Props<T>) => {
|
||||
!readOnly && onSave?.(prevData.current, currData.current);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -144,7 +144,7 @@ export const EditorViewer = <T extends Language>(props: Props<T>) => {
|
||||
try {
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ export const GroupItem = (props: Props) => {
|
||||
? alpha(palette.background.paper, 0.3)
|
||||
: alpha(palette.grey[400], 0.3)
|
||||
: type === "delete"
|
||||
? alpha(palette.error.main, 0.3)
|
||||
: alpha(palette.success.main, 0.3),
|
||||
? alpha(palette.error.main, 0.3)
|
||||
: alpha(palette.success.main, 0.3),
|
||||
height: "100%",
|
||||
margin: "8px 0",
|
||||
borderRadius: "8px",
|
||||
|
||||
@@ -90,27 +90,27 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
|
||||
const filteredPrependSeq = useMemo(
|
||||
() => prependSeq.filter((group) => match(group.name)),
|
||||
[prependSeq, match]
|
||||
[prependSeq, match],
|
||||
);
|
||||
const filteredGroupList = useMemo(
|
||||
() => groupList.filter((group) => match(group.name)),
|
||||
[groupList, match]
|
||||
[groupList, match],
|
||||
);
|
||||
const filteredAppendSeq = useMemo(
|
||||
() => appendSeq.filter((group) => match(group.name)),
|
||||
[appendSeq, match]
|
||||
[appendSeq, match],
|
||||
);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
}),
|
||||
);
|
||||
const reorder = (
|
||||
list: IProxyGroupConfig[],
|
||||
startIndex: number,
|
||||
endIndex: number
|
||||
endIndex: number,
|
||||
) => {
|
||||
const result = Array.from(list);
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
@@ -188,8 +188,8 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
setCurrData(
|
||||
yaml.dump(
|
||||
{ prepend: prependSeq, append: appendSeq, delete: deleteSeq },
|
||||
{ forceQuotes: true }
|
||||
)
|
||||
{ forceQuotes: true },
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
// 防止异常导致UI卡死
|
||||
@@ -226,7 +226,7 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
return !moreDeleteProxies.includes(proxy);
|
||||
}
|
||||
}),
|
||||
moreAppendProxies
|
||||
moreAppendProxies,
|
||||
);
|
||||
|
||||
setProxyPolicyList(
|
||||
@@ -236,8 +236,8 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
.map((group: IProxyGroupConfig) => group.name)
|
||||
.filter((name) => !deleteSeq.includes(name)) || [],
|
||||
appendSeq.map((group: IProxyGroupConfig) => group.name),
|
||||
proxies.map((proxy: any) => proxy.name)
|
||||
)
|
||||
proxies.map((proxy: any) => proxy.name),
|
||||
),
|
||||
);
|
||||
};
|
||||
const fetchProfile = async () => {
|
||||
@@ -266,7 +266,7 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
{},
|
||||
originProvider,
|
||||
moreProvider,
|
||||
globalProvider
|
||||
globalProvider,
|
||||
);
|
||||
|
||||
setProxyProviderList(Object.keys(provider));
|
||||
@@ -297,11 +297,11 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
const handleSave = useLockFn(async () => {
|
||||
try {
|
||||
await saveProfileFile(property, currData);
|
||||
showNotice('success', t("Saved Successfully"));
|
||||
showNotice("success", t("Saved Successfully"));
|
||||
onSave?.(prevData, currData);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -502,7 +502,7 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
{t("seconds")}
|
||||
</InputAdornment>
|
||||
),
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
@@ -530,7 +530,7 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
{t("millis")}
|
||||
</InputAdornment>
|
||||
),
|
||||
}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
@@ -742,7 +742,7 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
}
|
||||
setPrependSeq([formIns.getValues(), ...prependSeq]);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -764,7 +764,7 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
}
|
||||
setAppendSeq([...appendSeq, formIns.getValues()]);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -811,8 +811,8 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
onDelete={() => {
|
||||
setPrependSeq(
|
||||
prependSeq.filter(
|
||||
(v) => v.name !== item.name
|
||||
)
|
||||
(v) => v.name !== item.name,
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -838,8 +838,8 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
) {
|
||||
setDeleteSeq(
|
||||
deleteSeq.filter(
|
||||
(v) => v !== filteredGroupList[newIndex].name
|
||||
)
|
||||
(v) => v !== filteredGroupList[newIndex].name,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
setDeleteSeq((prev) => [
|
||||
@@ -871,8 +871,8 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
onDelete={() => {
|
||||
setAppendSeq(
|
||||
appendSeq.filter(
|
||||
(v) => v.name !== item.name
|
||||
)
|
||||
(v) => v.name !== item.name,
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -906,8 +906,9 @@ export const GroupsEditorViewer = (props: Props) => {
|
||||
padding: {
|
||||
top: 33, // 顶部padding防止遮挡snippets
|
||||
},
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${
|
||||
getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontLigatures: false, // 连字符
|
||||
smoothScrolling: true, // 平滑滚动
|
||||
}}
|
||||
|
||||
@@ -1,57 +1,58 @@
|
||||
import { alpha, Box, styled } from "@mui/material";
|
||||
|
||||
export const ProfileBox = styled(Box)(
|
||||
({ theme, "aria-selected": selected }) => {
|
||||
const { mode, primary, text } = theme.palette;
|
||||
const key = `${mode}-${!!selected}`;
|
||||
export const ProfileBox = styled(Box)(({
|
||||
theme,
|
||||
"aria-selected": selected,
|
||||
}) => {
|
||||
const { mode, primary, text } = theme.palette;
|
||||
const key = `${mode}-${!!selected}`;
|
||||
|
||||
const backgroundColor = mode === "light" ? "#ffffff" : "#282A36";
|
||||
const backgroundColor = mode === "light" ? "#ffffff" : "#282A36";
|
||||
|
||||
const color = {
|
||||
"light-true": text.secondary,
|
||||
"light-false": text.secondary,
|
||||
"dark-true": alpha(text.secondary, 0.65),
|
||||
"dark-false": alpha(text.secondary, 0.65),
|
||||
}[key]!;
|
||||
const color = {
|
||||
"light-true": text.secondary,
|
||||
"light-false": text.secondary,
|
||||
"dark-true": alpha(text.secondary, 0.65),
|
||||
"dark-false": alpha(text.secondary, 0.65),
|
||||
}[key]!;
|
||||
|
||||
const h2color = {
|
||||
"light-true": primary.main,
|
||||
"light-false": text.primary,
|
||||
"dark-true": primary.main,
|
||||
"dark-false": text.primary,
|
||||
}[key]!;
|
||||
const h2color = {
|
||||
"light-true": primary.main,
|
||||
"light-false": text.primary,
|
||||
"dark-true": primary.main,
|
||||
"dark-false": text.primary,
|
||||
}[key]!;
|
||||
|
||||
const borderSelect = {
|
||||
"light-true": {
|
||||
borderLeft: `3px solid ${primary.main}`,
|
||||
width: `calc(100% + 3px)`,
|
||||
marginLeft: `-3px`,
|
||||
},
|
||||
"light-false": {
|
||||
width: "100%",
|
||||
},
|
||||
"dark-true": {
|
||||
borderLeft: `3px solid ${primary.main}`,
|
||||
width: `calc(100% + 3px)`,
|
||||
marginLeft: `-3px`,
|
||||
},
|
||||
"dark-false": {
|
||||
width: "100%",
|
||||
},
|
||||
}[key];
|
||||
const borderSelect = {
|
||||
"light-true": {
|
||||
borderLeft: `3px solid ${primary.main}`,
|
||||
width: `calc(100% + 3px)`,
|
||||
marginLeft: `-3px`,
|
||||
},
|
||||
"light-false": {
|
||||
width: "100%",
|
||||
},
|
||||
"dark-true": {
|
||||
borderLeft: `3px solid ${primary.main}`,
|
||||
width: `calc(100% + 3px)`,
|
||||
marginLeft: `-3px`,
|
||||
},
|
||||
"dark-false": {
|
||||
width: "100%",
|
||||
},
|
||||
}[key];
|
||||
|
||||
return {
|
||||
position: "relative",
|
||||
display: "block",
|
||||
cursor: "pointer",
|
||||
textAlign: "left",
|
||||
padding: "8px 16px",
|
||||
boxSizing: "border-box",
|
||||
backgroundColor,
|
||||
...borderSelect,
|
||||
borderRadius: "8px",
|
||||
color,
|
||||
"& h2": { color: h2color },
|
||||
};
|
||||
}
|
||||
);
|
||||
return {
|
||||
position: "relative",
|
||||
display: "block",
|
||||
cursor: "pointer",
|
||||
textAlign: "left",
|
||||
padding: "8px 16px",
|
||||
boxSizing: "border-box",
|
||||
backgroundColor,
|
||||
...borderSelect,
|
||||
borderRadius: "8px",
|
||||
color,
|
||||
"& h2": { color: h2color },
|
||||
};
|
||||
});
|
||||
|
||||
@@ -75,7 +75,10 @@ export const ProfileItem = (props: Props) => {
|
||||
|
||||
// 获取下次更新时间的函数
|
||||
const fetchNextUpdateTime = useLockFn(async (forceRefresh = false) => {
|
||||
if (itemData.option?.update_interval && itemData.option.update_interval > 0) {
|
||||
if (
|
||||
itemData.option?.update_interval &&
|
||||
itemData.option.update_interval > 0
|
||||
) {
|
||||
try {
|
||||
console.log(`尝试获取配置 ${itemData.uid} 的下次更新时间`);
|
||||
|
||||
@@ -97,7 +100,7 @@ export const ProfileItem = (props: Props) => {
|
||||
setNextUpdateTime(t("Last Update failed"));
|
||||
} else {
|
||||
// 否则显示剩余时间
|
||||
const diffMinutes = nextUpdateDate.diff(now, 'minute');
|
||||
const diffMinutes = nextUpdateDate.diff(now, "minute");
|
||||
|
||||
if (diffMinutes < 60) {
|
||||
if (diffMinutes <= 0) {
|
||||
@@ -159,11 +162,17 @@ export const ProfileItem = (props: Props) => {
|
||||
};
|
||||
|
||||
// 只注册定时器更新事件监听
|
||||
window.addEventListener('verge://timer-updated', handleTimerUpdate as EventListener);
|
||||
window.addEventListener(
|
||||
"verge://timer-updated",
|
||||
handleTimerUpdate as EventListener,
|
||||
);
|
||||
|
||||
return () => {
|
||||
// 清理事件监听
|
||||
window.removeEventListener('verge://timer-updated', handleTimerUpdate as EventListener);
|
||||
window.removeEventListener(
|
||||
"verge://timer-updated",
|
||||
handleTimerUpdate as EventListener,
|
||||
);
|
||||
};
|
||||
}, [showNextUpdate, itemData.uid]);
|
||||
|
||||
@@ -271,7 +280,7 @@ export const ProfileItem = (props: Props) => {
|
||||
try {
|
||||
await viewProfile(itemData.uid);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err?.message || err.toString());
|
||||
showNotice("error", err?.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -302,7 +311,7 @@ export const ProfileItem = (props: Props) => {
|
||||
await updateProfile(itemData.uid, option);
|
||||
|
||||
// 更新成功,刷新列表
|
||||
showNotice('success', t("Update subscription successfully"));
|
||||
showNotice("success", t("Update subscription successfully"));
|
||||
mutate("getProfiles");
|
||||
} catch (err: any) {
|
||||
// 更新完全失败(包括后端的回退尝试)
|
||||
@@ -421,13 +430,25 @@ export const ProfileItem = (props: Props) => {
|
||||
};
|
||||
|
||||
// 注册事件监听
|
||||
window.addEventListener('profile-update-started', handleUpdateStarted as EventListener);
|
||||
window.addEventListener('profile-update-completed', handleUpdateCompleted as EventListener);
|
||||
window.addEventListener(
|
||||
"profile-update-started",
|
||||
handleUpdateStarted as EventListener,
|
||||
);
|
||||
window.addEventListener(
|
||||
"profile-update-completed",
|
||||
handleUpdateCompleted as EventListener,
|
||||
);
|
||||
|
||||
return () => {
|
||||
// 清理事件监听
|
||||
window.removeEventListener('profile-update-started', handleUpdateStarted as EventListener);
|
||||
window.removeEventListener('profile-update-completed', handleUpdateCompleted as EventListener);
|
||||
window.removeEventListener(
|
||||
"profile-update-started",
|
||||
handleUpdateStarted as EventListener,
|
||||
);
|
||||
window.removeEventListener(
|
||||
"profile-update-completed",
|
||||
handleUpdateCompleted as EventListener,
|
||||
);
|
||||
};
|
||||
}, [itemData.uid, showNextUpdate]);
|
||||
|
||||
@@ -541,13 +562,23 @@ export const ProfileItem = (props: Props) => {
|
||||
)
|
||||
)}
|
||||
{hasUrl && (
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end", ml: "auto" }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
ml: "auto",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
noWrap
|
||||
component="span"
|
||||
fontSize={14}
|
||||
textAlign="right"
|
||||
title={showNextUpdate ? t("Click to show last update time") : `${t("Update Time")}: ${parseExpire(updated)}\n${t("Click to show next update")}`}
|
||||
title={
|
||||
showNextUpdate
|
||||
? t("Click to show last update time")
|
||||
: `${t("Update Time")}: ${parseExpire(updated)}\n${t("Click to show next update")}`
|
||||
}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
@@ -556,13 +587,15 @@ export const ProfileItem = (props: Props) => {
|
||||
"&:hover": {
|
||||
borderBottomColor: "primary.main",
|
||||
color: "primary.main",
|
||||
}
|
||||
},
|
||||
}}
|
||||
onClick={toggleUpdateTimeDisplay}
|
||||
>
|
||||
{showNextUpdate
|
||||
? nextUpdateTime
|
||||
: (updated > 0 ? dayjs(updated * 1000).fromNow() : "")}
|
||||
: updated > 0
|
||||
? dayjs(updated * 1000).fromNow()
|
||||
: ""}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const ProfileMore = (props: Props) => {
|
||||
try {
|
||||
await viewProfile(id);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err?.message || err.toString());
|
||||
showNotice("error", err?.message || err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
||||
setOpen(false);
|
||||
fileDataRef.current = null;
|
||||
setTimeout(() => formIns.reset(), 500);
|
||||
} catch { }
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const text = {
|
||||
|
||||
@@ -66,27 +66,27 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
|
||||
const filteredPrependSeq = useMemo(
|
||||
() => prependSeq.filter((proxy) => match(proxy.name)),
|
||||
[prependSeq, match]
|
||||
[prependSeq, match],
|
||||
);
|
||||
const filteredProxyList = useMemo(
|
||||
() => proxyList.filter((proxy) => match(proxy.name)),
|
||||
[proxyList, match]
|
||||
[proxyList, match],
|
||||
);
|
||||
const filteredAppendSeq = useMemo(
|
||||
() => appendSeq.filter((proxy) => match(proxy.name)),
|
||||
[appendSeq, match]
|
||||
[appendSeq, match],
|
||||
);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
}),
|
||||
);
|
||||
const reorder = (
|
||||
list: IProxyConfig[],
|
||||
startIndex: number,
|
||||
endIndex: number
|
||||
endIndex: number,
|
||||
) => {
|
||||
const result = Array.from(list);
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
@@ -208,8 +208,8 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
setCurrData(
|
||||
yaml.dump(
|
||||
{ prepend: prependSeq, append: appendSeq, delete: deleteSeq },
|
||||
{ forceQuotes: true }
|
||||
)
|
||||
{ forceQuotes: true },
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
// 防止异常导致UI卡死
|
||||
@@ -232,11 +232,11 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
const handleSave = useLockFn(async () => {
|
||||
try {
|
||||
await saveProfileFile(property, currData);
|
||||
showNotice('success', t("Saved Successfully"));
|
||||
showNotice("success", t("Saved Successfully"));
|
||||
onSave?.(prevData, currData);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -358,8 +358,8 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
onDelete={() => {
|
||||
setPrependSeq(
|
||||
prependSeq.filter(
|
||||
(v) => v.name !== item.name
|
||||
)
|
||||
(v) => v.name !== item.name,
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -385,8 +385,8 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
) {
|
||||
setDeleteSeq(
|
||||
deleteSeq.filter(
|
||||
(v) => v !== filteredProxyList[newIndex].name
|
||||
)
|
||||
(v) => v !== filteredProxyList[newIndex].name,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
setDeleteSeq((prev) => [
|
||||
@@ -418,8 +418,8 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
onDelete={() => {
|
||||
setAppendSeq(
|
||||
appendSeq.filter(
|
||||
(v) => v.name !== item.name
|
||||
)
|
||||
(v) => v.name !== item.name,
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -453,8 +453,9 @@ export const ProxiesEditorViewer = (props: Props) => {
|
||||
padding: {
|
||||
top: 33, // 顶部padding防止遮挡snippets
|
||||
},
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${
|
||||
getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontLigatures: false, // 连字符
|
||||
smoothScrolling: true, // 平滑滚动
|
||||
}}
|
||||
|
||||
@@ -49,8 +49,8 @@ export const ProxyItem = (props: Props) => {
|
||||
? alpha(palette.background.paper, 0.3)
|
||||
: alpha(palette.grey[400], 0.3)
|
||||
: type === "delete"
|
||||
? alpha(palette.error.main, 0.3)
|
||||
: alpha(palette.success.main, 0.3),
|
||||
? alpha(palette.error.main, 0.3)
|
||||
: alpha(palette.success.main, 0.3),
|
||||
height: "100%",
|
||||
margin: "8px 0",
|
||||
borderRadius: "8px",
|
||||
|
||||
@@ -52,8 +52,8 @@ export const RuleItem = (props: Props) => {
|
||||
? alpha(palette.background.paper, 0.3)
|
||||
: alpha(palette.grey[400], 0.3)
|
||||
: type === "delete"
|
||||
? alpha(palette.error.main, 0.3)
|
||||
: alpha(palette.success.main, 0.3),
|
||||
? alpha(palette.error.main, 0.3)
|
||||
: alpha(palette.success.main, 0.3),
|
||||
height: "100%",
|
||||
margin: "8px 0",
|
||||
borderRadius: "8px",
|
||||
|
||||
@@ -55,17 +55,17 @@ interface Props {
|
||||
|
||||
const portValidator = (value: string): boolean => {
|
||||
return new RegExp(
|
||||
"^(?:[1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$"
|
||||
"^(?:[1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$",
|
||||
).test(value);
|
||||
};
|
||||
const ipv4CIDRValidator = (value: string): boolean => {
|
||||
return new RegExp(
|
||||
"^(?:(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))\\.){3}(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))(?:\\/(?:[12]?[0-9]|3[0-2]))$"
|
||||
"^(?:(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))\\.){3}(?:[1-9]?[0-9]|1[0-9][0-9]|2(?:[0-4][0-9]|5[0-5]))(?:\\/(?:[12]?[0-9]|3[0-2]))$",
|
||||
).test(value);
|
||||
};
|
||||
const ipv6CIDRValidator = (value: string): boolean => {
|
||||
return new RegExp(
|
||||
"^([0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){7}|::|:(?::[0-9a-fA-F]{1,4}){1,6}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,5}|(?:[0-9a-fA-F]{1,4}:){2}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){3}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){4}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){5}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,6}:)\\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$"
|
||||
"^([0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){7}|::|:(?::[0-9a-fA-F]{1,4}){1,6}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,5}|(?:[0-9a-fA-F]{1,4}:){2}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){3}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){4}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){5}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,6}:)\\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$",
|
||||
).test(value);
|
||||
};
|
||||
|
||||
@@ -76,161 +76,161 @@ const rules: {
|
||||
noResolve?: boolean;
|
||||
validator?: (value: string) => boolean;
|
||||
}[] = [
|
||||
{
|
||||
name: "DOMAIN",
|
||||
example: "example.com",
|
||||
},
|
||||
{
|
||||
name: "DOMAIN-SUFFIX",
|
||||
example: "example.com",
|
||||
},
|
||||
{
|
||||
name: "DOMAIN-KEYWORD",
|
||||
example: "example",
|
||||
},
|
||||
{
|
||||
name: "DOMAIN-REGEX",
|
||||
example: "example.*",
|
||||
},
|
||||
{
|
||||
name: "GEOSITE",
|
||||
example: "youtube",
|
||||
},
|
||||
{
|
||||
name: "GEOIP",
|
||||
example: "CN",
|
||||
noResolve: true,
|
||||
},
|
||||
{
|
||||
name: "SRC-GEOIP",
|
||||
example: "CN",
|
||||
},
|
||||
{
|
||||
name: "IP-ASN",
|
||||
example: "13335",
|
||||
noResolve: true,
|
||||
validator: (value) => (+value ? true : false),
|
||||
},
|
||||
{
|
||||
name: "SRC-IP-ASN",
|
||||
example: "9808",
|
||||
validator: (value) => (+value ? true : false),
|
||||
},
|
||||
{
|
||||
name: "IP-CIDR",
|
||||
example: "127.0.0.0/8",
|
||||
noResolve: true,
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "IP-CIDR6",
|
||||
example: "2620:0:2d0:200::7/32",
|
||||
noResolve: true,
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "SRC-IP-CIDR",
|
||||
example: "192.168.1.201/32",
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "IP-SUFFIX",
|
||||
example: "8.8.8.8/24",
|
||||
noResolve: true,
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "SRC-IP-SUFFIX",
|
||||
example: "192.168.1.201/8",
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "SRC-PORT",
|
||||
example: "7777",
|
||||
validator: (value) => portValidator(value),
|
||||
},
|
||||
{
|
||||
name: "DST-PORT",
|
||||
example: "80",
|
||||
validator: (value) => portValidator(value),
|
||||
},
|
||||
{
|
||||
name: "IN-PORT",
|
||||
example: "7890",
|
||||
validator: (value) => portValidator(value),
|
||||
},
|
||||
{
|
||||
name: "DSCP",
|
||||
example: "4",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-NAME",
|
||||
example: getSystem() === "windows" ? "chrome.exe" : "curl",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-PATH",
|
||||
example:
|
||||
getSystem() === "windows"
|
||||
? "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
||||
: "/usr/bin/wget",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-NAME-REGEX",
|
||||
example: ".*telegram.*",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-PATH-REGEX",
|
||||
example:
|
||||
getSystem() === "windows" ? "(?i).*Application\\chrome.*" : ".*bin/wget",
|
||||
},
|
||||
{
|
||||
name: "NETWORK",
|
||||
example: "udp",
|
||||
validator: (value) => ["tcp", "udp"].includes(value),
|
||||
},
|
||||
{
|
||||
name: "UID",
|
||||
example: "1001",
|
||||
validator: (value) => (+value ? true : false),
|
||||
},
|
||||
{
|
||||
name: "IN-TYPE",
|
||||
example: "SOCKS/HTTP",
|
||||
},
|
||||
{
|
||||
name: "IN-USER",
|
||||
example: "mihomo",
|
||||
},
|
||||
{
|
||||
name: "IN-NAME",
|
||||
example: "ss",
|
||||
},
|
||||
{
|
||||
name: "SUB-RULE",
|
||||
example: "(NETWORK,tcp)",
|
||||
},
|
||||
{
|
||||
name: "RULE-SET",
|
||||
example: "providername",
|
||||
noResolve: true,
|
||||
},
|
||||
{
|
||||
name: "AND",
|
||||
example: "((DOMAIN,baidu.com),(NETWORK,UDP))",
|
||||
},
|
||||
{
|
||||
name: "OR",
|
||||
example: "((NETWORK,UDP),(DOMAIN,baidu.com))",
|
||||
},
|
||||
{
|
||||
name: "NOT",
|
||||
example: "((DOMAIN,baidu.com))",
|
||||
},
|
||||
{
|
||||
name: "MATCH",
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
{
|
||||
name: "DOMAIN",
|
||||
example: "example.com",
|
||||
},
|
||||
{
|
||||
name: "DOMAIN-SUFFIX",
|
||||
example: "example.com",
|
||||
},
|
||||
{
|
||||
name: "DOMAIN-KEYWORD",
|
||||
example: "example",
|
||||
},
|
||||
{
|
||||
name: "DOMAIN-REGEX",
|
||||
example: "example.*",
|
||||
},
|
||||
{
|
||||
name: "GEOSITE",
|
||||
example: "youtube",
|
||||
},
|
||||
{
|
||||
name: "GEOIP",
|
||||
example: "CN",
|
||||
noResolve: true,
|
||||
},
|
||||
{
|
||||
name: "SRC-GEOIP",
|
||||
example: "CN",
|
||||
},
|
||||
{
|
||||
name: "IP-ASN",
|
||||
example: "13335",
|
||||
noResolve: true,
|
||||
validator: (value) => (+value ? true : false),
|
||||
},
|
||||
{
|
||||
name: "SRC-IP-ASN",
|
||||
example: "9808",
|
||||
validator: (value) => (+value ? true : false),
|
||||
},
|
||||
{
|
||||
name: "IP-CIDR",
|
||||
example: "127.0.0.0/8",
|
||||
noResolve: true,
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "IP-CIDR6",
|
||||
example: "2620:0:2d0:200::7/32",
|
||||
noResolve: true,
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "SRC-IP-CIDR",
|
||||
example: "192.168.1.201/32",
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "IP-SUFFIX",
|
||||
example: "8.8.8.8/24",
|
||||
noResolve: true,
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "SRC-IP-SUFFIX",
|
||||
example: "192.168.1.201/8",
|
||||
validator: (value) => ipv4CIDRValidator(value) || ipv6CIDRValidator(value),
|
||||
},
|
||||
{
|
||||
name: "SRC-PORT",
|
||||
example: "7777",
|
||||
validator: (value) => portValidator(value),
|
||||
},
|
||||
{
|
||||
name: "DST-PORT",
|
||||
example: "80",
|
||||
validator: (value) => portValidator(value),
|
||||
},
|
||||
{
|
||||
name: "IN-PORT",
|
||||
example: "7890",
|
||||
validator: (value) => portValidator(value),
|
||||
},
|
||||
{
|
||||
name: "DSCP",
|
||||
example: "4",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-NAME",
|
||||
example: getSystem() === "windows" ? "chrome.exe" : "curl",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-PATH",
|
||||
example:
|
||||
getSystem() === "windows"
|
||||
? "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
|
||||
: "/usr/bin/wget",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-NAME-REGEX",
|
||||
example: ".*telegram.*",
|
||||
},
|
||||
{
|
||||
name: "PROCESS-PATH-REGEX",
|
||||
example:
|
||||
getSystem() === "windows" ? "(?i).*Application\\chrome.*" : ".*bin/wget",
|
||||
},
|
||||
{
|
||||
name: "NETWORK",
|
||||
example: "udp",
|
||||
validator: (value) => ["tcp", "udp"].includes(value),
|
||||
},
|
||||
{
|
||||
name: "UID",
|
||||
example: "1001",
|
||||
validator: (value) => (+value ? true : false),
|
||||
},
|
||||
{
|
||||
name: "IN-TYPE",
|
||||
example: "SOCKS/HTTP",
|
||||
},
|
||||
{
|
||||
name: "IN-USER",
|
||||
example: "mihomo",
|
||||
},
|
||||
{
|
||||
name: "IN-NAME",
|
||||
example: "ss",
|
||||
},
|
||||
{
|
||||
name: "SUB-RULE",
|
||||
example: "(NETWORK,tcp)",
|
||||
},
|
||||
{
|
||||
name: "RULE-SET",
|
||||
example: "providername",
|
||||
noResolve: true,
|
||||
},
|
||||
{
|
||||
name: "AND",
|
||||
example: "((DOMAIN,baidu.com),(NETWORK,UDP))",
|
||||
},
|
||||
{
|
||||
name: "OR",
|
||||
example: "((NETWORK,UDP),(DOMAIN,baidu.com))",
|
||||
},
|
||||
{
|
||||
name: "NOT",
|
||||
example: "((DOMAIN,baidu.com))",
|
||||
},
|
||||
{
|
||||
name: "MATCH",
|
||||
required: false,
|
||||
},
|
||||
];
|
||||
|
||||
const builtinProxyPolicies = ["DIRECT", "REJECT", "REJECT-DROP", "PASS"];
|
||||
|
||||
@@ -260,22 +260,22 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
|
||||
const filteredPrependSeq = useMemo(
|
||||
() => prependSeq.filter((rule) => match(rule)),
|
||||
[prependSeq, match]
|
||||
[prependSeq, match],
|
||||
);
|
||||
const filteredRuleList = useMemo(
|
||||
() => ruleList.filter((rule) => match(rule)),
|
||||
[ruleList, match]
|
||||
[ruleList, match],
|
||||
);
|
||||
const filteredAppendSeq = useMemo(
|
||||
() => appendSeq.filter((rule) => match(rule)),
|
||||
[appendSeq, match]
|
||||
[appendSeq, match],
|
||||
);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
})
|
||||
}),
|
||||
);
|
||||
const reorder = (list: string[], startIndex: number, endIndex: number) => {
|
||||
const result = Array.from(list);
|
||||
@@ -333,11 +333,11 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
setCurrData(
|
||||
yaml.dump(
|
||||
{ prepend: prependSeq, append: appendSeq, delete: deleteSeq },
|
||||
{ forceQuotes: true }
|
||||
)
|
||||
{ forceQuotes: true },
|
||||
),
|
||||
);
|
||||
} catch (e: any) {
|
||||
showNotice('error', e?.message || e?.toString() || 'YAML dump error');
|
||||
showNotice("error", e?.message || e?.toString() || "YAML dump error");
|
||||
}
|
||||
};
|
||||
if (window.requestIdleCallback) {
|
||||
@@ -371,7 +371,7 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
return !moreDeleteGroups.includes(group);
|
||||
}
|
||||
}),
|
||||
moreAppendGroups
|
||||
moreAppendGroups,
|
||||
);
|
||||
|
||||
let originRuleSetObj = yaml.load(data) as { "rule-providers": {} } | null;
|
||||
@@ -396,7 +396,7 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
let globalSubRule = globalSubRuleObj?.["sub-rules"] || {};
|
||||
let subRule = Object.assign({}, originSubRule, moreSubRule, globalSubRule);
|
||||
setProxyPolicyList(
|
||||
builtinProxyPolicies.concat(groups.map((group: any) => group.name))
|
||||
builtinProxyPolicies.concat(groups.map((group: any) => group.name)),
|
||||
);
|
||||
setRuleSetList(Object.keys(ruleSet));
|
||||
setSubRuleList(Object.keys(subRule));
|
||||
@@ -417,19 +417,20 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
throw new Error(t("Invalid Rule"));
|
||||
}
|
||||
|
||||
const condition = ruleType.required ?? true ? ruleContent : "";
|
||||
return `${ruleType.name}${condition ? "," + condition : ""},${proxyPolicy}${ruleType.noResolve && noResolve ? ",no-resolve" : ""
|
||||
}`;
|
||||
const condition = (ruleType.required ?? true) ? ruleContent : "";
|
||||
return `${ruleType.name}${condition ? "," + condition : ""},${proxyPolicy}${
|
||||
ruleType.noResolve && noResolve ? ",no-resolve" : ""
|
||||
}`;
|
||||
};
|
||||
|
||||
const handleSave = useLockFn(async () => {
|
||||
try {
|
||||
await saveProfileFile(property, currData);
|
||||
showNotice('success', t("Saved Successfully"));
|
||||
showNotice("success", t("Saved Successfully"));
|
||||
onSave?.(prevData, currData);
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.toString());
|
||||
showNotice("error", err.toString());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -557,7 +558,7 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
if (prependSeq.includes(raw)) return;
|
||||
setPrependSeq([raw, ...prependSeq]);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -575,7 +576,7 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
if (appendSeq.includes(raw)) return;
|
||||
setAppendSeq([...appendSeq, raw]);
|
||||
} catch (err: any) {
|
||||
showNotice('error', err.message || err.toString());
|
||||
showNotice("error", err.message || err.toString());
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -621,7 +622,7 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
ruleRaw={item}
|
||||
onDelete={() => {
|
||||
setPrependSeq(
|
||||
prependSeq.filter((v) => v !== item)
|
||||
prependSeq.filter((v) => v !== item),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -645,8 +646,8 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
if (deleteSeq.includes(filteredRuleList[newIndex])) {
|
||||
setDeleteSeq(
|
||||
deleteSeq.filter(
|
||||
(v) => v !== filteredRuleList[newIndex]
|
||||
)
|
||||
(v) => v !== filteredRuleList[newIndex],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
setDeleteSeq((prev) => [
|
||||
@@ -677,7 +678,7 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
ruleRaw={item}
|
||||
onDelete={() => {
|
||||
setAppendSeq(
|
||||
appendSeq.filter((v) => v !== item)
|
||||
appendSeq.filter((v) => v !== item),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -711,8 +712,9 @@ export const RulesEditorViewer = (props: Props) => {
|
||||
padding: {
|
||||
top: 33, // 顶部padding防止遮挡snippets
|
||||
},
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${
|
||||
getSystem() === "windows" ? ", twemoji mozilla" : ""
|
||||
}`,
|
||||
fontLigatures: false, // 连字符
|
||||
smoothScrolling: true, // 平滑滚动
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user