From a9bf32919e8c6c10ae9c84e117f584447802e38c Mon Sep 17 00:00:00 2001
From: GyDi <segydi@foxmail.com>
Date: Mon, 25 Jul 2022 01:20:13 +0800
Subject: [PATCH] feat: runtime config viewer

---
 src-tauri/src/cmds.rs                     |  7 +++
 src-tauri/src/core/clash.rs               | 17 ++++-
 src-tauri/src/core/mod.rs                 | 13 ++--
 src-tauri/src/main.rs                     |  1 +
 src/components/setting/config-viewer.tsx  | 75 +++++++++++++++++++++++
 src/components/setting/setting-system.tsx | 10 ++-
 src/services/cmds.ts                      |  4 ++
 7 files changed, 119 insertions(+), 8 deletions(-)
 create mode 100644 src/components/setting/config-viewer.tsx

diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs
index a39bfc4..4d30c85 100644
--- a/src-tauri/src/cmds.rs
+++ b/src-tauri/src/cmds.rs
@@ -174,6 +174,13 @@ pub fn get_clash_info(core: State<'_, Core>) -> CmdResult<ClashInfo> {
   Ok(clash.info.clone())
 }
 
+/// get the running clash config string
+#[tauri::command]
+pub fn get_running_config(core: State<'_, Core>) -> CmdResult<Option<String>> {
+  let clash = core.clash.lock();
+  Ok(clash.running_config.clone())
+}
+
 /// update the clash core config
 /// after putting the change to the clash core
 /// then we should save the latest config
diff --git a/src-tauri/src/core/clash.rs b/src-tauri/src/core/clash.rs
index 5d88aaf..92c078d 100644
--- a/src-tauri/src/core/clash.rs
+++ b/src-tauri/src/core/clash.rs
@@ -87,6 +87,9 @@ pub struct Clash {
 
   /// some info
   pub info: ClashInfo,
+
+  /// save the running config
+  pub running_config: Option<String>,
 }
 
 impl Clash {
@@ -94,7 +97,11 @@ impl Clash {
     let config = Clash::read_config();
     let info = ClashInfo::from(&config);
 
-    Clash { config, info }
+    Clash {
+      config,
+      info,
+      running_config: None,
+    }
   }
 
   /// get clash config
@@ -111,6 +118,14 @@ impl Clash {
     )
   }
 
