Ver código fonte

feat: Clash配置、Merge配置提供JSON Schema语法支持、[连接]界面调整 (#887)

dongchengjie 1 ano atrás
pai
commit
402299e9c8

+ 2 - 1
package.json

@@ -32,7 +32,8 @@
     "dayjs": "1.11.5",
     "i18next": "^23.7.16",
     "lodash-es": "^4.17.21",
-    "monaco-editor": "^0.34.1",
+    "monaco-editor": "^0.47.0",
+    "monaco-yaml": "^5.1.1",
     "nanoid": "^5.0.4",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",

+ 5 - 3
src/components/connection/connection-detail.tsx

@@ -3,8 +3,8 @@ import { forwardRef, useImperativeHandle, useState } from "react";
 import { useLockFn } from "ahooks";
 import { Box, Button, Snackbar } from "@mui/material";
 import { deleteConnection } from "@/services/api";
-import { truncateStr } from "@/utils/truncate-str";
 import parseTraffic from "@/utils/parse-traffic";
+import { t } from "i18next";
 
 export interface ConnectionDetailRef {
   open: (detail: IConnectionsItem) => void;
@@ -69,7 +69,9 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
     { label: "Rule", value: rule },
     {
       label: "Process",
-      value: truncateStr(metadata.process || metadata.processPath),
+      value: `${metadata.process}${
+        metadata.processPath ? `(${metadata.processPath})` : ""
+      }`,
     },
     { label: "Time", value: dayjs(data.start).fromNow() },
     { label: "Source", value: `${metadata.sourceIP}:${metadata.sourcePort}` },
@@ -96,7 +98,7 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
             onClose?.();
           }}
         >
-          Close
+          {t("Close")}
         </Button>
       </Box>
     </Box>

+ 37 - 11
src/components/profile/editor-viewer.tsx

@@ -12,23 +12,39 @@ import {
 import { atomThemeMode } from "@/services/states";
 import { readProfileFile, saveProfileFile } from "@/services/cmds";
 import { Notice } from "@/components/base";
+import { nanoid } from "nanoid";
 
-import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js";
-import "monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js";
-import "monaco-editor/esm/vs/editor/contrib/folding/browser/folding.js";
+import * as monaco from "monaco-editor";
 import { editor } from "monaco-editor/esm/vs/editor/editor.api";
+import { configureMonacoYaml } from "monaco-yaml";
 
 interface Props {
   uid: string;
   open: boolean;
-  mode: "yaml" | "javascript";
+  language: "yaml" | "javascript";
+  schema?: "clash" | "merge";
   onClose: () => void;
   onChange?: () => void;
 }
 
-export const EditorViewer = (props: Props) => {
-  const { uid, open, mode, onClose, onChange } = props;
+// yaml worker
+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 editorRef = useRef<any>();
   const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
@@ -41,13 +57,21 @@ export const EditorViewer = (props: Props) => {
       const dom = editorRef.current;
 
       if (!dom) return;
+
       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, {
-        value: data,
-        language: mode,
+        model: model,
+        language: language,
         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>
       <DialogTitle>{t("Edit File")}</DialogTitle>
 
-      <DialogContent sx={{ width: "95%", pb: 1, userSelect: "text" }}>
-        <div style={{ width: "100%", height: "500px" }} ref={editorRef} />
+      <DialogContent
+        sx={{ width: "95%", height: "100vh", pb: 1, userSelect: "text" }}
+      >
+        <div style={{ width: "100%", height: "100%" }} ref={editorRef} />
       </DialogContent>
 
       <DialogActions>

+ 2 - 1
src/components/profile/profile-item.tsx

@@ -386,7 +386,8 @@ export const ProfileItem = (props: Props) => {
       <EditorViewer
         uid={uid}
         open={fileOpen}
-        mode="yaml"
+        language="yaml"
+        schema="clash"
         onClose={() => setFileOpen(false)}
       />
       <ConfirmViewer

+ 2 - 1
src/components/profile/profile-more.tsx

@@ -235,7 +235,8 @@ export const ProfileMore = (props: Props) => {
       <EditorViewer
         uid={uid}
         open={fileOpen}
-        mode={type === "merge" ? "yaml" : "javascript"}
+        language={type === "merge" ? "yaml" : "javascript"}
+        schema={type === "merge" ? "merge" : undefined}
         onClose={() => setFileOpen(false)}
       />
       <ConfirmViewer

+ 1 - 0
src/locales/en.json

@@ -29,6 +29,7 @@
   "New": "New",
   "Create Profile": "Create Profile",
   "Choose File": "Choose File",
+  "Close": "Close",
   "Close All": "Close All",
   "Home": "Home",
   "Select": "Select",

+ 1 - 0
src/locales/ru.json

@@ -29,6 +29,7 @@
   "New": "Новый",
   "Create Profile": "Создать профиль",
   "Choose File": "Выбрать файл",
+  "Close": "Закрыть",
   "Close All": "Закрыть всё",
   "Home": "Главная",
   "Select": "Выбрать",

+ 1 - 0
src/locales/zh.json

@@ -29,6 +29,7 @@
   "New": "新建",
   "Create Profile": "新建订阅",
   "Choose File": "选择文件",
+  "Close": "关闭",
   "Close All": "关闭全部",
   "Home": "首页",
   "Select": "使用",

+ 6 - 1
src/pages/connections.tsx

@@ -38,7 +38,12 @@ const ConnectionsPage = () => {
   const isTableLayout = setting.layout === "table";
 
   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!),
     "Download Speed": (list) =>
       list.sort((a, b) => b.curDownload! - a.curDownload!),

+ 10 - 2
vite.config.ts

@@ -2,7 +2,7 @@ import { defineConfig } from "vite";
 import path from "path";
 import svgr from "vite-plugin-svgr";
 import react from "@vitejs/plugin-react";
-import monaco from "vite-plugin-monaco-editor";
+import monacoEditor from "vite-plugin-monaco-editor";
 
 // https://vitejs.dev/config/
 export default defineConfig({
@@ -11,7 +11,15 @@ export default defineConfig({
   plugins: [
     svgr(),
     react(),
-    monaco({ languageWorkers: ["editorWorkerService", "typescript"] }),
+    monacoEditor({
+      languageWorkers: ["editorWorkerService", "typescript"],
+      customWorkers: [
+        {
+          label: "yaml",
+          entry: "monaco-yaml/yaml.worker",
+        },
+      ],
+    }),
   ],
   build: {
     outDir: "../dist",