diff --git a/package.json b/package.json
index 8f33b9a95425c4da25cd0d9cb43ad7a397630d0c..68c8baad48a1b498f562f46138ca845db7fe2eba 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
     "axios": "^0.26.0",
     "dayjs": "^1.10.8",
     "i18next": "^21.6.14",
+    "monaco-editor": "^0.33.0",
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "react-i18next": "^11.15.6",
@@ -49,7 +50,8 @@
     "pretty-quick": "^3.1.3",
     "sass": "^1.49.7",
     "typescript": "^4.5.5",
-    "vite": "^2.8.6"
+    "vite": "^2.8.6",
+    "vite-plugin-monaco-editor": "^1.0.10"
   },
   "prettier": {
     "tabWidth": 2,
diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs
index fd876475d543b6da339e522631f2225b96f85de9..5ba74d5df8d8b8226e6b7cd8010ffdf9bc2d9e28 100644
--- a/src-tauri/src/cmds.rs
+++ b/src-tauri/src/cmds.rs
@@ -212,6 +212,35 @@ pub fn view_profile(index: String, profiles_state: State<'_, ProfilesState>) ->
   wrap_err!(open::that(path))
 }
 
+/// read the profile item file data
+#[tauri::command]
+pub fn read_profile_file(
+  index: String,
+  profiles_state: State<'_, ProfilesState>,
+) -> Result<String, String> {
+  let profiles = profiles_state.0.lock().unwrap();
+  let item = wrap_err!(profiles.get_item(&index))?;
+  let data = wrap_err!(item.read_file())?;
+
+  Ok(data)
+}
+
+/// save the profile item file data
+#[tauri::command]
+pub fn save_profile_file(
+  index: String,
+  file_data: Option<String>,
+  profiles_state: State<'_, ProfilesState>,
+) -> Result<(), String> {
+  if file_data.is_none() {
+    return Ok(());
+  }
+
+  let profiles = profiles_state.0.lock().unwrap();
+  let item = wrap_err!(profiles.get_item(&index))?;
+  wrap_err!(item.save_file(file_data.unwrap()))
+}
+
 /// restart the sidecar
 #[tauri::command]
 pub fn restart_sidecar(
diff --git a/src-tauri/src/core/profiles.rs b/src-tauri/src/core/profiles.rs
index 0d86bacaef3001b437f03f180ffd3509a492f903..8311bacbe5f7804c683756318436d0c5c7efbe47 100644
--- a/src-tauri/src/core/profiles.rs
+++ b/src-tauri/src/core/profiles.rs
@@ -279,6 +279,28 @@ impl PrfItem {
       file_data: Some(tmpl::ITEM_SCRIPT.into()),
     })
   }
+
+  /// get the file data
+  pub fn read_file(&self) -> Result<String> {
+    if self.file.is_none() {
+      bail!("could not find the file");
+    }
+
+    let file = self.file.clone().unwrap();
+    let path = dirs::app_profiles_dir().join(file);
+    fs::read_to_string(path).context("failed to read the file")
+  }
+
+  /// save the file data
+  pub fn save_file(&self, data: String) -> Result<()> {
+    if self.file.is_none() {
+      bail!("could not find the file");
+    }
+
+    let file = self.file.clone().unwrap();
+    let path = dirs::app_profiles_dir().join(file);
+    fs::write(path, data.as_bytes()).context("failed to save the file")
+  }
 }
 
 ///
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index fb091919d2e0e5df5bf796b8620e5b567fb3c73b..da973fda8a31a5f6c6548b68580aaad20d505b54 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -118,7 +118,9 @@ fn main() -> std::io::Result<()> {
       cmds::get_profiles,
       cmds::sync_profiles,
       cmds::enhance_profiles,
-      cmds::change_profile_chain
+      cmds::change_profile_chain,
+      cmds::read_profile_file,
+      cmds::save_profile_file
     ]);
 
   #[cfg(target_os = "macos")]