+  /// save running config
+  pub fn set_running_config(&mut self, config: &Mapping) {
+    self.running_config = match serde_yaml::to_string(config) {
+      Ok(config_str) => Some(config_str),
+      Err(_) => None,
+    };
+  }
+
   /// patch update the clash config
   /// if the port is changed then return true
   pub fn patch_config(&mut self, patch: Mapping) -> Result<(bool, bool)> {
diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs
index 893e261..2d8da94 100644
--- a/src-tauri/src/core/mod.rs
+++ b/src-tauri/src/core/mod.rs
@@ -347,12 +347,10 @@ impl Core {
       Clash::strict_filter(data)
     };
 
-    let (mut config, info) = {
-      let clash = self.clash.lock();
-      let config = clash.config.clone();
-      let info = clash.info.clone();
-      (config, info)
-    };
+    let mut clash = self.clash.lock();
+
+    let mut config = clash.config.clone();
+    let info = clash.info.clone();
 
     for (key, value) in data.into_iter() {
       config.insert(key, value);
@@ -369,6 +367,9 @@ impl Core {
       Notice::from(window.clone())
     };
 
+    clash.set_running_config(&config);
+    drop(clash);
+
     let service = self.service.lock();
     service.set_config(info, config, notice)
   }
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 4010509..28eeff3 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -114,6 +114,7 @@ fn main() -> std::io::Result<()> {
       cmds::get_clash_info,
       cmds::patch_clash_config,
       cmds::change_clash_core,
+      cmds::get_running_config,
       // verge
       cmds::get_verge_config,
       cmds::patch_verge_config,
diff --git a/src/components/setting/config-viewer.tsx b/src/components/setting/config-viewer.tsx
new file mode 100644
index 0000000..4a5a672
--- /dev/null
+++ b/src/components/setting/config-viewer.tsx
@@ -0,0 +1,75 @@
+import { useEffect, useRef, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { useRecoilValue } from "recoil";
+import {
+  Button,
+  Chip,
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogTitle,
+} from "@mui/material";
+import { InfoRounded } from "@mui/icons-material";
+import { atomThemeMode } from "../../services/states";
+import { getRunningConfig } from "../../services/cmds";
+
+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 { editor } from "monaco-editor/esm/vs/editor/editor.api";
+
+const ConfigViewer = () => {
+  const { t } = useTranslation();
+  const [open, setOpen] = useState(false);
+
+  const editorRef = useRef<any>();
+  const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
+  const themeMode = useRecoilValue(atomThemeMode);
+
+  useEffect(() => {
+    if (!open) return;
+
+    getRunningConfig().then((data) => {
+      const dom = editorRef.current;
+
+      if (!dom) return;
+      if (instanceRef.current) instanceRef.current.dispose();
+
+      instanceRef.current = editor.create(editorRef.current, {
+        value: data ?? "# Error\n",
+        language: "yaml",
+        theme: themeMode === "light" ? "vs" : "vs-dark",
+        minimap: { enabled: false },
+        readOnly: true,
+      });
+    });
+
+    return () => {
+      if (instanceRef.current) {
+        instanceRef.current.dispose();
+        instanceRef.current = null;
+      }
+    };
+  }, [open]);
+
+  return (
+    <>
+      <Dialog open={open} onClose={() => setOpen(false)}>
+        <DialogTitle>
+          {t("Running Config")} <Chip label="ReadOnly" size="small" />
+        </DialogTitle>
+
+        <DialogContent sx={{ width: 520, pb: 1 }}>
+          <div style={{ width: "100%", height: "420px" }} ref={editorRef} />
+        </DialogContent>
+
+        <DialogActions>
+          <Button onClick={() => setOpen(false)}>{t("Back")}</Button>
+        </DialogActions>
+      </Dialog>
+
+      <InfoRounded fontSize="small" onClick={() => setOpen(true)} />
+    </>
+  );
+};
+export default ConfigViewer;
diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx
index bd3b993..36fface 100644
--- a/src/components/setting/setting-system.tsx
+++ b/src/components/setting/setting-system.tsx
@@ -18,6 +18,7 @@ import { SettingList, SettingItem } from "./setting";
 import { CmdType } from "../../services/types";
 import GuardState from "./guard-state";
 import ServiceMode from "./service-mode";
+import ConfigViewer from "./config-viewer";
 import SysproxyTooltip from "./sysproxy-tooltip";
 import getSystem from "../../utils/get-system";
 
@@ -58,7 +59,14 @@ const SettingSystem = ({ onError }: Props) => {
   return (
     <SettingList title={t("System Setting")}>
       <SettingItem>
-        <ListItemText primary={t("Tun Mode")} />
+        <ListItemText
+          primary={
+            <Box sx={{ display: "flex", alignItems: "center" }}>
+              <span style={{ marginRight: 4 }}>{t("Tun Mode")}</span>
+              <ConfigViewer />
+            </Box>
+          }
+        />
         <GuardState
           value={enable_tun_mode ?? false}
           valueProps="checked"
diff --git a/src/services/cmds.ts b/src/services/cmds.ts
index fd7f99a..08b6987 100644
--- a/src/services/cmds.ts
+++ b/src/services/cmds.ts
@@ -70,6 +70,10 @@ export async function getClashInfo() {
   return invoke<CmdType.ClashInfo | null>("get_clash_info");
 }
 
+export async function getRunningConfig() {
+  return invoke<string | null>("get_running_config");
+}
+
 export async function patchClashConfig(payload: Partial<ApiType.ConfigData>) {
   return invoke<void>("patch_clash_config", { payload });
 }
-- 
GitLab