diff --git a/package.json b/package.json index 05a39539..619daec0 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@juggle/resize-observer": "^3.4.0", + "@monaco-editor/react": "^4.6.0", "@mui/icons-material": "^5.15.20", "@mui/lab": "5.0.0-alpha.149", "@mui/material": "^5.15.20", @@ -47,7 +48,6 @@ "react-hook-form": "^7.52.0", "react-i18next": "^13.5.0", "react-markdown": "^9.0.1", - "react-monaco-editor": "^0.55.0", "react-router-dom": "^6.23.1", "react-transition-group": "^4.4.5", "react-virtuoso": "^4.7.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ff6d870..59c0a0e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: "@juggle/resize-observer": specifier: ^3.4.0 version: 3.4.0 + "@monaco-editor/react": + specifier: ^4.6.0 + version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) "@mui/icons-material": specifier: ^5.15.20 version: 5.15.20(@mui/material@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) @@ -94,9 +97,6 @@ importers: react-markdown: specifier: ^9.0.1 version: 9.0.1(@types/react@18.3.3)(react@18.3.1) - react-monaco-editor: - specifier: ^0.55.0 - version: 0.55.0(@types/react@18.3.3)(monaco-editor@0.49.0)(react@18.3.1) react-router-dom: specifier: ^6.23.1 version: 6.23.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1546,6 +1546,24 @@ packages: integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==, } + "@monaco-editor/loader@1.4.0": + resolution: + { + integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==, + } + peerDependencies: + monaco-editor: ">= 0.21.0 < 1" + + "@monaco-editor/react@4.6.0": + resolution: + { + integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==, + } + peerDependencies: + monaco-editor: ">= 0.25.0 < 1" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + "@mui/base@5.0.0-beta.20": resolution: { @@ -3845,16 +3863,6 @@ packages: "@types/react": ">=18" react: ">=18" - react-monaco-editor@0.55.0: - resolution: - { - integrity: sha512-GdEP0Q3Rn1dczfKEEyY08Nes5plWwIYU4sWRBQO0+jsQWQsKMHKCC6+hPRwR7G/4aA3V/iU9jSmWPzVJYMVFSQ==, - } - peerDependencies: - "@types/react": ">=16 <= 18" - monaco-editor: ^0.44.0 - react: ">=16 <= 18" - react-refresh@0.14.2: resolution: { @@ -4098,6 +4106,12 @@ packages: integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==, } + state-local@1.0.7: + resolution: + { + integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==, + } + stringify-entities@4.0.4: resolution: { @@ -5525,6 +5539,18 @@ snapshots: "@juggle/resize-observer@3.4.0": {} + "@monaco-editor/loader@1.4.0(monaco-editor@0.49.0)": + dependencies: + monaco-editor: 0.49.0 + state-local: 1.0.7 + + "@monaco-editor/react@4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": + dependencies: + "@monaco-editor/loader": 1.4.0(monaco-editor@0.49.0) + monaco-editor: 0.49.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + "@mui/base@5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)": dependencies: "@babel/runtime": 7.24.7 @@ -7000,13 +7026,6 @@ snapshots: transitivePeerDependencies: - supports-color - react-monaco-editor@0.55.0(@types/react@18.3.3)(monaco-editor@0.49.0)(react@18.3.1): - dependencies: - "@types/react": 18.3.3 - monaco-editor: 0.49.0 - prop-types: 15.8.1 - react: 18.3.1 - react-refresh@0.14.2: {} react-router-dom@6.23.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -7163,6 +7182,8 @@ snapshots: space-separated-tokens@2.0.2: {} + state-local@1.0.7: {} + stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx index 807aed37..38868e59 100644 --- a/src/components/profile/profile-item.tsx +++ b/src/components/profile/profile-item.tsx @@ -483,6 +483,7 @@ export const ProfileItem = (props: Props) => { onClose={() => setFileOpen(false)} /> { - const { title, property, open, onClose, onChange } = props; + const { title, profileUid, property, open, onClose, onChange } = props; const { t } = useTranslation(); - const editorRef = useRef(); // 编辑器实例 - const monacoRef = useRef(); // monaco 实例 - const monacoHoverProviderRef = useRef(); // monaco 注册缓存 - const monacoCompletionItemProviderRef = useRef(); // monaco 注册缓存 - - // 获取编辑器实例 - const editorDidMountHandle = useCallback( - (editor: monaco.editor.IStandaloneCodeEditor, monacoIns: typeof monaco) => { - editorRef.current = editor; - monacoRef.current = monacoIns; - }, - [] - ); - const themeMode = useThemeMode(); const [prevData, setPrevData] = useState(""); const [currData, setCurrData] = useState(""); - const [method, setMethod] = useState("append"); - const [ruleType, setRuleType] = useState("DOMAIN"); + const [rule, setRule] = useState(""); + const [ruleType, setRuleType] = + useState<(typeof RuleTypeList)[number]>("DOMAIN"); const [ruleContent, setRuleContent] = useState(""); - const [proxyPolicy, setProxyPolicy] = useState(""); + const [noResolve, setNoResolve] = useState(false); + const [proxyPolicy, setProxyPolicy] = useState("DIRECT"); + const [proxyPolicyList, setProxyPolicyList] = useState([]); + const [ruleList, setRuleList] = useState([]); - const uri = monaco.Uri.parse(`${nanoid()}`); - const model = monaco.editor.createModel(prevData, "yaml", uri); + const editorOptions = { + tabSize: 2, + minimap: { enabled: false }, + mouseWheelZoom: true, + quickSuggestions: { + strings: true, + comments: true, + other: true, + }, + padding: { + top: 33, + }, + fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"${ + getSystem() === "windows" ? ", twemoji mozilla" : "" + }`, + fontLigatures: true, + smoothScrolling: true, + }; const fetchContent = async () => { let data = await readProfileFile(property); @@ -101,43 +157,59 @@ export const RulesEditorViewer = (props: Props) => { setPrevData(data); }; - const addSeq = async () => { + const fetchProfile = async () => { + let data = await readProfileFile(profileUid); + let obj = yaml.load(data) as { "proxy-groups": []; proxies: []; rules: [] }; + if (!obj["proxy-groups"]) { + obj = { "proxy-groups": [], proxies: [], rules: [] }; + } + setProxyPolicyList( + BuiltinProxyPolicyList.concat( + obj["proxy-groups"].map((item: any) => item.name) + ) + ); + setRuleList(obj.rules); + }; + + const addSeq = async (method: "prepend" | "append" | "delete") => { let obj = yaml.load(currData) as ISeqProfileConfig; if (!obj.prepend) { obj = { prepend: [], append: [], delete: [] }; } switch (method) { case "append": { - obj.append.push(`${ruleType},${ruleContent},${proxyPolicy}`); + obj.append.push( + `${ruleType}${ + ruleType === "MATCH" ? "" : "," + ruleContent + },${proxyPolicy}${ + NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : "" + }` + ); break; } case "prepend": { - obj.prepend.push(`${ruleType},${ruleContent},${proxyPolicy}`); + obj.prepend.push( + `${ruleType}${ + ruleType === "MATCH" ? "" : "," + ruleContent + },${proxyPolicy}${ + NoResolveList.includes(ruleType) && noResolve ? ",no-resolve" : "" + }` + ); break; } case "delete": { - obj.delete.push(`${ruleType},${ruleContent},${proxyPolicy}`); + obj.delete.push(rule); break; } } let raw = yaml.dump(obj); - await saveProfileFile(property, raw); setCurrData(raw); }; useEffect(() => { fetchContent(); - }, []); - - useEffect(() => { - return () => { - if (editorRef.current) { - editorRef.current.dispose(); - } - monacoCompletionItemProviderRef.current?.dispose(); - monacoHoverProviderRef.current?.dispose(); - }; + fetchProfile(); }, [open]); const onSave = useLockFn(async () => { @@ -152,7 +224,7 @@ export const RulesEditorViewer = (props: Props) => { return ( - {title ?? t("Edit File")} + {title ?? t("Edit Rules")}
{ }} > - - - - { @@ -200,7 +251,9 @@ export const RulesEditorViewer = (props: Props) => { { setRuleContent(e.target.value); }} @@ -208,52 +261,93 @@ export const RulesEditorViewer = (props: Props) => { - { - setProxyPolicy(e.target.value); + options={proxyPolicyList} + onChange={(_, v) => { + if (v) setProxyPolicy(v); }} + renderInput={(params) => } /> + {NoResolveList.includes(ruleType) && ( + + + { + setNoResolve(!noResolve); + }} + /> + + )} - + + + + + + + + { + if (v) setRule(v); + }} + renderInput={(params) => } + /> + + + +
- { + if (value) setCurrData(value); }} - editorDidMount={editorDidMountHandle} + options={editorOptions} />
diff --git a/src/locales/en.json b/src/locales/en.json index a8e3679c..38d8b838 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -52,6 +52,13 @@ "Edit Profile": "Edit Profile", "Edit Proxies": "Edit Proxies", "Edit Rules": "Edit Rules", + "Rule Type": "Rule Type", + "Rule Content": "Rule Content", + "Proxy Policy": "roxy Policy", + "No Resolve": "No Resolve", + "Add Prepend Rule": "Add Prepend Rule", + "Add Append Rule": "Add Append Rule", + "Delete Rule": "Delete Rule", "Edit Groups": "Edit Proxy Groups", "Edit Merge": "Edit Merge", "Edit Script": "Edit Script", diff --git a/src/locales/zh.json b/src/locales/zh.json index f9497564..24042d76 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -52,6 +52,13 @@ "Edit Profile": "编辑配置", "Edit Proxies": "添加/删除 节点", "Edit Rules": "添加/删除 规则", + "Rule Type": "规则类型", + "Rule Content": "规则内容", + "Proxy Policy": "代理策略", + "No Resolve": "跳过DNS解析", + "Add Prepend Rule": "添加前置规则", + "Add Append Rule": "添加后置规则", + "Delete Rule": "删除规则", "Edit Groups": "添加/删除 代理组", "Edit Merge": "微调配置 (yaml)", "Edit Script": "微调配置 (js)",