diff --git a/src/components/profile/file-editor.tsx b/src/components/profile/file-editor.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..69c21a9aa9af91797c93798b03547615cb5e515e
--- /dev/null
+++ b/src/components/profile/file-editor.tsx
@@ -0,0 +1,97 @@
+import useSWR from "swr";
+import { useEffect, useRef } from "react";
+import { useLockFn } from "ahooks";
+import { useTranslation } from "react-i18next";
+import {
+  Button,
+  Dialog,
+  DialogActions,
+  DialogContent,
+  DialogTitle,
+} from "@mui/material";
+import {
+  getVergeConfig,
+  readProfileFile,
+  saveProfileFile,
+} from "../../services/cmds";
+import Notice from "../base/base-notice";
+
+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";
+
+interface Props {
+  uid: string;
+  open: boolean;
+  mode: "yaml" | "javascript";
+  onClose: () => void;
+  onChange?: () => void;
+}
+
+const FileEditor = (props: Props) => {
+  const { uid, open, mode, onClose, onChange } = props;
+
+  const { t } = useTranslation();
+  const editorRef = useRef<any>();
+  const instanceRef = useRef<editor.IStandaloneCodeEditor | null>(null);
+  const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
+  const { theme_mode } = vergeConfig ?? {};
+
+  useEffect(() => {
+    if (!open) return;
+
+    readProfileFile(uid).then((data) => {
+      const dom = editorRef.current;
+
+      if (!dom) return;
+      if (instanceRef.current) instanceRef.current.dispose();
+
+      instanceRef.current = editor.create(editorRef.current, {
+        value: data,
+        language: mode,
+        theme: theme_mode === "light" ? "vs" : "vs-dark",
+        minimap: { enabled: false },
+      });
+    });
+
+    return () => {
+      if (instanceRef.current) {
+        instanceRef.current.dispose();
+        instanceRef.current = null;
+      }
+    };
+  }, [open]);
+
+  const onSave = useLockFn(async () => {
+    const value = instanceRef.current?.getValue();
+
+    if (value == null) return;
+
+    try {
+      await saveProfileFile(uid, value);
+      onChange?.();
+    } catch (err: any) {
+      Notice.error(err.message || err.toString());
+    }
+  });
+
+  return (
+    <Dialog open={open} onClose={onClose}>
+      <DialogTitle>{t("Edit File")}</DialogTitle>
+
+      <DialogContent sx={{ width: 520, pb: 1 }}>
+        <div style={{ width: "100%", height: "420px" }} ref={editorRef} />
+      </DialogContent>
+
+      <DialogActions>
+        <Button onClick={onClose}>{t("Cancel")}</Button>
+        <Button onClick={onSave} variant="contained">
+          {t("Save")}
+        </Button>
+      </DialogActions>
+    </Dialog>
+  );
+};
+
+export default FileEditor;
diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx
index c6a2893d6aef54787b36592d91e41c0735b641e6..8e29d7632bee8e6ab24cb3fba6c2e278ddab2bb1 100644
--- a/src/components/profile/profile-item.tsx
+++ b/src/components/profile/profile-item.tsx
@@ -21,6 +21,7 @@ import { atomLoadingCache } from "../../services/states";
 import { updateProfile, deleteProfile, viewProfile } from "../../services/cmds";
 import parseTraffic from "../../utils/parse-traffic";
 import ProfileEdit from "./profile-edit";
