Compare commits
25 Commits
v1.6.0
...
dependenci
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -8,13 +8,13 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
## 在提交问题之前,请确认以下事项:
|
## 在提交问题之前,请确认以下事项:
|
||||||
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq.html)
|
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/install/)
|
||||||
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
|
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似issue,否则请在已有的issue下进行讨论
|
||||||
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
|
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
|
||||||
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保问题依然存在
|
4. 请 **务必** 先下载 [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) 版本测试,确保问题依然存在
|
||||||
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
|
5. 请 **务必** 按照模板规范详细描述问题,否则issue将会被关闭
|
||||||
## Before submitting the issue, please make sure of the following checklist:
|
## Before submitting the issue, please make sure of the following checklist:
|
||||||
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide.html) and [FAQ](https://clash-verge-rev.github.io/faq.html)
|
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide.html) and [FAQ](https://clash-verge-rev.github.io/faq/install/)
|
||||||
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
|
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
|
||||||
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
|
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
|
||||||
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the problem still exists
|
4. Please be sure to download the [Alpha](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/alpha) version for testing to ensure that the problem still exists
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
4
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
contact_links:
|
||||||
|
- name: 讨论交流 / Communication
|
||||||
|
url: https://t.me/clash_verge_rev
|
||||||
|
about: 在 Telegram 群组中与其他用户讨论交流 / Communicate with other users in the Telegram group
|
||||||
4
.github/workflows/alpha.yml
vendored
4
.github/workflows/alpha.yml
vendored
@@ -127,7 +127,7 @@ jobs:
|
|||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
- [https://clash-verge-rev.github.io/faq.html](https://clash-verge-rev.github.io/faq.html)
|
- [FAQ](https://clash-verge-rev.github.io/faq/install/)
|
||||||
|
|
||||||
Created at ${{ env.BUILDTIME }}.
|
Created at ${{ env.BUILDTIME }}.
|
||||||
EOF
|
EOF
|
||||||
@@ -189,7 +189,7 @@ jobs:
|
|||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
- [https://clash-verge-rev.github.io/faq.html](https://clash-verge-rev.github.io/faq.html)
|
- [FAQ](https://clash-verge-rev.github.io/faq/install/)
|
||||||
|
|
||||||
Created at ${{ env.BUILDTIME }}.
|
Created at ${{ env.BUILDTIME }}.
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
|
|||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq.html)
|
Refer to [Doc FAQ Page](https://clash-verge-rev.github.io/faq/install/)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|||||||
17
UPDATELOG.md
17
UPDATELOG.md
@@ -1,3 +1,20 @@
|
|||||||
|
## v1.6.1
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- 鼠标悬浮显示当前订阅的名称 [#938](https://github.com/clash-verge-rev/clash-verge-rev/pull/938)
|
||||||
|
- 日志过滤支持正则表达式 [#959](https://github.com/clash-verge-rev/clash-verge-rev/pull/959)
|
||||||
|
- 更新 Clash 内核到 1.18.4
|
||||||
|
|
||||||
|
### Bugs Fixes
|
||||||
|
|
||||||
|
- 修复 Linux KDE 环境下系统代理无法开启的问题
|
||||||
|
- 窗口最大化图标调整 [#924](https://github.com/clash-verge-rev/clash-verge-rev/pull/924)
|
||||||
|
- 修改 MacOS 托盘点击行为(左键菜单,右键点击事件)
|
||||||
|
- 修复 MacOS 服务模式安装失败的问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v1.6.0
|
## v1.6.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|||||||
52
package.json
52
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "clash-verge",
|
"name": "clash-verge",
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tauri dev",
|
"dev": "tauri dev",
|
||||||
@@ -19,48 +19,48 @@
|
|||||||
"@dnd-kit/core": "^6.1.0",
|
"@dnd-kit/core": "^6.1.0",
|
||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@emotion/react": "^11.11.3",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"@mui/icons-material": "^5.15.5",
|
"@mui/icons-material": "^5.15.15",
|
||||||
"@mui/lab": "5.0.0-alpha.149",
|
"@mui/lab": "5.0.0-alpha.149",
|
||||||
"@mui/material": "^5.15.5",
|
"@mui/material": "^5.15.15",
|
||||||
"@mui/x-data-grid": "^6.18.7",
|
"@mui/x-data-grid": "^6.19.11",
|
||||||
"@tauri-apps/api": "^1.5.3",
|
"@tauri-apps/api": "^1.5.4",
|
||||||
"@types/json-schema": "^7.0.15",
|
"@types/json-schema": "^7.0.15",
|
||||||
"ahooks": "^3.7.8",
|
"ahooks": "^3.7.11",
|
||||||
"axios": "^1.6.5",
|
"axios": "^1.6.8",
|
||||||
"dayjs": "1.11.5",
|
"dayjs": "1.11.5",
|
||||||
"i18next": "^23.7.16",
|
"i18next": "^23.11.2",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"meta-json-schema": "^1.18.3-beta",
|
"meta-json-schema": "1.18.4-beta2",
|
||||||
"monaco-editor": "^0.47.0",
|
"monaco-editor": "^0.47.0",
|
||||||
"monaco-yaml": "^5.1.1",
|
"monaco-yaml": "^5.1.1",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"react-error-boundary": "^3.1.4",
|
"react-error-boundary": "^3.1.4",
|
||||||
"react-hook-form": "^7.49.3",
|
"react-hook-form": "^7.51.3",
|
||||||
"react-i18next": "^13.5.0",
|
"react-i18next": "^13.5.0",
|
||||||
"react-router-dom": "^6.21.2",
|
"react-markdown": "^9.0.1",
|
||||||
|
"react-router-dom": "^6.23.0",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"react-virtuoso": "^4.6.2",
|
"react-virtuoso": "^4.7.10",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
"snarkdown": "^2.0.0",
|
|
||||||
"swr": "^1.3.0",
|
"swr": "^1.3.0",
|
||||||
"tar": "^6.2.0"
|
"tar": "^6.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/github": "^5.1.1",
|
"@actions/github": "^5.1.1",
|
||||||
"@tauri-apps/cli": "^1.5.9",
|
"@tauri-apps/cli": "^1.5.12",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/js-cookie": "^3.0.6",
|
"@types/js-cookie": "^3.0.6",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.3.1",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/react-transition-group": "^4.4.10",
|
"@types/react-transition-group": "^4.4.10",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"adm-zip": "^0.5.10",
|
"adm-zip": "^0.5.12",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.2.0",
|
||||||
"https-proxy-agent": "^5.0.1",
|
"https-proxy-agent": "^5.0.1",
|
||||||
@@ -68,9 +68,9 @@
|
|||||||
"node-fetch": "^3.3.2",
|
"node-fetch": "^3.3.2",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"pretty-quick": "^3.3.1",
|
"pretty-quick": "^3.3.1",
|
||||||
"sass": "^1.70.0",
|
"sass": "^1.75.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.4.5",
|
||||||
"vite": "^5.0.11",
|
"vite": "^5.2.10",
|
||||||
"vite-plugin-monaco-editor": "^1.1.0",
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vite-plugin-svgr": "^4.2.0"
|
"vite-plugin-svgr": "^4.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
1673
pnpm-lock.yaml
generated
1673
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -100,7 +100,7 @@ async function getLatestAlphaVersion() {
|
|||||||
|
|
||||||
/* ======= clash meta stable ======= */
|
/* ======= clash meta stable ======= */
|
||||||
const META_VERSION_URL =
|
const META_VERSION_URL =
|
||||||
"https://github.com/MetaCubeX/mihomo/releases/download/v1.18.1/version.txt";
|
"https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt";
|
||||||
const META_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download`;
|
const META_URL_PREFIX = `https://github.com/MetaCubeX/mihomo/releases/download`;
|
||||||
let META_VERSION;
|
let META_VERSION;
|
||||||
|
|
||||||
|
|||||||
1493
src-tauri/Cargo.lock
generated
1493
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "1.6.0"
|
version = "1.6.1"
|
||||||
description = "clash verge"
|
description = "clash verge"
|
||||||
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
@@ -37,7 +37,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
|
||||||
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
|
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
|
||||||
auto-launch = { git="https://github.com/zzzgydi/auto-launch", branch = "main" }
|
auto-launch = { git="https://github.com/zzzgydi/auto-launch", branch = "main" }
|
||||||
tauri = { version = "1.6", features = [ "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
|
tauri = { version = "1.6", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
runas = "=1.2.0"
|
runas = "=1.2.0"
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult {
|
|||||||
match CoreManager::global().update_config().await {
|
match CoreManager::global().update_config().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
handle::Handle::refresh_clash();
|
handle::Handle::refresh_clash();
|
||||||
|
let _ = handle::Handle::update_systray_part();
|
||||||
Config::profiles().apply();
|
Config::profiles().apply();
|
||||||
wrap_err!(Config::profiles().data().save_file())?;
|
wrap_err!(Config::profiles().data().save_file())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ pub async fn install_service() -> Result<()> {
|
|||||||
if !installer_path.exists() {
|
if !installer_path.exists() {
|
||||||
bail!("installer not found");
|
bail!("installer not found");
|
||||||
}
|
}
|
||||||
let shell = installer_path.to_string_lossy();
|
let shell = installer_path.to_string_lossy().replace(" ", "\\\\ ");
|
||||||
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
|
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
|
||||||
|
|
||||||
let status = StdCommand::new("osascript")
|
let status = StdCommand::new("osascript")
|
||||||
|
|||||||
@@ -261,18 +261,30 @@ impl Tray {
|
|||||||
map
|
map
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut current_profile_name = "None".to_string();
|
||||||
|
let profiles = Config::profiles();
|
||||||
|
let profiles = profiles.latest();
|
||||||
|
if let Some(current_profile_uid) = profiles.get_current() {
|
||||||
|
let current_profile = profiles.get_item(¤t_profile_uid);
|
||||||
|
current_profile_name = match ¤t_profile.unwrap().name {
|
||||||
|
Some(profile_name) => profile_name.to_string(),
|
||||||
|
None => current_profile_name,
|
||||||
|
};
|
||||||
|
};
|
||||||
let _ = tray.set_tooltip(&format!(
|
let _ = tray.set_tooltip(&format!(
|
||||||
"Clash Verge {version}\n{}: {}\n{}: {}",
|
"Clash Verge {version}\n{}: {}\n{}: {}\n{}: {}",
|
||||||
t!("System Proxy", "系统代理"),
|
t!("System Proxy", "系统代理"),
|
||||||
switch_map[system_proxy],
|
switch_map[system_proxy],
|
||||||
t!("TUN Mode", "Tun 模式"),
|
t!("TUN Mode", "Tun 模式"),
|
||||||
switch_map[tun_mode]
|
switch_map[tun_mode],
|
||||||
|
t!("Curent Profile", "当前订阅"),
|
||||||
|
current_profile_name
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_left_click(app_handle: &AppHandle) {
|
pub fn on_click(app_handle: &AppHandle) {
|
||||||
let tray_event = { Config::verge().latest().tray_event.clone() };
|
let tray_event = { Config::verge().latest().tray_event.clone() };
|
||||||
let tray_event = tray_event.unwrap_or("main_window".into());
|
let tray_event = tray_event.unwrap_or("main_window".into());
|
||||||
match tray_event.as_str() {
|
match tray_event.as_str() {
|
||||||
@@ -285,7 +297,10 @@ impl Tray {
|
|||||||
|
|
||||||
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
|
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
|
||||||
match event {
|
match event {
|
||||||
SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
SystemTrayEvent::LeftClick { .. } => Tray::on_click(app_handle),
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
SystemTrayEvent::RightClick { .. } => Tray::on_click(app_handle),
|
||||||
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
|
||||||
mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
|
mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
|
||||||
let mode = &mode[0..mode.len() - 5];
|
let mode = &mode[0..mode.len() - 5];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
function main(params) {
|
function main(config) {
|
||||||
if (params.mode === "script") {
|
if (config.mode === "script") {
|
||||||
params.mode = "rule";
|
config.mode = "rule";
|
||||||
}
|
}
|
||||||
return params;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
function main(params) {
|
function main(config) {
|
||||||
if (Array.isArray(params.proxies)) {
|
if (Array.isArray(config.proxies)) {
|
||||||
params.proxies.forEach((p, i) => {
|
config.proxies.forEach((p, i) => {
|
||||||
if (p.type === "hysteria" && typeof p.alpn === "string") {
|
if (p.type === "hysteria" && typeof p.alpn === "string") {
|
||||||
params.proxies[i].alpn = [p.alpn];
|
config.proxies[i].alpn = [p.alpn];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return params;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,8 +42,19 @@ pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
|||||||
let resource_dir = dirs::app_resources_dir().unwrap();
|
let resource_dir = dirs::app_resources_dir().unwrap();
|
||||||
let script = resource_dir.join("set_dns.sh");
|
let script = resource_dir.join("set_dns.sh");
|
||||||
let script = script.to_string_lossy();
|
let script = script.to_string_lossy();
|
||||||
match Command::new("bash").args([script]).output() {
|
match Command::new("bash")
|
||||||
Ok(_) => log::info!(target: "app", "set system dns successfully"),
|
.args([script])
|
||||||
|
.current_dir(resource_dir)
|
||||||
|
.status()
|
||||||
|
{
|
||||||
|
Ok(status) => {
|
||||||
|
if status.success() {
|
||||||
|
log::info!(target: "app", "set system dns successfully");
|
||||||
|
} else {
|
||||||
|
let code = status.code().unwrap_or(-1);
|
||||||
|
log::error!(target: "app", "set system dns failed: {code}");
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!(target: "app", "set system dns failed: {err}");
|
log::error!(target: "app", "set system dns failed: {err}");
|
||||||
}
|
}
|
||||||
@@ -59,8 +70,19 @@ pub fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
|
|||||||
let resource_dir = dirs::app_resources_dir().unwrap();
|
let resource_dir = dirs::app_resources_dir().unwrap();
|
||||||
let script = resource_dir.join("unset_dns.sh");
|
let script = resource_dir.join("unset_dns.sh");
|
||||||
let script = script.to_string_lossy();
|
let script = script.to_string_lossy();
|
||||||
match Command::new("bash").args([script]).output() {
|
match Command::new("bash")
|
||||||
Ok(_) => log::info!(target: "app", "unset system dns successfully"),
|
.args([script])
|
||||||
|
.current_dir(resource_dir)
|
||||||
|
.status()
|
||||||
|
{
|
||||||
|
Ok(status) => {
|
||||||
|
if status.success() {
|
||||||
|
log::info!(target: "app", "unset system dns successfully");
|
||||||
|
} else {
|
||||||
|
let code = status.code().unwrap_or(-1);
|
||||||
|
log::error!(target: "app", "unset system dns failed: {code}");
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!(target: "app", "unset system dns failed: {err}");
|
log::error!(target: "app", "unset system dns failed: {err}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ pub fn copy_clash_env(app_handle: &AppHandle) {
|
|||||||
|
|
||||||
let sh =
|
let sh =
|
||||||
format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
|
format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
|
||||||
let cmd: String = format!("set http_proxy={http_proxy} \n set https_proxy={http_proxy}");
|
let cmd: String = format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}");
|
||||||
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
|
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
|
||||||
|
|
||||||
let mut cliboard = app_handle.clipboard_manager();
|
let mut cliboard = app_handle.clipboard_manager();
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ fn main() -> std::io::Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1");
|
||||||
|
|
||||||
crate::log_err!(init::init_config());
|
crate::log_err!(init::init_config());
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
//! Some config file template
|
//! Some config file template
|
||||||
|
|
||||||
/// template for new a profile item
|
/// template for new a profile item
|
||||||
pub const ITEM_LOCAL: &str = "# Profile Template for clash verge
|
pub const ITEM_LOCAL: &str = "# Profile Template for Clash Verge
|
||||||
|
|
||||||
proxies:
|
proxies: []
|
||||||
|
|
||||||
proxy-groups:
|
proxy-groups: []
|
||||||
|
|
||||||
rules:
|
rules: []
|
||||||
";
|
";
|
||||||
|
|
||||||
/// enhanced profile
|
/// enhanced profile
|
||||||
pub const ITEM_MERGE: &str = "# Merge Template for clash verge
|
pub const ITEM_MERGE: &str = "# Profile Enhancement Merge Template for Clash Verge
|
||||||
# The `Merge` format used to enhance profile
|
|
||||||
|
|
||||||
prepend-rules: []
|
prepend-rules: []
|
||||||
|
|
||||||
@@ -36,9 +35,9 @@ append-proxy-groups: []
|
|||||||
";
|
";
|
||||||
|
|
||||||
/// enhanced profile
|
/// enhanced profile
|
||||||
pub const ITEM_SCRIPT: &str = "// Define the `main` function
|
pub const ITEM_SCRIPT: &str = "// Define main function (script entry)
|
||||||
|
|
||||||
function main(params) {
|
function main(config) {
|
||||||
return params;
|
return config;
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "Clash Verge",
|
"productName": "Clash Verge",
|
||||||
"version": "1.6.0"
|
"version": "1.6.1"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"distDir": "../dist",
|
"distDir": "../dist",
|
||||||
@@ -69,7 +69,8 @@
|
|||||||
},
|
},
|
||||||
"fs": {
|
"fs": {
|
||||||
"exists": true,
|
"exists": true,
|
||||||
"scope": ["$APPDATA/**", "$RESOURCE/../**"]
|
"readFile": true,
|
||||||
|
"scope": ["$APPDATA/**", "$RESOURCE/../**", "**"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"windows": [],
|
"windows": [],
|
||||||
|
|||||||
@@ -8,18 +8,29 @@ import {
|
|||||||
PushPinOutlined,
|
PushPinOutlined,
|
||||||
PushPinRounded,
|
PushPinRounded,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export const LayoutControl = () => {
|
export const LayoutControl = () => {
|
||||||
const minWidth = 40;
|
const minWidth = 40;
|
||||||
|
|
||||||
const [isMaximized, setIsMaximized] = useState(false);
|
const [isMaximized, setIsMaximized] = useState(false);
|
||||||
const [isPined, setIsPined] = useState(false);
|
const [isPined, setIsPined] = useState(false);
|
||||||
appWindow.onResized(() => {
|
|
||||||
appWindow.isMaximized().then((isMaximized) => {
|
useEffect(() => {
|
||||||
setIsMaximized(() => isMaximized);
|
const unlistenResized = appWindow.onResized(() => {
|
||||||
|
appWindow.isMaximized().then((maximized) => {
|
||||||
|
setIsMaximized(() => maximized);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
appWindow.isMaximized().then((maximized) => {
|
||||||
|
setIsMaximized(() => maximized);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unlistenResized.then((fn) => fn());
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
|
|||||||
@@ -86,12 +86,15 @@ export const ServiceViewer = forwardRef<DialogRef, Props>((props, ref) => {
|
|||||||
disableFooter
|
disableFooter
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
>
|
>
|
||||||
<Typography>Current State: {state}</Typography>
|
<Typography>
|
||||||
|
{t("Current State")}: {t(state)}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
{(state === "unknown" || state === "uninstall") && (
|
{(state === "unknown" || state === "uninstall") && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Information: Please make sure that the Clash Verge Service is
|
{t(
|
||||||
installed and enabled
|
"Information: Please make sure that the Clash Verge Service is installed and enabled"
|
||||||
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -102,19 +105,19 @@ export const ServiceViewer = forwardRef<DialogRef, Props>((props, ref) => {
|
|||||||
>
|
>
|
||||||
{state === "uninstall" && enable && (
|
{state === "uninstall" && enable && (
|
||||||
<Button variant="contained" onClick={onDisable}>
|
<Button variant="contained" onClick={onDisable}>
|
||||||
Disable Service Mode
|
{t("Disable Service Mode")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{state === "uninstall" && (
|
{state === "uninstall" && (
|
||||||
<Button variant="contained" onClick={onInstall}>
|
<Button variant="contained" onClick={onInstall}>
|
||||||
Install
|
{t("Install")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(state === "active" || state === "installed") && (
|
{(state === "active" || state === "installed") && (
|
||||||
<Button variant="outlined" onClick={onUninstall}>
|
<Button variant="outlined" onClick={onUninstall}>
|
||||||
Uninstall
|
{t("Uninstall")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { useClash } from "@/hooks/use-clash";
|
import { useClash } from "@/hooks/use-clash";
|
||||||
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
|
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
|
||||||
import { StackModeSwitch } from "./stack-mode-switch";
|
import { StackModeSwitch } from "./stack-mode-switch";
|
||||||
|
import { enhanceProfiles } from "@/services/cmds";
|
||||||
|
|
||||||
export const TunViewer = forwardRef<DialogRef>((props, ref) => {
|
export const TunViewer = forwardRef<DialogRef>((props, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -65,6 +66,12 @@ export const TunViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
}),
|
}),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
|
await enhanceProfiles();
|
||||||
|
Notice.success("Refresh clash config", 1000);
|
||||||
|
} catch (err: any) {
|
||||||
|
Notice.error(err.message || err.toString(), 3000);
|
||||||
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Notice.error(err.message || err.toString());
|
Notice.error(err.message || err.toString());
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import snarkdown from "snarkdown";
|
|
||||||
import { forwardRef, useImperativeHandle, useState, useMemo } from "react";
|
import { forwardRef, useImperativeHandle, useState, useMemo } from "react";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { Box, LinearProgress, styled } from "@mui/material";
|
import { Box, LinearProgress } from "@mui/material";
|
||||||
import { useRecoilState } from "recoil";
|
import { useRecoilState } from "recoil";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { relaunch } from "@tauri-apps/api/process";
|
import { relaunch } from "@tauri-apps/api/process";
|
||||||
@@ -11,10 +10,8 @@ import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
|||||||
import { atomUpdateState } from "@/services/states";
|
import { atomUpdateState } from "@/services/states";
|
||||||
import { listen, Event, UnlistenFn } from "@tauri-apps/api/event";
|
import { listen, Event, UnlistenFn } from "@tauri-apps/api/event";
|
||||||
import { portableFlag } from "@/pages/_layout";
|
import { portableFlag } from "@/pages/_layout";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
const UpdateLog = styled(Box)(() => ({
|
|
||||||
"h1,h2,h3,ul,ol,p": { margin: "0.5em 0", color: "inherit" },
|
|
||||||
}));
|
|
||||||
let eventListener: UnlistenFn | null = null;
|
let eventListener: UnlistenFn | null = null;
|
||||||
|
|
||||||
export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
||||||
@@ -38,12 +35,11 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
close: () => setOpen(false),
|
close: () => setOpen(false),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// markdown parser
|
const markdownContent = useMemo(() => {
|
||||||
const parseContent = useMemo(() => {
|
|
||||||
if (!updateInfo?.manifest?.body) {
|
if (!updateInfo?.manifest?.body) {
|
||||||
return "New Version is available";
|
return "New Version is available";
|
||||||
}
|
}
|
||||||
return snarkdown(updateInfo?.manifest?.body);
|
return updateInfo?.manifest?.body;
|
||||||
}, [updateInfo]);
|
}, [updateInfo]);
|
||||||
|
|
||||||
const onUpdate = useLockFn(async () => {
|
const onUpdate = useLockFn(async () => {
|
||||||
@@ -87,10 +83,22 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
onCancel={() => setOpen(false)}
|
onCancel={() => setOpen(false)}
|
||||||
onOk={onUpdate}
|
onOk={onUpdate}
|
||||||
>
|
>
|
||||||
<UpdateLog
|
<Box sx={{ height: "calc(100% - 10px)", overflow: "auto" }}>
|
||||||
dangerouslySetInnerHTML={{ __html: parseContent }}
|
<ReactMarkdown
|
||||||
sx={{ height: "calc(100% - 10px)", overflow: "auto" }}
|
components={{
|
||||||
/>
|
a: ({ node, ...props }) => {
|
||||||
|
const { children } = props;
|
||||||
|
return (
|
||||||
|
<a {...props} target="_blank">
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{markdownContent}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</Box>
|
||||||
{updateState && (
|
{updateState && (
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
variant="buffer"
|
variant="buffer"
|
||||||
|
|||||||
@@ -185,7 +185,20 @@
|
|||||||
"MTU": "Max Transmission Unit",
|
"MTU": "Max Transmission Unit",
|
||||||
"Reset to Default": "Reset to Default",
|
"Reset to Default": "Reset to Default",
|
||||||
|
|
||||||
|
"Current State": "Current State",
|
||||||
|
"pending": "pending",
|
||||||
|
"installed": "installed",
|
||||||
|
"uninstall": "uninstalled",
|
||||||
|
"active": "active",
|
||||||
|
"unknown": "unknown",
|
||||||
|
"Disable Service Mode": "Disable Service Mode",
|
||||||
|
"Install": "Install",
|
||||||
|
"Uninstall": "Uninstall",
|
||||||
|
|
||||||
"Portable Updater Error": "The portable version does not support in-app updates. Please manually download and replace it",
|
"Portable Updater Error": "The portable version does not support in-app updates. Please manually download and replace it",
|
||||||
"Tun Mode Info": "The Tun mode requires granting core-related permissions. Please enable service mode before using it",
|
"Tun Mode Info": "The Tun mode requires granting core-related permissions. Please enable service mode before using it",
|
||||||
"System and Mixed Can Only be Used in Service Mode": "System and Mixed Can Only be Used in Service Mode"
|
"System and Mixed Can Only be Used in Service Mode": "System and Mixed Can Only be Used in Service Mode",
|
||||||
|
"Information: Please make sure that the Clash Verge Service is installed and enabled": "Information: Please make sure that the Clash Verge Service is installed and enabled",
|
||||||
|
|
||||||
|
"Use Regular Expression": "Use Regular Expression"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,20 @@
|
|||||||
"MTU": "Максимальная единица передачи",
|
"MTU": "Максимальная единица передачи",
|
||||||
"Reset to Default": "Сбросить настройки по умолчанию",
|
"Reset to Default": "Сбросить настройки по умолчанию",
|
||||||
|
|
||||||
|
"Current State": "Текущее состояние",
|
||||||
|
"pending": "Ожидающий",
|
||||||
|
"installed": "Установленный",
|
||||||
|
"uninstall": "Не установленный",
|
||||||
|
"active": "Активированный",
|
||||||
|
"unknown": "неизвестный",
|
||||||
|
"Disable Service Mode": "Отключить режим обслуживания",
|
||||||
|
"Install": "Установить",
|
||||||
|
"Uninstall": "Удалить",
|
||||||
|
|
||||||
"Portable Updater Error": "Портативная версия не поддерживает обновление внутри приложения, пожалуйста, скачайте и замените вручную",
|
"Portable Updater Error": "Портативная версия не поддерживает обновление внутри приложения, пожалуйста, скачайте и замените вручную",
|
||||||
"Tun Mode Info": "Режим туннеля требует предоставления разрешений, связанных с ядром. Пожалуйста, включите сервисный режим перед его использованием",
|
"Tun Mode Info": "Режим туннеля требует предоставления разрешений, связанных с ядром. Пожалуйста, включите сервисный режим перед его использованием",
|
||||||
"System and Mixed Can Only be Used in Service Mode": "Система и смешанные могут использоваться только в сервисном режиме"
|
"System and Mixed Can Only be Used in Service Mode": "Система и смешанные могут использоваться только в сервисном режиме",
|
||||||
|
"Information: Please make sure that the Clash Verge Service is installed and enabled": "Информация: Пожалуйста, убедитесь, что сервис Clash Verge Service установлен и включен",
|
||||||
|
|
||||||
|
"Use Regular Expression": "Использование регулярных выражений"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,20 @@
|
|||||||
"MTU": "最大传输单元",
|
"MTU": "最大传输单元",
|
||||||
"Reset to Default": "重置为默认值",
|
"Reset to Default": "重置为默认值",
|
||||||
|
|
||||||
|
"Current State": "当前状态",
|
||||||
|
"pending": "等待中",
|
||||||
|
"installed": "已安装",
|
||||||
|
"uninstall": "未安装",
|
||||||
|
"active": "已激活",
|
||||||
|
"unknown": "未知",
|
||||||
|
"Disable Service Mode": "禁用服务模式",
|
||||||
|
"Install": "安装",
|
||||||
|
"Uninstall": "卸载",
|
||||||
|
|
||||||
"Portable Updater Error": "便携版不支持应用内更新,请手动下载替换",
|
"Portable Updater Error": "便携版不支持应用内更新,请手动下载替换",
|
||||||
"Tun Mode Info": "Tun模式需要授予内核相关权限,使用前请先开启服务模式",
|
"Tun Mode Info": "Tun模式需要授予内核相关权限,使用前请先开启服务模式",
|
||||||
"System and Mixed Can Only be Used in Service Mode": "System和Mixed只能在服务模式下使用"
|
"System and Mixed Can Only be Used in Service Mode": "System和Mixed只能在服务模式下使用",
|
||||||
|
"Information: Please make sure that the Clash Verge Service is installed and enabled": "提示信息: 请确保Clash Verge Service已安装并启用",
|
||||||
|
|
||||||
|
"Use Regular Expression": "使用正则表达式"
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/main.tsx
13
src/main.tsx
@@ -24,6 +24,19 @@ if (!container) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
// Disable WebView keyboard shortcuts
|
||||||
|
if (["F5", "F7"].includes(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(event.ctrlKey || event.metaKey) &&
|
||||||
|
["F", "H", "P", "R", "U"].includes(event.key.toUpperCase())
|
||||||
|
) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
createRoot(container).render(
|
createRoot(container).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<RecoilRoot>
|
<RecoilRoot>
|
||||||
|
|||||||
@@ -47,8 +47,27 @@ const LogPage = () => {
|
|||||||
const isDark = theme.palette.mode === "dark";
|
const isDark = theme.palette.mode === "dark";
|
||||||
const [logState, setLogState] = useState("all");
|
const [logState, setLogState] = useState("all");
|
||||||
const [filterText, setFilterText] = useState("");
|
const [filterText, setFilterText] = useState("");
|
||||||
|
const [useRegexSearch, setUseRegexSearch] = useState(true);
|
||||||
|
const [hasInputError, setInputError] = useState(false);
|
||||||
|
const [inputHelperText, setInputHelperText] = useState("");
|
||||||
const filterLogs = useMemo(() => {
|
const filterLogs = useMemo(() => {
|
||||||
|
setInputHelperText("");
|
||||||
|
setInputError(false);
|
||||||
|
if (useRegexSearch) {
|
||||||
|
try {
|
||||||
|
const regex = new RegExp(filterText);
|
||||||
|
return logData.filter((data) => {
|
||||||
|
return (
|
||||||
|
regex.test(data.payload) &&
|
||||||
|
(logState === "all" ? true : data.type.includes(logState))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
setInputHelperText(err.message.substring(0, 60));
|
||||||
|
setInputError(true);
|
||||||
|
return logData;
|
||||||
|
}
|
||||||
|
}
|
||||||
return logData.filter((data) => {
|
return logData.filter((data) => {
|
||||||
return (
|
return (
|
||||||
data.payload.includes(filterText) &&
|
data.payload.includes(filterText) &&
|
||||||
@@ -107,8 +126,24 @@ const LogPage = () => {
|
|||||||
</StyledSelect>
|
</StyledSelect>
|
||||||
|
|
||||||
<BaseStyledTextField
|
<BaseStyledTextField
|
||||||
|
error={hasInputError}
|
||||||
value={filterText}
|
value={filterText}
|
||||||
onChange={(e) => setFilterText(e.target.value)}
|
onChange={(e) => setFilterText(e.target.value)}
|
||||||
|
helperText={inputHelperText}
|
||||||
|
placeholder={t("Filter conditions")}
|
||||||
|
InputProps={{
|
||||||
|
sx: { pr: 1 },
|
||||||
|
endAdornment: (
|
||||||
|
<IconButton
|
||||||
|
sx={{ fontWeight: "800", height: "100%", padding: "0" }}
|
||||||
|
color={useRegexSearch ? "primary" : "default"}
|
||||||
|
title={t("Use Regular Expression")}
|
||||||
|
onClick={() => setUseRegexSearch(!useRegexSearch)}
|
||||||
|
>
|
||||||
|
.*
|
||||||
|
</IconButton>
|
||||||
|
),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
import { useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { useSetRecoilState } from "recoil";
|
import { useSetRecoilState } from "recoil";
|
||||||
import { Box, Button, Grid, IconButton, Stack, Divider } from "@mui/material";
|
import { Box, Button, Grid, IconButton, Stack, Divider } from "@mui/material";
|
||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
deleteProfile,
|
deleteProfile,
|
||||||
updateProfile,
|
updateProfile,
|
||||||
reorderProfile,
|
reorderProfile,
|
||||||
|
createProfile,
|
||||||
} from "@/services/cmds";
|
} from "@/services/cmds";
|
||||||
import { atomLoadingCache } from "@/services/states";
|
import { atomLoadingCache } from "@/services/states";
|
||||||
import { closeAllConnections } from "@/services/api";
|
import { closeAllConnections } from "@/services/api";
|
||||||
@@ -49,6 +50,8 @@ import { throttle } from "lodash-es";
|
|||||||
import { useRecoilState } from "recoil";
|
import { useRecoilState } from "recoil";
|
||||||
import { atomThemeMode } from "@/services/states";
|
import { atomThemeMode } from "@/services/states";
|
||||||
import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
|
import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
|
||||||
|
import { listen } from "@tauri-apps/api/event";
|
||||||
|
import { readTextFile } from "@tauri-apps/api/fs";
|
||||||
|
|
||||||
const ProfilePage = () => {
|
const ProfilePage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -63,6 +66,35 @@ const ProfilePage = () => {
|
|||||||
coordinateGetter: sortableKeyboardCoordinates,
|
coordinateGetter: sortableKeyboardCoordinates,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unlisten = listen("tauri://file-drop", async (event) => {
|
||||||
|
const fileList = event.payload as string[];
|
||||||
|
for (let file of fileList) {
|
||||||
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) {
|
||||||
|
Notice.error("Only support YAML files.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const item = {
|
||||||
|
type: "local",
|
||||||
|
name: file.split(/\/|\\/).pop() ?? "New Profile",
|
||||||
|
desc: "",
|
||||||
|
url: "",
|
||||||
|
option: {
|
||||||
|
with_proxy: false,
|
||||||
|
self_proxy: false,
|
||||||
|
},
|
||||||
|
} as IProfileItem;
|
||||||
|
let data = await readTextFile(file);
|
||||||
|
await createProfile(item, data);
|
||||||
|
await mutateProfiles();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
unlisten.then((fn) => fn());
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
profiles = {},
|
profiles = {},
|
||||||
activateSelected,
|
activateSelected,
|
||||||
|
|||||||
Reference in New Issue
Block a user