mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
chore(deps): update npm dependencies (#4939)
* chore(deps): update npm dependencies * Refactor components to use function syntax instead of forwardRef for better type handling and clarity. Updated imports and adjusted prop types accordingly across multiple viewer components including TrafficGraph, ProfileViewer, BackupViewer, ClashCoreViewer, ControllerViewer, DnsViewer, LiteModeViewer, NetworkInterfaceViewer, ThemeViewer, TunViewer, UpdateViewer, and WebUIViewer. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Tunglies <77394545+Tunglies@users.noreply.github.com>
This commit is contained in:
38
package.json
38
package.json
@@ -36,10 +36,10 @@
|
|||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"@mui/icons-material": "^7.3.2",
|
"@mui/icons-material": "^7.3.4",
|
||||||
"@mui/lab": "7.0.0-beta.17",
|
"@mui/lab": "7.0.0-beta.17",
|
||||||
"@mui/material": "^7.3.2",
|
"@mui/material": "^7.3.4",
|
||||||
"@mui/x-data-grid": "^8.12.1",
|
"@mui/x-data-grid": "^8.13.1",
|
||||||
"@tauri-apps/api": "2.8.0",
|
"@tauri-apps/api": "2.8.0",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
|
"@tauri-apps/plugin-clipboard-manager": "^2.3.0",
|
||||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||||
@@ -53,17 +53,17 @@
|
|||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"dayjs": "1.11.18",
|
"dayjs": "1.11.18",
|
||||||
"foxact": "^0.2.49",
|
"foxact": "^0.2.49",
|
||||||
"i18next": "^25.5.2",
|
"i18next": "^25.5.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json-schema": "^0.4.0",
|
"json-schema": "^0.4.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"monaco-editor": "^0.53.0",
|
"monaco-editor": "^0.53.0",
|
||||||
"monaco-yaml": "^5.4.0",
|
"monaco-yaml": "^5.4.0",
|
||||||
"nanoid": "^5.1.6",
|
"nanoid": "^5.1.6",
|
||||||
"react": "19.1.1",
|
"react": "19.2.0",
|
||||||
"react-dom": "19.1.1",
|
"react-dom": "19.2.0",
|
||||||
"react-error-boundary": "6.0.0",
|
"react-error-boundary": "6.0.0",
|
||||||
"react-hook-form": "^7.63.0",
|
"react-hook-form": "^7.64.0",
|
||||||
"react-i18next": "16.0.0",
|
"react-i18next": "16.0.0",
|
||||||
"react-markdown": "10.1.0",
|
"react-markdown": "10.1.0",
|
||||||
"react-monaco-editor": "0.59.0",
|
"react-monaco-editor": "0.59.0",
|
||||||
@@ -75,40 +75,40 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/github": "^6.0.1",
|
"@actions/github": "^6.0.1",
|
||||||
"@eslint-react/eslint-plugin": "^2.0.1",
|
"@eslint-react/eslint-plugin": "^2.0.6",
|
||||||
"@eslint/js": "^9.36.0",
|
"@eslint/js": "^9.37.0",
|
||||||
"@tauri-apps/cli": "2.8.4",
|
"@tauri-apps/cli": "2.8.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/react": "19.1.15",
|
"@types/react": "19.2.0",
|
||||||
"@types/react-dom": "19.1.9",
|
"@types/react-dom": "19.2.0",
|
||||||
"@vitejs/plugin-legacy": "^7.2.1",
|
"@vitejs/plugin-legacy": "^7.2.1",
|
||||||
"@vitejs/plugin-react": "5.0.4",
|
"@vitejs/plugin-react": "5.0.4",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"cli-color": "^2.0.4",
|
"cli-color": "^2.0.4",
|
||||||
"commander": "^14.0.1",
|
"commander": "^14.0.1",
|
||||||
"cross-env": "^10.0.0",
|
"cross-env": "^10.1.0",
|
||||||
"eslint": "^9.36.0",
|
"eslint": "^9.37.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-plugin-import-x": "^4.16.1",
|
"eslint-plugin-import-x": "^4.16.1",
|
||||||
"eslint-plugin-prettier": "^5.5.4",
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
"eslint-plugin-react-hooks": "^6.1.1",
|
"eslint-plugin-react-hooks": "^6.1.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.22",
|
"eslint-plugin-react-refresh": "^0.4.23",
|
||||||
"eslint-plugin-unused-imports": "^4.2.0",
|
"eslint-plugin-unused-imports": "^4.2.0",
|
||||||
"glob": "^11.0.3",
|
"glob": "^11.0.3",
|
||||||
"globals": "^16.4.0",
|
"globals": "^16.4.0",
|
||||||
"https-proxy-agent": "^7.0.6",
|
"https-proxy-agent": "^7.0.6",
|
||||||
"jiti": "^2.6.0",
|
"jiti": "^2.6.1",
|
||||||
"meta-json-schema": "^1.19.13",
|
"meta-json-schema": "^1.19.14",
|
||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"prettier": "^3.6.2",
|
"prettier": "^3.6.2",
|
||||||
"sass": "^1.93.2",
|
"sass": "^1.93.2",
|
||||||
"tar": "^7.5.1",
|
"tar": "^7.5.1",
|
||||||
"terser": "^5.44.0",
|
"terser": "^5.44.0",
|
||||||
"typescript": "^5.9.2",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.44.1",
|
"typescript-eslint": "^8.45.0",
|
||||||
"vite": "^7.1.7",
|
"vite": "^7.1.9",
|
||||||
"vite-plugin-monaco-editor": "^1.1.0",
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vite-plugin-svgr": "^4.5.0"
|
"vite-plugin-svgr": "^4.5.0"
|
||||||
},
|
},
|
||||||
|
|||||||
1181
pnpm-lock.yaml
generated
1181
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ import { Box, Button, Snackbar, useTheme } from "@mui/material";
|
|||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import { useImperativeHandle, useState, type Ref } from "react";
|
||||||
|
|
||||||
import { deleteConnection } from "@/services/cmds";
|
import { deleteConnection } from "@/services/cmds";
|
||||||
import parseTraffic from "@/utils/parse-traffic";
|
import parseTraffic from "@/utils/parse-traffic";
|
||||||
@@ -11,45 +11,43 @@ export interface ConnectionDetailRef {
|
|||||||
open: (detail: IConnectionsItem) => void;
|
open: (detail: IConnectionsItem) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConnectionDetail = forwardRef<ConnectionDetailRef>(
|
export function ConnectionDetail({ ref }: { ref?: Ref<ConnectionDetailRef> }) {
|
||||||
(props, ref) => {
|
const [open, setOpen] = useState(false);
|
||||||
const [open, setOpen] = useState(false);
|
const [detail, setDetail] = useState<IConnectionsItem>(null!);
|
||||||
const [detail, setDetail] = useState<IConnectionsItem>(null!);
|
const theme = useTheme();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
open: (detail: IConnectionsItem) => {
|
open: (detail: IConnectionsItem) => {
|
||||||
if (open) return;
|
if (open) return;
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
setDetail(detail);
|
setDetail(detail);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const onClose = () => setOpen(false);
|
const onClose = () => setOpen(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Snackbar
|
<Snackbar
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
sx={{
|
sx={{
|
||||||
".MuiSnackbarContent-root": {
|
".MuiSnackbarContent-root": {
|
||||||
maxWidth: "520px",
|
maxWidth: "520px",
|
||||||
maxHeight: "480px",
|
maxHeight: "480px",
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
message={
|
message={
|
||||||
detail ? (
|
detail ? (
|
||||||
<InnerConnectionDetail data={detail} onClose={onClose} />
|
<InnerConnectionDetail data={detail} onClose={onClose} />
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
interface InnerProps {
|
interface InnerProps {
|
||||||
data: IConnectionsItem;
|
data: IConnectionsItem;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Box, useTheme } from "@mui/material";
|
import { Box, useTheme } from "@mui/material";
|
||||||
|
import type { Ref } from "react";
|
||||||
import {
|
import {
|
||||||
forwardRef,
|
memo,
|
||||||
useImperativeHandle,
|
|
||||||
useState,
|
|
||||||
useEffect,
|
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
memo,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@@ -44,7 +44,6 @@ interface TooltipData {
|
|||||||
highlightY: number; // 高亮Y轴位置
|
highlightY: number; // 高亮Y轴位置
|
||||||
}
|
}
|
||||||
|
|
||||||
// Canvas图表配置
|
|
||||||
const MAX_POINTS = 300;
|
const MAX_POINTS = 300;
|
||||||
const TARGET_FPS = 15; // 降低帧率减少闪烁
|
const TARGET_FPS = 15; // 降低帧率减少闪烁
|
||||||
const LINE_WIDTH_UP = 2.5;
|
const LINE_WIDTH_UP = 2.5;
|
||||||
@@ -77,12 +76,18 @@ const GRAPH_CONFIG = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface EnhancedCanvasTrafficGraphProps {
|
||||||
|
ref?: Ref<EnhancedCanvasTrafficGraphRef>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 稳定版Canvas流量图表组件
|
* 稳定版Canvas流量图表组件
|
||||||
* 修复闪烁问题,添加时间轴显示
|
* 修复闪烁问题,添加时间轴显示
|
||||||
*/
|
*/
|
||||||
export const EnhancedCanvasTrafficGraph = memo(
|
export const EnhancedCanvasTrafficGraph = memo(
|
||||||
forwardRef<EnhancedCanvasTrafficGraphRef>((props, ref) => {
|
function EnhancedCanvasTrafficGraph({
|
||||||
|
ref,
|
||||||
|
}: EnhancedCanvasTrafficGraphProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -999,7 +1004,7 @@ export const EnhancedCanvasTrafficGraph = memo(
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
EnhancedCanvasTrafficGraph.displayName = "EnhancedCanvasTrafficGraph";
|
EnhancedCanvasTrafficGraph.displayName = "EnhancedCanvasTrafficGraph";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useTheme } from "@mui/material";
|
import { useTheme } from "@mui/material";
|
||||||
import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
|
import { useEffect, useImperativeHandle, useRef, type Ref } from "react";
|
||||||
|
|
||||||
const maxPoint = 30;
|
const maxPoint = 30;
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export interface TrafficRef {
|
|||||||
/**
|
/**
|
||||||
* draw the traffic graph
|
* draw the traffic graph
|
||||||
*/
|
*/
|
||||||
export const TrafficGraph = forwardRef<TrafficRef>((props, ref) => {
|
export function TrafficGraph({ ref }: { ref?: Ref<TrafficRef> }) {
|
||||||
const countRef = useRef(0);
|
const countRef = useRef(0);
|
||||||
const styleRef = useRef(true);
|
const styleRef = useRef(true);
|
||||||
const listRef = useRef<TrafficData[]>(defaultList);
|
const listRef = useRef<TrafficData[]>(defaultList);
|
||||||
@@ -196,4 +196,4 @@ export const TrafficGraph = forwardRef<TrafficRef>((props, ref) => {
|
|||||||
}, [palette]);
|
}, [palette]);
|
||||||
|
|
||||||
return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
|
return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -9,14 +9,9 @@ import {
|
|||||||
TextField,
|
TextField,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import {
|
import type { Ref } from "react";
|
||||||
forwardRef,
|
import { useEffect, useImperativeHandle, useRef, useState } from "react";
|
||||||
useEffect,
|
import { Controller, useForm } from "react-hook-form";
|
||||||
useImperativeHandle,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { useForm, Controller } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, Switch } from "@/components/base";
|
import { BaseDialog, Switch } from "@/components/base";
|
||||||
@@ -38,304 +33,279 @@ export interface ProfileViewerRef {
|
|||||||
|
|
||||||
// create or edit the profile
|
// create or edit the profile
|
||||||
// remote / local
|
// remote / local
|
||||||
export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
type ProfileViewerProps = Props & { ref?: Ref<ProfileViewerRef> };
|
||||||
(props, ref) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [open, setOpen] = useState(false);
|
|
||||||
const [openType, setOpenType] = useState<"new" | "edit">("new");
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const { profiles } = useProfiles();
|
|
||||||
|
|
||||||
// file input
|
export function ProfileViewer({ onChange, ref }: ProfileViewerProps) {
|
||||||
const fileDataRef = useRef<string | null>(null);
|
const { t } = useTranslation();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [openType, setOpenType] = useState<"new" | "edit">("new");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const { profiles } = useProfiles();
|
||||||
|
|
||||||
const {
|
// file input
|
||||||
control,
|
const fileDataRef = useRef<string | null>(null);
|
||||||
watch,
|
|
||||||
register: _register,
|
const {
|
||||||
...formIns
|
control,
|
||||||
} = useForm<IProfileItem>({
|
watch,
|
||||||
defaultValues: {
|
register: _register,
|
||||||
type: "remote",
|
...formIns
|
||||||
name: "",
|
} = useForm<IProfileItem>({
|
||||||
desc: "",
|
defaultValues: {
|
||||||
url: "",
|
type: "remote",
|
||||||
option: {
|
name: "",
|
||||||
with_proxy: false,
|
desc: "",
|
||||||
self_proxy: false,
|
url: "",
|
||||||
},
|
option: {
|
||||||
|
with_proxy: false,
|
||||||
|
self_proxy: false,
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
create: () => {
|
create: () => {
|
||||||
setOpenType("new");
|
setOpenType("new");
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
},
|
},
|
||||||
edit: (item) => {
|
edit: (item: IProfileItem) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
Object.entries(item).forEach(([key, value]) => {
|
Object.entries(item).forEach(([key, value]) => {
|
||||||
formIns.setValue(key as any, value);
|
formIns.setValue(key as any, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setOpenType("edit");
|
setOpenType("edit");
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const selfProxy = watch("option.self_proxy");
|
const selfProxy = watch("option.self_proxy");
|
||||||
const withProxy = watch("option.with_proxy");
|
const withProxy = watch("option.with_proxy");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selfProxy) formIns.setValue("option.with_proxy", false);
|
if (selfProxy) formIns.setValue("option.with_proxy", false);
|
||||||
}, [selfProxy]);
|
}, [selfProxy]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (withProxy) formIns.setValue("option.self_proxy", false);
|
if (withProxy) formIns.setValue("option.self_proxy", false);
|
||||||
}, [withProxy]);
|
}, [withProxy]);
|
||||||
|
|
||||||
const handleOk = useLockFn(
|
const handleOk = useLockFn(
|
||||||
formIns.handleSubmit(async (form) => {
|
formIns.handleSubmit(async (form) => {
|
||||||
if (form.option?.timeout_seconds) {
|
if (form.option?.timeout_seconds) {
|
||||||
form.option.timeout_seconds = +form.option.timeout_seconds;
|
form.option.timeout_seconds = +form.option.timeout_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 基本验证
|
||||||
|
if (!form.type) throw new Error("`Type` should not be null");
|
||||||
|
if (form.type === "remote" && !form.url) {
|
||||||
|
throw new Error("The URL should not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
// 处理表单数据
|
||||||
try {
|
if (form.option?.update_interval) {
|
||||||
// 基本验证
|
form.option.update_interval = +form.option.update_interval;
|
||||||
if (!form.type) throw new Error("`Type` should not be null");
|
} else {
|
||||||
if (form.type === "remote" && !form.url) {
|
delete form.option?.update_interval;
|
||||||
throw new Error("The URL should not be null");
|
}
|
||||||
}
|
if (form.option?.user_agent === "") {
|
||||||
|
delete form.option.user_agent;
|
||||||
|
}
|
||||||
|
|
||||||
// 处理表单数据
|
const name = form.name || `${form.type} file`;
|
||||||
if (form.option?.update_interval) {
|
const item = { ...form, name };
|
||||||
form.option.update_interval = +form.option.update_interval;
|
const isRemote = form.type === "remote";
|
||||||
|
const isUpdate = openType === "edit";
|
||||||
|
|
||||||
|
// 判断是否是当前激活的配置
|
||||||
|
const isActivating = isUpdate && form.uid === (profiles?.current ?? "");
|
||||||
|
|
||||||
|
// 保存原始代理设置以便回退成功后恢复
|
||||||
|
const originalOptions = {
|
||||||
|
with_proxy: form.option?.with_proxy,
|
||||||
|
self_proxy: form.option?.self_proxy,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 执行创建或更新操作,本地配置不需要回退机制
|
||||||
|
if (!isRemote) {
|
||||||
|
if (openType === "new") {
|
||||||
|
await createProfile(item, fileDataRef.current);
|
||||||
} else {
|
} else {
|
||||||
delete form.option?.update_interval;
|
if (!form.uid) throw new Error("UID not found");
|
||||||
|
await patchProfile(form.uid, item);
|
||||||
}
|
}
|
||||||
if (form.option?.user_agent === "") {
|
} else {
|
||||||
delete form.option.user_agent;
|
// 远程配置使用回退机制
|
||||||
}
|
try {
|
||||||
|
// 尝试正常操作
|
||||||
const name = form.name || `${form.type} file`;
|
|
||||||
const item = { ...form, name };
|
|
||||||
const isRemote = form.type === "remote";
|
|
||||||
const isUpdate = openType === "edit";
|
|
||||||
|
|
||||||
// 判断是否是当前激活的配置
|
|
||||||
const isActivating =
|
|
||||||
isUpdate && form.uid === (profiles?.current ?? "");
|
|
||||||
|
|
||||||
// 保存原始代理设置以便回退成功后恢复
|
|
||||||
const originalOptions = {
|
|
||||||
with_proxy: form.option?.with_proxy,
|
|
||||||
self_proxy: form.option?.self_proxy,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 执行创建或更新操作,本地配置不需要回退机制
|
|
||||||
if (!isRemote) {
|
|
||||||
if (openType === "new") {
|
if (openType === "new") {
|
||||||
await createProfile(item, fileDataRef.current);
|
await createProfile(item, fileDataRef.current);
|
||||||
} else {
|
} else {
|
||||||
if (!form.uid) throw new Error("UID not found");
|
if (!form.uid) throw new Error("UID not found");
|
||||||
await patchProfile(form.uid, item);
|
await patchProfile(form.uid, item);
|
||||||
}
|
}
|
||||||
} else {
|
} catch {
|
||||||
// 远程配置使用回退机制
|
// 首次创建/更新失败,尝试使用自身代理
|
||||||
try {
|
showNotice(
|
||||||
// 尝试正常操作
|
"info",
|
||||||
if (openType === "new") {
|
t("Profile creation failed, retrying with Clash proxy..."),
|
||||||
await createProfile(item, fileDataRef.current);
|
);
|
||||||
} else {
|
|
||||||
if (!form.uid) throw new Error("UID not found");
|
|
||||||
await patchProfile(form.uid, item);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// 首次创建/更新失败,尝试使用自身代理
|
|
||||||
showNotice(
|
|
||||||
"info",
|
|
||||||
t("Profile creation failed, retrying with Clash proxy..."),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 使用自身代理的配置
|
// 使用自身代理的配置
|
||||||
const retryItem = {
|
const retryItem = {
|
||||||
...item,
|
...item,
|
||||||
option: {
|
option: {
|
||||||
...item.option,
|
...item.option,
|
||||||
with_proxy: false,
|
with_proxy: false,
|
||||||
self_proxy: true,
|
self_proxy: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// 使用自身代理再次尝试
|
// 使用自身代理再次尝试
|
||||||
if (openType === "new") {
|
if (openType === "new") {
|
||||||
await createProfile(retryItem, fileDataRef.current);
|
await createProfile(retryItem, fileDataRef.current);
|
||||||
} else {
|
} else {
|
||||||
if (!form.uid) throw new Error("UID not found");
|
if (!form.uid) throw new Error("UID not found");
|
||||||
await patchProfile(form.uid, retryItem);
|
await patchProfile(form.uid, retryItem);
|
||||||
|
|
||||||
// 编辑模式下恢复原始代理设置
|
// 编辑模式下恢复原始代理设置
|
||||||
await patchProfile(form.uid, { option: originalOptions });
|
await patchProfile(form.uid, { option: originalOptions });
|
||||||
}
|
|
||||||
|
|
||||||
showNotice(
|
|
||||||
"success",
|
|
||||||
t("Profile creation succeeded with Clash proxy"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showNotice(
|
||||||
|
"success",
|
||||||
|
t("Profile creation succeeded with Clash proxy"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 成功后的操作
|
|
||||||
setOpen(false);
|
|
||||||
setTimeout(() => formIns.reset(), 500);
|
|
||||||
fileDataRef.current = null;
|
|
||||||
|
|
||||||
// 优化:UI先关闭,异步通知父组件
|
|
||||||
setTimeout(() => {
|
|
||||||
props.onChange(isActivating);
|
|
||||||
}, 0);
|
|
||||||
} catch (err: any) {
|
|
||||||
showNotice("error", err.message || err.toString());
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
// 成功后的操作
|
||||||
try {
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
fileDataRef.current = null;
|
|
||||||
setTimeout(() => formIns.reset(), 500);
|
setTimeout(() => formIns.reset(), 500);
|
||||||
} catch (e) {
|
fileDataRef.current = null;
|
||||||
console.warn("[ProfileViewer] handleClose error:", e);
|
|
||||||
|
// 优化:UI先关闭,异步通知父组件
|
||||||
|
setTimeout(() => {
|
||||||
|
onChange(isActivating);
|
||||||
|
}, 0);
|
||||||
|
} catch (err: any) {
|
||||||
|
showNotice("error", err.message || err.toString());
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const text = {
|
const handleClose = () => {
|
||||||
fullWidth: true,
|
try {
|
||||||
size: "small",
|
setOpen(false);
|
||||||
margin: "normal",
|
fileDataRef.current = null;
|
||||||
variant: "outlined",
|
setTimeout(() => formIns.reset(), 500);
|
||||||
autoComplete: "off",
|
} catch (e) {
|
||||||
autoCorrect: "off",
|
console.warn("[ProfileViewer] handleClose error:", e);
|
||||||
} as const;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const formType = watch("type");
|
const text = {
|
||||||
const isRemote = formType === "remote";
|
fullWidth: true,
|
||||||
const isLocal = formType === "local";
|
size: "small",
|
||||||
|
margin: "normal",
|
||||||
|
variant: "outlined",
|
||||||
|
autoComplete: "off",
|
||||||
|
autoCorrect: "off",
|
||||||
|
} as const;
|
||||||
|
|
||||||
return (
|
const formType = watch("type");
|
||||||
<BaseDialog
|
const isRemote = formType === "remote";
|
||||||
open={open}
|
const isLocal = formType === "local";
|
||||||
title={openType === "new" ? t("Create Profile") : t("Edit Profile")}
|
|
||||||
contentSx={{ width: 375, pb: 0, maxHeight: "80%" }}
|
|
||||||
okBtn={t("Save")}
|
|
||||||
cancelBtn={t("Cancel")}
|
|
||||||
onClose={handleClose}
|
|
||||||
onCancel={handleClose}
|
|
||||||
onOk={handleOk}
|
|
||||||
loading={loading}
|
|
||||||
>
|
|
||||||
<Controller
|
|
||||||
name="type"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormControl size="small" fullWidth sx={{ mt: 1, mb: 1 }}>
|
|
||||||
<InputLabel>{t("Type")}</InputLabel>
|
|
||||||
<Select {...field} autoFocus label={t("Type")}>
|
|
||||||
<MenuItem value="remote">Remote</MenuItem>
|
|
||||||
<MenuItem value="local">Local</MenuItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
return (
|
||||||
name="name"
|
<BaseDialog
|
||||||
control={control}
|
open={open}
|
||||||
render={({ field }) => (
|
title={openType === "new" ? t("Create Profile") : t("Edit Profile")}
|
||||||
<TextField {...text} {...field} label={t("Name")} />
|
contentSx={{ width: 375, pb: 0, maxHeight: "80%" }}
|
||||||
)}
|
okBtn={t("Save")}
|
||||||
/>
|
cancelBtn={t("Cancel")}
|
||||||
|
onClose={handleClose}
|
||||||
<Controller
|
onCancel={handleClose}
|
||||||
name="desc"
|
onOk={handleOk}
|
||||||
control={control}
|
loading={loading}
|
||||||
render={({ field }) => (
|
>
|
||||||
<TextField {...text} {...field} label={t("Descriptions")} />
|
<Controller
|
||||||
)}
|
name="type"
|
||||||
/>
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
{isRemote && (
|
<FormControl size="small" fullWidth sx={{ mt: 1, mb: 1 }}>
|
||||||
<>
|
<InputLabel>{t("Type")}</InputLabel>
|
||||||
<Controller
|
<Select {...field} autoFocus label={t("Type")}>
|
||||||
name="url"
|
<MenuItem value="remote">Remote</MenuItem>
|
||||||
control={control}
|
<MenuItem value="local">Local</MenuItem>
|
||||||
render={({ field }) => (
|
</Select>
|
||||||
<TextField
|
</FormControl>
|
||||||
{...text}
|
|
||||||
{...field}
|
|
||||||
multiline
|
|
||||||
label={t("Subscription URL")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="option.user_agent"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField
|
|
||||||
{...text}
|
|
||||||
{...field}
|
|
||||||
placeholder={`clash-verge/v${version}`}
|
|
||||||
label="User Agent"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
|
||||||
name="option.timeout_seconds"
|
|
||||||
control={control}
|
|
||||||
render={({ field }) => (
|
|
||||||
<TextField
|
|
||||||
{...text}
|
|
||||||
{...field}
|
|
||||||
type="number"
|
|
||||||
placeholder="60"
|
|
||||||
label={t("HTTP Request Timeout")}
|
|
||||||
slotProps={{
|
|
||||||
input: {
|
|
||||||
endAdornment: (
|
|
||||||
<InputAdornment position="end">
|
|
||||||
{t("seconds")}
|
|
||||||
</InputAdornment>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
{(isRemote || isLocal) && (
|
<Controller
|
||||||
|
name="name"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField {...text} {...field} label={t("Name")} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="desc"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField {...text} {...field} label={t("Descriptions")} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isRemote && (
|
||||||
|
<>
|
||||||
<Controller
|
<Controller
|
||||||
name="option.update_interval"
|
name="url"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...text}
|
||||||
|
{...field}
|
||||||
|
multiline
|
||||||
|
label={t("Subscription URL")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="option.user_agent"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextField
|
||||||
|
{...text}
|
||||||
|
{...field}
|
||||||
|
placeholder={`clash-verge/v${version}`}
|
||||||
|
label="User Agent"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
name="option.timeout_seconds"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<TextField
|
<TextField
|
||||||
{...text}
|
{...text}
|
||||||
{...field}
|
{...field}
|
||||||
type="number"
|
type="number"
|
||||||
label={t("Update Interval")}
|
placeholder="60"
|
||||||
|
label={t("HTTP Request Timeout")}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
input: {
|
input: {
|
||||||
endAdornment: (
|
endAdornment: (
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="end">
|
||||||
{t("mins")}
|
{t("seconds")}
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -343,57 +313,79 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{isLocal && openType === "new" && (
|
{(isRemote || isLocal) && (
|
||||||
<FileInput
|
<Controller
|
||||||
onChange={(file, val) => {
|
name="option.update_interval"
|
||||||
formIns.setValue("name", formIns.getValues("name") || file.name);
|
control={control}
|
||||||
fileDataRef.current = val;
|
render={({ field }) => (
|
||||||
}}
|
<TextField
|
||||||
|
{...text}
|
||||||
|
{...field}
|
||||||
|
type="number"
|
||||||
|
label={t("Update Interval")}
|
||||||
|
slotProps={{
|
||||||
|
input: {
|
||||||
|
endAdornment: (
|
||||||
|
<InputAdornment position="end">{t("mins")}</InputAdornment>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLocal && openType === "new" && (
|
||||||
|
<FileInput
|
||||||
|
onChange={(file, val) => {
|
||||||
|
formIns.setValue("name", formIns.getValues("name") || file.name);
|
||||||
|
fileDataRef.current = val;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isRemote && (
|
||||||
|
<>
|
||||||
|
<Controller
|
||||||
|
name="option.with_proxy"
|
||||||
|
control={control}
|
||||||
|
render={({ field }) => (
|
||||||
|
<StyledBox>
|
||||||
|
<InputLabel>{t("Use System Proxy")}</InputLabel>
|
||||||
|
<Switch checked={field.value} {...field} color="primary" />
|
||||||
|
</StyledBox>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
|
|
||||||
{isRemote && (
|
<Controller
|
||||||
<>
|
name="option.self_proxy"
|
||||||
<Controller
|
control={control}
|
||||||
name="option.with_proxy"
|
render={({ field }) => (
|
||||||
control={control}
|
<StyledBox>
|
||||||
render={({ field }) => (
|
<InputLabel>{t("Use Clash Proxy")}</InputLabel>
|
||||||
<StyledBox>
|
<Switch checked={field.value} {...field} color="primary" />
|
||||||
<InputLabel>{t("Use System Proxy")}</InputLabel>
|
</StyledBox>
|
||||||
<Switch checked={field.value} {...field} color="primary" />
|
)}
|
||||||
</StyledBox>
|
/>
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Controller
|
<Controller
|
||||||
name="option.self_proxy"
|
name="option.danger_accept_invalid_certs"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<StyledBox>
|
<StyledBox>
|
||||||
<InputLabel>{t("Use Clash Proxy")}</InputLabel>
|
<InputLabel>{t("Accept Invalid Certs (Danger)")}</InputLabel>
|
||||||
<Switch checked={field.value} {...field} color="primary" />
|
<Switch checked={field.value} {...field} color="primary" />
|
||||||
</StyledBox>
|
</StyledBox>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
<Controller
|
)}
|
||||||
name="option.danger_accept_invalid_certs"
|
</BaseDialog>
|
||||||
control={control}
|
);
|
||||||
render={({ field }) => (
|
}
|
||||||
<StyledBox>
|
|
||||||
<InputLabel>{t("Accept Invalid Certs (Danger)")}</InputLabel>
|
|
||||||
<Switch checked={field.value} {...field} color="primary" />
|
|
||||||
</StyledBox>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</BaseDialog>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const StyledBox = styled(Box)(() => ({
|
const StyledBox = styled(Box)(() => ({
|
||||||
margin: "8px 0 8px 8px",
|
margin: "8px 0 8px 8px",
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
import { Box, Paper, Divider } from "@mui/material";
|
import { Box, Divider, Paper } from "@mui/material";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import customParseFormat from "dayjs/plugin/customParseFormat";
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
import {
|
import type { Ref } from "react";
|
||||||
forwardRef,
|
import { useCallback, useImperativeHandle, useMemo, useState } from "react";
|
||||||
useImperativeHandle,
|
|
||||||
useState,
|
|
||||||
useCallback,
|
|
||||||
useMemo,
|
|
||||||
} from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, DialogRef } from "@/components/base";
|
import { BaseDialog, BaseLoadingOverlay, DialogRef } from "@/components/base";
|
||||||
import { BaseLoadingOverlay } from "@/components/base";
|
|
||||||
import { listWebDavBackup } from "@/services/cmds";
|
import { listWebDavBackup } from "@/services/cmds";
|
||||||
|
|
||||||
import { BackupConfigViewer } from "./backup-config-viewer";
|
import { BackupConfigViewer } from "./backup-config-viewer";
|
||||||
import {
|
import {
|
||||||
BackupTableViewer,
|
|
||||||
BackupFile,
|
BackupFile,
|
||||||
|
BackupTableViewer,
|
||||||
DEFAULT_ROWS_PER_PAGE,
|
DEFAULT_ROWS_PER_PAGE,
|
||||||
} from "./backup-table-viewer";
|
} from "./backup-table-viewer";
|
||||||
dayjs.extend(customParseFormat);
|
dayjs.extend(customParseFormat);
|
||||||
@@ -25,7 +19,7 @@ dayjs.extend(customParseFormat);
|
|||||||
const DATE_FORMAT = "YYYY-MM-DD_HH-mm-ss";
|
const DATE_FORMAT = "YYYY-MM-DD_HH-mm-ss";
|
||||||
const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/;
|
const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/;
|
||||||
|
|
||||||
export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
|
export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
@@ -131,4 +125,4 @@ export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</Box>
|
</Box>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
SwitchAccessShortcutRounded,
|
|
||||||
RestartAltRounded,
|
RestartAltRounded,
|
||||||
|
SwitchAccessShortcutRounded,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from "@mui/lab";
|
||||||
import {
|
import {
|
||||||
@@ -12,17 +12,19 @@ import {
|
|||||||
ListItemText,
|
ListItemText,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import type { Ref } from "react";
|
||||||
|
import { useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
import { BaseDialog, DialogRef } from "@/components/base";
|
import { BaseDialog, DialogRef } from "@/components/base";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { changeClashCore, restartCore } from "@/services/cmds";
|
|
||||||
import {
|
import {
|
||||||
|
changeClashCore,
|
||||||
closeAllConnections,
|
closeAllConnections,
|
||||||
upgradeCore,
|
|
||||||
forceRefreshClashConfig,
|
forceRefreshClashConfig,
|
||||||
|
restartCore,
|
||||||
|
upgradeCore,
|
||||||
} from "@/services/cmds";
|
} from "@/services/cmds";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
@@ -31,7 +33,7 @@ const VALID_CORE = [
|
|||||||
{ name: "Mihomo Alpha", core: "verge-mihomo-alpha", chip: "Alpha Version" },
|
{ name: "Mihomo Alpha", core: "verge-mihomo-alpha", chip: "Alpha Version" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
|
export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { verge, mutateVerge } = useVerge();
|
const { verge, mutateVerge } = useVerge();
|
||||||
@@ -169,4 +171,4 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</List>
|
</List>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import { useImperativeHandle, useState, type Ref } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, DialogRef, Switch } from "@/components/base";
|
import { BaseDialog, DialogRef, Switch } from "@/components/base";
|
||||||
@@ -20,7 +20,7 @@ import { useClashInfo } from "@/hooks/use-clash";
|
|||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [copySuccess, setCopySuccess] = useState<null | string>(null);
|
const [copySuccess, setCopySuccess] = useState<null | string>(null);
|
||||||
@@ -217,4 +217,4 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</Snackbar>
|
</Snackbar>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import {
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import yaml from "js-yaml";
|
import yaml from "js-yaml";
|
||||||
import { forwardRef, useImperativeHandle, useState, useEffect } from "react";
|
import type { Ref } from "react";
|
||||||
|
import { useEffect, useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import MonacoEditor from "react-monaco-editor";
|
import MonacoEditor from "react-monaco-editor";
|
||||||
|
|
||||||
@@ -87,7 +88,7 @@ const DEFAULT_DNS_CONFIG = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { clash, mutateClash } = useClash();
|
const { clash, mutateClash } = useClash();
|
||||||
const themeMode = useThemeMode();
|
const themeMode = useThemeMode();
|
||||||
@@ -1034,4 +1035,4 @@ export const DnsViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
|
InputAdornment,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
InputAdornment,
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import type { Ref } from "react";
|
||||||
|
import { useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, DialogRef, Switch } from "@/components/base";
|
import { BaseDialog, DialogRef, Switch } from "@/components/base";
|
||||||
@@ -16,7 +17,7 @@ import { useVerge } from "@/hooks/use-verge";
|
|||||||
import { entry_lightweight_mode } from "@/services/cmds";
|
import { entry_lightweight_mode } from "@/services/cmds";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
export const LiteModeViewer = forwardRef<DialogRef>((props, ref) => {
|
export function LiteModeViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { verge, patchVerge } = useVerge();
|
const { verge, patchVerge } = useVerge();
|
||||||
|
|
||||||
@@ -143,4 +144,4 @@ export const LiteModeViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</List>
|
</List>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ContentCopyRounded } from "@mui/icons-material";
|
import { ContentCopyRounded } from "@mui/icons-material";
|
||||||
import { alpha, Box, Button, IconButton } from "@mui/material";
|
import { alpha, Box, Button, IconButton } from "@mui/material";
|
||||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import type { Ref } from "react";
|
||||||
|
import { useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ import { BaseDialog, DialogRef } from "@/components/base";
|
|||||||
import { getNetworkInterfacesInfo } from "@/services/cmds";
|
import { getNetworkInterfacesInfo } from "@/services/cmds";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
|
export function NetworkInterfaceViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [isV4, setIsV4] = useState(true);
|
const [isV4, setIsV4] = useState(true);
|
||||||
@@ -99,7 +100,7 @@ export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
))}
|
))}
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
const AddressDisplay = (props: { label: string; content: string }) => {
|
const AddressDisplay = (props: { label: string; content: string }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ import {
|
|||||||
useTheme,
|
useTheme,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import { useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, DialogRef } from "@/components/base";
|
import { BaseDialog, DialogRef } from "@/components/base";
|
||||||
import { EditorViewer } from "@/components/profile/editor-viewer";
|
import { EditorViewer } from "@/components/profile/editor-viewer";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
|
import { defaultDarkTheme, defaultTheme } from "@/pages/_theme";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
|
|
||||||
export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
|
export function ThemeViewer(props: { ref?: React.Ref<DialogRef> }) {
|
||||||
|
const { ref } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -144,7 +145,7 @@ export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</List>
|
</List>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
const Item = styled(ListItem)(() => ({
|
const Item = styled(ListItem)(() => ({
|
||||||
padding: "5px 2px",
|
padding: "5px 2px",
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
List,
|
List,
|
||||||
ListItem,
|
ListItem,
|
||||||
ListItemText,
|
ListItemText,
|
||||||
Box,
|
|
||||||
Typography,
|
|
||||||
Button,
|
|
||||||
TextField,
|
TextField,
|
||||||
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import type { Ref } from "react";
|
||||||
|
import { useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, DialogRef, Switch } from "@/components/base";
|
import { BaseDialog, DialogRef, Switch } from "@/components/base";
|
||||||
@@ -21,7 +22,7 @@ import { StackModeSwitch } from "./stack-mode-switch";
|
|||||||
|
|
||||||
const OS = getSystem();
|
const OS = getSystem();
|
||||||
|
|
||||||
export const TunViewer = forwardRef<DialogRef>((props, ref) => {
|
export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { clash, mutateClash, patchClash } = useClash();
|
const { clash, mutateClash, patchClash } = useClash();
|
||||||
@@ -238,4 +239,4 @@ export const TunViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</List>
|
</List>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import { Box, LinearProgress, Button } from "@mui/material";
|
import { Box, Button, LinearProgress } from "@mui/material";
|
||||||
import { Event, UnlistenFn } from "@tauri-apps/api/event";
|
import { Event, UnlistenFn } from "@tauri-apps/api/event";
|
||||||
import { relaunch } from "@tauri-apps/plugin-process";
|
import { relaunch } from "@tauri-apps/plugin-process";
|
||||||
import { open as openUrl } from "@tauri-apps/plugin-shell";
|
import { open as openUrl } from "@tauri-apps/plugin-shell";
|
||||||
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
|
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import {
|
import type { Ref } from "react";
|
||||||
forwardRef,
|
import { useEffect, useImperativeHandle, useMemo, useState } from "react";
|
||||||
useImperativeHandle,
|
|
||||||
useState,
|
|
||||||
useMemo,
|
|
||||||
useEffect,
|
|
||||||
} from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@@ -19,9 +14,9 @@ import { BaseDialog, DialogRef } from "@/components/base";
|
|||||||
import { useListen } from "@/hooks/use-listen";
|
import { useListen } from "@/hooks/use-listen";
|
||||||
import { portableFlag } from "@/pages/_layout";
|
import { portableFlag } from "@/pages/_layout";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
import { useUpdateState, useSetUpdateState } from "@/services/states";
|
import { useSetUpdateState, useUpdateState } from "@/services/states";
|
||||||
|
|
||||||
export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
@@ -167,4 +162,4 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Button, Box, Typography } from "@mui/material";
|
import { Box, Button, Typography } from "@mui/material";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import type { Ref } from "react";
|
||||||
|
import { useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { BaseDialog, BaseEmpty, DialogRef } from "@/components/base";
|
import { BaseDialog, BaseEmpty, DialogRef } from "@/components/base";
|
||||||
@@ -11,7 +12,7 @@ import { showNotice } from "@/services/noticeService";
|
|||||||
|
|
||||||
import { WebUIItem } from "./web-ui-item";
|
import { WebUIItem } from "./web-ui-item";
|
||||||
|
|
||||||
export const WebUIViewer = forwardRef<DialogRef>((props, ref) => {
|
export function WebUIViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { clashInfo } = useClashInfo();
|
const { clashInfo } = useClashInfo();
|
||||||
@@ -139,4 +140,4 @@ export const WebUIViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user