+import FileEditor from "./file-editor";
 import Notice from "../base/base-notice";
 
 const Wrapper = styled(Box)(({ theme }) => ({
@@ -54,7 +55,7 @@ const ProfileItem = (props: Props) => {
   const [position, setPosition] = useState({ left: 0, top: 0 });
   const [loadingCache, setLoadingCache] = useRecoilState(atomLoadingCache);
 
-  const { name = "Profile", extra, updated = 0 } = itemData;
+  const { uid, name = "Profile", extra, updated = 0 } = itemData;
   const { upload = 0, download = 0, total = 0 } = extra ?? {};
   const from = parseUrl(itemData.url);
   const expire = parseExpire(extra?.expire);
@@ -70,18 +71,16 @@ const ProfileItem = (props: Props) => {
   const loading = loadingCache[itemData.uid] ?? false;
 
   const [editOpen, setEditOpen] = useState(false);
-  const onEdit = () => {
+  const [fileOpen, setFileOpen] = useState(false);
+
+  const onEditInfo = () => {
     setAnchorEl(null);
     setEditOpen(true);
   };
 
-  const onView = async () => {
+  const onEditFile = () => {
     setAnchorEl(null);
-    try {
-      await viewProfile(itemData.uid);
-    } catch (err: any) {
-      Notice.error(err?.message || err.toString());
-    }
+    setFileOpen(true);
   };
 
   const onForceSelect = () => {
@@ -89,6 +88,15 @@ const ProfileItem = (props: Props) => {
     onSelect(true);
   };
 
+  const onOpenFile = useLockFn(async () => {
+    setAnchorEl(null);
+    try {
+      await viewProfile(itemData.uid);
+    } catch (err: any) {
+      Notice.error(err?.message || err.toString());
+    }
+  });
+
   const onUpdate = useLockFn(async (withProxy: boolean) => {
     setAnchorEl(null);
     setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
@@ -122,16 +130,18 @@ const ProfileItem = (props: Props) => {
 
   const urlModeMenu = [
     { label: "Select", handler: onForceSelect },
-    { label: "Edit", handler: onEdit },
-    { label: "File", handler: onView },
+    { label: "Edit Info", handler: onEditInfo },
+    { label: "Edit File", handler: onEditFile },
+    { label: "Open File", handler: onOpenFile },
     { label: "Update", handler: () => onUpdate(false) },
     { label: "Update(Proxy)", handler: () => onUpdate(true) },
     { label: "Delete", handler: onDelete },
   ];
   const fileModeMenu = [
     { label: "Select", handler: onForceSelect },
-    { label: "Edit", handler: onEdit },
-    { label: "File", handler: onView },
+    { label: "Edit Info", handler: onEditInfo },
+    { label: "Edit File", handler: onEditFile },
+    { label: "Open File", handler: onOpenFile },
     { label: "Delete", handler: onDelete },
   ];
 
@@ -256,6 +266,7 @@ const ProfileItem = (props: Props) => {
         onClose={() => setAnchorEl(null)}
         anchorPosition={position}
         anchorReference="anchorPosition"
+        transitionDuration={225}
         onContextMenu={(e) => {
           setAnchorEl(null);
           e.preventDefault();
@@ -279,6 +290,15 @@ const ProfileItem = (props: Props) => {
           onClose={() => setEditOpen(false)}
         />
       )}
+
+      {fileOpen && (
+        <FileEditor
+          uid={uid}
+          open={fileOpen}
+          mode="yaml"
+          onClose={() => setFileOpen(false)}
+        />
+      )}
     </>
   );
 };
diff --git a/src/components/profile/profile-more.tsx b/src/components/profile/profile-more.tsx
index 22f5b5df3de692e0bf1042f2e04d9d26791ee188..57a9c075c33bc2ca5614a5ceef796f7f9a7e0d4d 100644
--- a/src/components/profile/profile-more.tsx
+++ b/src/components/profile/profile-more.tsx
@@ -1,6 +1,7 @@
 import dayjs from "dayjs";
 import { useEffect, useState } from "react";
 import { useTranslation } from "react-i18next";
+import { useLockFn } from "ahooks";
 import {
   alpha,
   Box,
@@ -12,9 +13,10 @@ import {
 } from "@mui/material";
 import { CmdType } from "../../services/types";
 import { viewProfile } from "../../services/cmds";
+import enhance from "../../services/enhance";
 import ProfileEdit from "./profile-edit";
+import FileEditor from "./file-editor";
 import Notice from "../base/base-notice";
-import enhance from "../../services/enhance";
 
 const Wrapper = styled(Box)(({ theme }) => ({
   width: "100%",
@@ -57,6 +59,7 @@ const ProfileMore = (props: Props) => {
   const [anchorEl, setAnchorEl] = useState<any>(null);
   const [position, setPosition] = useState({ left: 0, top: 0 });
   const [editOpen, setEditOpen] = useState(false);
+  const [fileOpen, setFileOpen] = useState(false);
   const [status, setStatus] = useState(enhance.status(uid));
 
   // unlisten when unmount
@@ -65,40 +68,47 @@ const ProfileMore = (props: Props) => {
   // error during enhanced mode
   const hasError = selected && status?.status === "error";
 
-  const onEdit = () => {
+  const onEditInfo = () => {
     setAnchorEl(null);
     setEditOpen(true);
   };
 
-  const onView = async () => {
+  const onEditFile = () => {
+    setAnchorEl(null);
+    setFileOpen(true);
+  };
+
+  const onOpenFile = useLockFn(async () => {
     setAnchorEl(null);
     try {
       await viewProfile(itemData.uid);
     } catch (err: any) {
       Notice.error(err?.message || err.toString());
     }
-  };
+  });
 
-  const closeWrapper = (fn: () => void) => () => {
+  const fnWrapper = (fn: () => void) => () => {
     setAnchorEl(null);
     return fn();
   };
 
   const enableMenu = [
-    { label: "Disable", handler: closeWrapper(onDisable) },
-    { label: "Refresh", handler: closeWrapper(onEnhance) },
-    { label: "Edit", handler: onEdit },
-    { label: "File", handler: onView },
-    { label: "To Top", show: !hasError, handler: closeWrapper(onMoveTop) },
-    { label: "To End", show: !hasError, handler: closeWrapper(onMoveEnd) },
-    { label: "Delete", handler: closeWrapper(onDelete) },
+    { label: "Disable", handler: fnWrapper(onDisable) },
+    { label: "Refresh", handler: fnWrapper(onEnhance) },
+    { label: "Edit Info", handler: onEditInfo },
+    { label: "Edit File", handler: onEditFile },
+    { label: "Open File", handler: onOpenFile },
+    { label: "To Top", show: !hasError, handler: fnWrapper(onMoveTop) },
+    { label: "To End", show: !hasError, handler: fnWrapper(onMoveEnd) },
+    { label: "Delete", handler: fnWrapper(onDelete) },
   ];
 
   const disableMenu = [
-    { label: "Enable", handler: closeWrapper(onEnable) },
-    { label: "Edit", handler: onEdit },
-    { label: "File", handler: onView },
-    { label: "Delete", handler: closeWrapper(onDelete) },
+    { label: "Enable", handler: fnWrapper(onEnable) },
+    { label: "Edit Info", handler: onEditInfo },
+    { label: "Edit File", handler: onEditFile },
+    { label: "Open File", handler: onOpenFile },
+    { label: "Delete", handler: fnWrapper(onDelete) },
   ];
 
   const boxStyle = {
@@ -208,6 +218,7 @@ const ProfileMore = (props: Props) => {
         onClose={() => setAnchorEl(null)}
         anchorPosition={position}
         anchorReference="anchorPosition"
+        transitionDuration={225}
         onContextMenu={(e) => {
           setAnchorEl(null);
           e.preventDefault();
@@ -233,6 +244,15 @@ const ProfileMore = (props: Props) => {
           onClose={() => setEditOpen(false)}
         />
       )}
+
+      {fileOpen && (
+        <FileEditor
+          uid={uid}
+          open={fileOpen}
+          mode={type === "merge" ? "yaml" : "javascript"}
+          onClose={() => setFileOpen(false)}
+        />
+      )}
     </>
   );
 };
diff --git a/src/locales/en.json b/src/locales/en.json
index d357734fe67ad50abe31eee2354f2375ecba6ef1..4a2601be9ec723999d265dd220f0ff46d5dfabe6 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -19,8 +19,9 @@
   "New": "New",
   "Close All": "Close All",
   "Select": "Select",
-  "Edit": "Edit",
-  "File": "File",
+  "Edit Info": "Edit Info",
+  "Edit File": "Edit File",
+  "Open File": "Open File",
   "Update": "Update",
   "Update(Proxy)": "Update(Proxy)",
   "Delete": "Delete",
@@ -41,6 +42,7 @@
   "Clash core": "Clash core",
   "Tun Mode": "Tun Mode",
   "Auto Launch": "Auto Launch",
+  "Silent Start": "Silent Start",
   "System Proxy": "System Proxy",
   "Proxy Guard": "Proxy Guard",
   "Proxy Bypass": "Proxy Bypass",
@@ -50,5 +52,8 @@
   "Language": "Language",
   "Open App Dir": "Open App Dir",
   "Open Logs Dir": "Open Logs Dir",
-  "Version": "Version"
+  "Version": "Version",
+
+  "Save": "Save",
+  "Cancel": "Cancel"
 }
diff --git a/src/locales/zh.json b/src/locales/zh.json
index 7ca3a325d842d99f8b6d4df0a2cccca06a86af75..68d1dcbfcc4a443ffe62ab30a6f3f9b6aa1bfa5c 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -19,8 +19,9 @@
   "New": "新建",
   "Close All": "关闭全部",
   "Select": "使用",
-  "Edit": "编辑信息",
-  "File": "打开文件",
+  "Edit Info": "编辑信息",
+  "Edit File": "编辑文件",
+  "Open File": "打开文件",
   "Update": "æ›´æ–°",
   "Update(Proxy)": "更新(代理)",
   "Delete": "删除",
@@ -41,6 +42,7 @@
   "Clash core": "Clash 内核",
   "Tun Mode": "Tun 模式",
   "Auto Launch": "开机自启",
+  "Silent Start": "静默启动",
   "System Proxy": "系统代理",
   "Proxy Guard": "系统代理守卫",
   "Proxy Bypass": "Proxy Bypass",
@@ -50,5 +52,8 @@
   "Language": "语言设置",
   "Open App Dir": "应用目录",
   "Open Logs Dir": "日志目录",
-  "Version": "版本"
+  "Version": "版本",
+
+  "Save": "保存",
+  "Cancel": "取消"
 }
diff --git a/src/services/cmds.ts b/src/services/cmds.ts
index 06a4ecdc88d2cb23a6efdc14f3b9cc4c5d901ccc..cf4729566bd6461c0f14d83fc197f96eb96dff1e 100644
--- a/src/services/cmds.ts
+++ b/src/services/cmds.ts
@@ -25,6 +25,14 @@ export async function viewProfile(index: string) {
   return invoke<void>("view_profile", { index });
 }
 
+export async function readProfileFile(index: string) {
+  return invoke<string>("read_profile_file", { index });
+}
+
+export async function saveProfileFile(index: string, fileData: string) {
+  return invoke<void>("save_profile_file", { index, fileData });
+}
+
 export async function importProfile(url: string) {
   return invoke<void>("import_profile", {
     url,
diff --git a/vite.config.ts b/vite.config.ts
index 8173b2abe435d0be76d59515803ff91f4cafd1c9..97daae4095bb133e6562a61407695d0b6cfa3109 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,10 +1,11 @@
 import { defineConfig } from "vite";
 import react from "@vitejs/plugin-react";
+import monaco from "vite-plugin-monaco-editor";
 
 // https://vitejs.dev/config/
 export default defineConfig({
   root: "src",
-  plugins: [react()],
+  plugins: [react(), monaco()],
   build: {
     outDir: "../dist",
     emptyOutDir: true,
diff --git a/yarn.lock b/yarn.lock
index a92cf69cb3abe66d2052f83da78d9e8c7d827ae2..34eb17a9cdc862afd3a19132fe4a5d60e996c80b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1476,6 +1476,11 @@ minimist@^1.2.5:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
   integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
 
+monaco-editor@^0.33.0:
+  version "0.33.0"
+  resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.33.0.tgz#842e244f3750a2482f8a29c676b5684e75ff34af"
+  integrity sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw==
+
 mri@^1.1.5:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
@@ -1922,6 +1927,11 @@ universalify@^2.0.0:
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
   integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
 
+vite-plugin-monaco-editor@^1.0.10:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.0.10.tgz#cd370f68d4121bced6f902c6284649cc8eca4170"
+  integrity sha512-7yTAFIE0SefjCmfnjrvXOl53wkxeSASc/ZIcB5tZeEK3vAmHhveV8y3f90Vp8b+PYdbUipjqf91mbFbSENkpcw==
+
 vite@^2.8.6:
   version "2.8.6"
   resolved "https://registry.yarnpkg.com/vite/-/vite-2.8.6.tgz#32d50e23c99ca31b26b8ccdc78b1d72d4d7323d3"