mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 04:33:45 +08:00
feat: Clash配置、Merge配置提供JSON Schema语法支持、[连接]界面调整 (#887)
This commit is contained in:
parent
e5b82dca4d
commit
0686781359
@ -32,7 +32,8 @@
|
|||||||
"dayjs": "1.11.5",
|
"dayjs": "1.11.5",
|
||||||
"i18next": "^23.7.16",
|
"i18next": "^23.7.16",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"monaco-editor": "^0.34.1",
|
"monaco-editor": "^0.47.0",
|
||||||
|
"monaco-yaml": "^5.1.1",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
@ -3,8 +3,8 @@ import { forwardRef, useImperativeHandle, useState } from "react";
|
|||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { Box, Button, Snackbar } from "@mui/material";
|
import { Box, Button, Snackbar } from "@mui/material";
|
||||||
import { deleteConnection } from "@/services/api";
|
import { deleteConnection } from "@/services/api";
|
||||||
import { truncateStr } from "@/utils/truncate-str";
|
|
||||||
import parseTraffic from "@/utils/parse-traffic";
|
import parseTraffic from "@/utils/parse-traffic";
|
||||||
|
import { t } from "i18next";
|
||||||
|
|
||||||
export interface ConnectionDetailRef {
|
export interface ConnectionDetailRef {
|
||||||
open: (detail: IConnectionsItem) => void;
|
open: (detail: IConnectionsItem) => void;
|
||||||
@ -69,7 +69,9 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
|
|||||||
{ label: "Rule", value: rule },
|
{ label: "Rule", value: rule },
|
||||||
{
|
{
|
||||||
label: "Process",
|
label: "Process",
|
||||||
value: truncateStr(metadata.process || metadata.processPath),
|
value: `${metadata.process}${
|
||||||
|
metadata.processPath ? `(${metadata.processPath})` : ""
|
||||||
|
}`,
|
||||||
},
|
},
|
||||||
{ label: "Time", value: dayjs(data.start).fromNow() },
|
{ label: "Time", value: dayjs(data.start).fromNow() },
|
||||||
{ label: "Source", value: `${metadata.sourceIP}:${metadata.sourcePort}` },
|
{ label: "Source", value: `${metadata.sourceIP}:${metadata.sourcePort}` },
|
||||||
@ -96,7 +98,7 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
|
|||||||
onClose?.();
|
onClose?.();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Close
|
{t("Close")}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -12,23 +12,39 @@ import {
|
|||||||
import { atomThemeMode } from "@/services/states";
|
import { atomThemeMode } from "@/services/states";
|
||||||
import { readProfileFile, saveProfileFile } from "@/services/cmds";
|
import { readProfileFile, saveProfileFile } from "@/services/cmds";
|
||||||
import { Notice } from "@/components/base";
|
import { Notice } from "@/components/base";
|
||||||
|
import { nanoid } from "nanoid";
|
||||||
|
|
||||||
import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js";
|
import * as monaco from "monaco-editor";
|
||||||
import "monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js";
|
|
||||||
import "monaco-editor/esm/vs/editor/contrib/folding/browser/folding.js";
|
|
||||||
import { editor } from "monaco-editor/esm/vs/editor/editor.api";
|
import { editor } from "monaco-editor/esm/vs/editor/editor.api";
|
||||||
|
import { configureMonacoYaml } from "monaco-yaml";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
uid: string;
|
uid: string;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
mode: "yaml" | "javascript";
|
language: "yaml" | "javascript";
|
||||||
|
schema?: "clash" | "merge";
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onChange?: () => void;
|
onChange?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditorViewer = (props: Props) => {
|
// yaml worker
|
||||||
const { uid, open, mode, onClose, onChange } = props;
|
configureMonacoYaml(monaco, {
|
||||||
|
validate: true,
|
||||||
|
enableSchemaRequest: true,
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
uri: "https://fastly.jsdelivr.net/gh/dongchengjie/meta-json-schema@main/schemas/meta-json-schema.json",
|
||||||
|
fileMatch: ["**/*.clash.yaml"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uri: "https://fastly.jsdelivr.net/gh/dongchengjie/meta-json-schema@main/schemas/clash-verge-merge-json-schema.json",
|
||||||
|
fileMatch: ["**/*.merge.yaml"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const EditorViewer = (props: Props) => {
|
||||||
|
const { uid, open, language, schema, onClose, onChange } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const editorRef = useRef<any>();
|
const editorRef = useRef<any>();
|
||||||
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
|
||||||
@ -41,13 +57,21 @@ export const EditorViewer = (props: Props) => {
|
|||||||
const dom = editorRef.current;
|
const dom = editorRef.current;
|
||||||
|
|
||||||
if (!dom) return;
|
if (!dom) return;
|
||||||
|
|
||||||
if (instanceRef.current) instanceRef.current.dispose();
|
if (instanceRef.current) instanceRef.current.dispose();
|
||||||
|
|
||||||
|
const uri = monaco.Uri.parse(`${nanoid()}.${schema}.${language}`);
|
||||||
|
const model = monaco.editor.createModel(data, language, uri);
|
||||||
instanceRef.current = editor.create(editorRef.current, {
|
instanceRef.current = editor.create(editorRef.current, {
|
||||||
value: data,
|
model: model,
|
||||||
language: mode,
|
language: language,
|
||||||
theme: themeMode === "light" ? "vs" : "vs-dark",
|
theme: themeMode === "light" ? "vs" : "vs-dark",
|
||||||
minimap: { enabled: false },
|
minimap: { enabled: dom.clientWidth >= 1000 }, // 超过一定宽度显示minimap滚动条
|
||||||
|
quickSuggestions: {
|
||||||
|
strings: true, // 字符串类型的建议
|
||||||
|
comments: true, // 注释类型的建议
|
||||||
|
other: true, // 其他类型的建议
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -77,8 +101,10 @@ export const EditorViewer = (props: Props) => {
|
|||||||
<Dialog open={open} onClose={onClose} maxWidth="xl" fullWidth>
|
<Dialog open={open} onClose={onClose} maxWidth="xl" fullWidth>
|
||||||
<DialogTitle>{t("Edit File")}</DialogTitle>
|
<DialogTitle>{t("Edit File")}</DialogTitle>
|
||||||
|
|
||||||
<DialogContent sx={{ width: "95%", pb: 1, userSelect: "text" }}>
|
<DialogContent
|
||||||
<div style={{ width: "100%", height: "500px" }} ref={editorRef} />
|
sx={{ width: "95%", height: "100vh", pb: 1, userSelect: "text" }}
|
||||||
|
>
|
||||||
|
<div style={{ width: "100%", height: "100%" }} ref={editorRef} />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
|
@ -386,7 +386,8 @@ export const ProfileItem = (props: Props) => {
|
|||||||
<EditorViewer
|
<EditorViewer
|
||||||
uid={uid}
|
uid={uid}
|
||||||
open={fileOpen}
|
open={fileOpen}
|
||||||
mode="yaml"
|
language="yaml"
|
||||||
|
schema="clash"
|
||||||
onClose={() => setFileOpen(false)}
|
onClose={() => setFileOpen(false)}
|
||||||
/>
|
/>
|
||||||
<ConfirmViewer
|
<ConfirmViewer
|
||||||
|
@ -235,7 +235,8 @@ export const ProfileMore = (props: Props) => {
|
|||||||
<EditorViewer
|
<EditorViewer
|
||||||
uid={uid}
|
uid={uid}
|
||||||
open={fileOpen}
|
open={fileOpen}
|
||||||
mode={type === "merge" ? "yaml" : "javascript"}
|
language={type === "merge" ? "yaml" : "javascript"}
|
||||||
|
schema={type === "merge" ? "merge" : undefined}
|
||||||
onClose={() => setFileOpen(false)}
|
onClose={() => setFileOpen(false)}
|
||||||
/>
|
/>
|
||||||
<ConfirmViewer
|
<ConfirmViewer
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"New": "New",
|
"New": "New",
|
||||||
"Create Profile": "Create Profile",
|
"Create Profile": "Create Profile",
|
||||||
"Choose File": "Choose File",
|
"Choose File": "Choose File",
|
||||||
|
"Close": "Close",
|
||||||
"Close All": "Close All",
|
"Close All": "Close All",
|
||||||
"Home": "Home",
|
"Home": "Home",
|
||||||
"Select": "Select",
|
"Select": "Select",
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"New": "Новый",
|
"New": "Новый",
|
||||||
"Create Profile": "Создать профиль",
|
"Create Profile": "Создать профиль",
|
||||||
"Choose File": "Выбрать файл",
|
"Choose File": "Выбрать файл",
|
||||||
|
"Close": "Закрыть",
|
||||||
"Close All": "Закрыть всё",
|
"Close All": "Закрыть всё",
|
||||||
"Home": "Главная",
|
"Home": "Главная",
|
||||||
"Select": "Выбрать",
|
"Select": "Выбрать",
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"New": "新建",
|
"New": "新建",
|
||||||
"Create Profile": "新建订阅",
|
"Create Profile": "新建订阅",
|
||||||
"Choose File": "选择文件",
|
"Choose File": "选择文件",
|
||||||
|
"Close": "关闭",
|
||||||
"Close All": "关闭全部",
|
"Close All": "关闭全部",
|
||||||
"Home": "首页",
|
"Home": "首页",
|
||||||
"Select": "使用",
|
"Select": "使用",
|
||||||
|
@ -38,7 +38,12 @@ const ConnectionsPage = () => {
|
|||||||
const isTableLayout = setting.layout === "table";
|
const isTableLayout = setting.layout === "table";
|
||||||
|
|
||||||
const orderOpts: Record<string, OrderFunc> = {
|
const orderOpts: Record<string, OrderFunc> = {
|
||||||
Default: (list) => list,
|
Default: (list) =>
|
||||||
|
list.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(b.start || "0").getTime()! -
|
||||||
|
new Date(a.start || "0").getTime()!
|
||||||
|
),
|
||||||
"Upload Speed": (list) => list.sort((a, b) => b.curUpload! - a.curUpload!),
|
"Upload Speed": (list) => list.sort((a, b) => b.curUpload! - a.curUpload!),
|
||||||
"Download Speed": (list) =>
|
"Download Speed": (list) =>
|
||||||
list.sort((a, b) => b.curDownload! - a.curDownload!),
|
list.sort((a, b) => b.curDownload! - a.curDownload!),
|
||||||
|
@ -2,7 +2,7 @@ import { defineConfig } from "vite";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import svgr from "vite-plugin-svgr";
|
import svgr from "vite-plugin-svgr";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import monaco from "vite-plugin-monaco-editor";
|
import monacoEditor from "vite-plugin-monaco-editor";
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@ -11,7 +11,15 @@ export default defineConfig({
|
|||||||
plugins: [
|
plugins: [
|
||||||
svgr(),
|
svgr(),
|
||||||
react(),
|
react(),
|
||||||
monaco({ languageWorkers: ["editorWorkerService", "typescript"] }),
|
monacoEditor({
|
||||||
|
languageWorkers: ["editorWorkerService", "typescript"],
|
||||||
|
customWorkers: [
|
||||||
|
{
|
||||||
|
label: "yaml",
|
||||||
|
entry: "monaco-yaml/yaml.worker",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
outDir: "../dist",
|
outDir: "../dist",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user