diff --git a/Changelog.md b/Changelog.md index ad1e120f2..b221553b0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -26,5 +26,6 @@ - 应用内更新日志支持解析并渲染 HTML 标签 - 性能优化前后端在渲染流量图时的资源 - 在 Linux NVIDIA 显卡环境下尝试禁用 WebKit DMABUF 渲染以规避潜在问题 +- Windows 下自启动改为计划任务实现 diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index cb877fbe3..03d5cbdd8 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -113,10 +113,12 @@ winapi = { version = "0.3.9", features = [ "errhandlingapi", "minwindef", "winerror", + "stringapiset", "tlhelp32", "processthreadsapi", "winhttp", "winreg", + "winnls", ] } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] diff --git a/src-tauri/src/core/sysopt.rs b/src-tauri/src/core/sysopt.rs index ca1a35200..6fe766884 100644 --- a/src-tauri/src/core/sysopt.rs +++ b/src-tauri/src/core/sysopt.rs @@ -1,12 +1,14 @@ #[cfg(target_os = "windows")] -use crate::utils::autostart as startup_shortcut; +use crate::utils::schtasks as startup_task; use crate::{ config::{Config, IVerge}, core::handle::Handle, singleton, }; use anyhow::Result; -use clash_verge_logging::{Type, logging, logging_error}; +#[cfg(not(target_os = "windows"))] +use clash_verge_logging::logging_error; +use clash_verge_logging::{Type, logging}; use parking_lot::RwLock; use scopeguard::defer; use smartstring::alias::String; @@ -18,7 +20,10 @@ use std::{ time::Duration, }; use sysproxy::{Autoproxy, GuardMonitor, GuardType, Sysproxy}; +#[cfg(not(target_os = "windows"))] use tauri_plugin_autostart::ManagerExt as _; +#[cfg(target_os = "windows")] +use tauri_plugin_clash_verge_sysinfo::is_current_app_handle_admin; pub struct Sysopt { update_sysproxy: AtomicBool, @@ -230,35 +235,21 @@ impl Sysopt { let is_enable = enable_auto_launch.unwrap_or(false); logging!(info, Type::System, "Setting auto-launch state to: {:?}", is_enable); - // 首先尝试使用快捷方式方法 #[cfg(target_os = "windows")] { - if is_enable { - if let Err(e) = startup_shortcut::create_shortcut().await { - logging!(error, Type::Setup, "创建启动快捷方式失败: {e}"); - // 如果快捷方式创建失败,回退到原来的方法 - self.try_original_autostart_method(is_enable); - } else { - return Ok(()); - } - } else if let Err(e) = startup_shortcut::remove_shortcut().await { - logging!(error, Type::Setup, "删除启动快捷方式失败: {e}"); - self.try_original_autostart_method(is_enable); - } else { - return Ok(()); - } + let is_admin = is_current_app_handle_admin(Handle::app_handle()); + startup_task::set_auto_launch(is_enable, is_admin).await } #[cfg(not(target_os = "windows"))] { - // 非Windows平台使用原来的方法 self.try_original_autostart_method(is_enable); + Ok(()) } - - Ok(()) } /// 尝试使用原来的自启动方法 + #[cfg(not(target_os = "windows"))] fn try_original_autostart_method(&self, is_enable: bool) { let app_handle = Handle::app_handle(); let autostart_manager = app_handle.autolaunch(); @@ -272,32 +263,28 @@ impl Sysopt { /// 获取当前自启动的实际状态 pub fn get_launch_status(&self) -> Result { - // 首先尝试检查快捷方式是否存在 #[cfg(target_os = "windows")] { - match startup_shortcut::is_shortcut_enabled() { - Ok(enabled) => { - logging!(info, Type::System, "快捷方式自启动状态: {enabled}"); - return Ok(enabled); - } - Err(e) => { - logging!(error, Type::System, "检查快捷方式失败,尝试原来的方法: {e}"); - } + let enabled = startup_task::is_auto_launch_enabled(); + if let Ok(status) = enabled { + logging!(info, Type::System, "Auto launch status (scheduled task): {status}"); } + enabled } - // 回退到原来的方法 - let app_handle = Handle::app_handle(); - let autostart_manager = app_handle.autolaunch(); - - match autostart_manager.is_enabled() { - Ok(status) => { - logging!(info, Type::System, "Auto launch status: {status}"); - Ok(status) - } - Err(e) => { - logging!(error, Type::System, "Failed to get auto launch status: {e}"); - Err(anyhow::anyhow!("Failed to get auto launch status: {}", e)) + #[cfg(not(target_os = "windows"))] + { + let app_handle = Handle::app_handle(); + let autostart_manager = app_handle.autolaunch(); + match autostart_manager.is_enabled() { + Ok(status) => { + logging!(info, Type::System, "Auto launch status: {status}"); + Ok(status) + } + Err(e) => { + logging!(error, Type::System, "Failed to get auto launch status: {e}"); + Err(anyhow::anyhow!("Failed to get auto launch status: {}", e)) + } } } } diff --git a/src-tauri/src/utils/autostart.rs b/src-tauri/src/utils/autostart.rs deleted file mode 100644 index 65128e55e..000000000 --- a/src-tauri/src/utils/autostart.rs +++ /dev/null @@ -1,132 +0,0 @@ -#[cfg(target_os = "windows")] -use anyhow::{Result, anyhow}; -#[cfg(target_os = "windows")] -use clash_verge_logging::{Type, logging}; - -#[cfg(target_os = "windows")] -use std::{os::windows::process::CommandExt as _, path::Path, path::PathBuf}; - -/// Windows 下的开机启动文件夹路径 -#[cfg(target_os = "windows")] -pub fn get_startup_dir() -> Result { - let appdata = std::env::var("APPDATA").map_err(|_| anyhow!("无法获取 APPDATA 环境变量"))?; - - let startup_dir = Path::new(&appdata) - .join("Microsoft") - .join("Windows") - .join("Start Menu") - .join("Programs") - .join("Startup"); - - if !startup_dir.exists() { - return Err(anyhow!("Startup 目录不存在: {:?}", startup_dir)); - } - - Ok(startup_dir) -} - -/// 获取当前可执行文件路径 -#[cfg(target_os = "windows")] -pub fn get_exe_path() -> Result { - let exe_path = std::env::current_exe().map_err(|e| anyhow!("无法获取当前可执行文件路径: {}", e))?; - - Ok(exe_path) -} - -/// 创建快捷方式 -#[cfg(target_os = "windows")] -pub async fn create_shortcut() -> Result<()> { - use crate::utils::dirs::PathBufExec as _; - - let exe_path = get_exe_path()?; - let startup_dir = get_startup_dir()?; - let old_shortcut_path = startup_dir.join("Clash-Verge.lnk"); - let new_shortcut_path = startup_dir.join("Clash Verge.lnk"); - - // 移除旧的快捷方式 - let _ = old_shortcut_path - .remove_if_exists() - .await - .inspect(|_| { - logging!(info, Type::Setup, "成功移除旧启动快捷方式"); - }) - .inspect_err(|err| { - logging!(error, Type::Setup, "移除旧启动快捷方式失败: {err}"); - }); - - // 如果新快捷方式已存在,直接返回成功 - if new_shortcut_path.exists() { - logging!(info, Type::Setup, "启动快捷方式已存在"); - return Ok(()); - } - - // 使用 PowerShell 创建快捷方式 - let powershell_command = format!( - "$WshShell = New-Object -ComObject WScript.Shell; \ - $Shortcut = $WshShell.CreateShortcut('{}'); \ - $Shortcut.TargetPath = '{}'; \ - $Shortcut.Save()", - new_shortcut_path.to_string_lossy().replace("\\", "\\\\"), - exe_path.to_string_lossy().replace("\\", "\\\\") - ); - - let output = std::process::Command::new("powershell") - .args(["-Command", &powershell_command]) - // 隐藏 PowerShell 窗口 - .creation_flags(0x08000000) // CREATE_NO_WINDOW - .output() - .map_err(|e| anyhow!("执行 PowerShell 命令失败: {}", e))?; - - if !output.status.success() { - let error_msg = String::from_utf8_lossy(&output.stderr); - return Err(anyhow!("创建快捷方式失败: {}", error_msg)); - } - - logging!(info, Type::Setup, "成功创建启动快捷方式"); - Ok(()) -} - -/// 删除快捷方式 -#[cfg(target_os = "windows")] -pub async fn remove_shortcut() -> Result<()> { - use crate::utils::dirs::PathBufExec as _; - - let startup_dir = get_startup_dir()?; - let old_shortcut_path = startup_dir.join("Clash-Verge.lnk"); - let new_shortcut_path = startup_dir.join("Clash Verge.lnk"); - - let mut removed_any = false; - - let _ = old_shortcut_path - .remove_if_exists() - .await - .inspect(|_| { - logging!(info, Type::Setup, "成功删除旧启动快捷方式"); - removed_any = true; - }) - .inspect_err(|err| { - logging!(error, Type::Setup, "删除旧启动快捷方式失败: {err}"); - }); - - let _ = new_shortcut_path - .remove_if_exists() - .await - .inspect(|_| { - logging!(info, Type::Setup, "成功删除启动快捷方式"); - removed_any = true; - }) - .inspect_err(|err| { - logging!(error, Type::Setup, "删除启动快捷方式失败: {err}"); - }); - - Ok(()) -} - -/// 检查快捷方式是否存在 -#[cfg(target_os = "windows")] -pub fn is_shortcut_enabled() -> Result { - let startup_dir = get_startup_dir()?; - let new_shortcut_path = startup_dir.join("Clash Verge.lnk"); - - Ok(new_shortcut_path.exists()) -} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 3eb1d30ae..faf542fe3 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -1,4 +1,3 @@ -pub mod autostart; pub mod dirs; pub mod format; pub mod help; @@ -9,6 +8,8 @@ pub mod linux; pub mod network; pub mod notification; pub mod resolve; +#[cfg(target_os = "windows")] +pub mod schtasks; pub mod server; pub mod singleton; pub mod tmpl; diff --git a/src-tauri/src/utils/schtasks.rs b/src-tauri/src/utils/schtasks.rs new file mode 100644 index 000000000..ab970a078 --- /dev/null +++ b/src-tauri/src/utils/schtasks.rs @@ -0,0 +1,403 @@ +use crate::utils::dirs::{self, PathBufExec as _}; +use anyhow::{Result, anyhow}; +use clash_verge_logging::{Type, logging}; +use std::fs; +use std::os::windows::process::CommandExt as _; +use std::path::{Path, PathBuf}; +use std::process::{Command, Output}; +use winapi::um::stringapiset::MultiByteToWideChar; +use winapi::um::winnls::{GetACP, GetOEMCP}; + +const CREATE_NO_WINDOW: u32 = 0x08000000; +const TASK_NAME_USER: &str = "Clash Verge"; +const TASK_NAME_ADMIN: &str = "Clash Verge (Admin)"; +const TASK_XML_DIR: &str = "tasks"; +const TASK_XML_USER: &str = "clash-verge-task-user.xml"; +const TASK_XML_ADMIN: &str = "clash-verge-task-admin.xml"; + +#[derive(Clone, Copy)] +pub enum TaskMode { + User, + Admin, +} + +impl TaskMode { + const fn name(self) -> &'static str { + match self { + Self::User => TASK_NAME_USER, + Self::Admin => TASK_NAME_ADMIN, + } + } + + const fn label(self) -> &'static str { + match self { + Self::User => "user", + Self::Admin => "admin", + } + } + + const fn xml_run_level(self) -> &'static str { + match self { + Self::User => "LeastPrivilege", + Self::Admin => "HighestAvailable", + } + } + + const fn xml_file_name(self) -> &'static str { + match self { + Self::User => TASK_XML_USER, + Self::Admin => TASK_XML_ADMIN, + } + } +} + +fn get_exe_path() -> Result { + let exe_path = std::env::current_exe().map_err(|e| anyhow!("failed to get exe path: {}", e))?; + Ok(exe_path) +} + +fn get_task_user_id() -> Result { + let username = std::env::var_os("USERNAME") + .or_else(|| std::env::var_os("USER")) + .ok_or_else(|| anyhow!("failed to get current user name"))?; + let username = username.to_string_lossy(); + let username = username.trim(); + if username.is_empty() { + return Err(anyhow!("current user name is empty")); + } + + let domain = std::env::var_os("USERDOMAIN") + .or_else(|| std::env::var_os("COMPUTERNAME")) + .map(|value| value.to_string_lossy().to_string()); + + if let Some(domain) = domain { + let domain = domain.trim(); + if !domain.is_empty() { + return Ok(format!("{domain}\\{username}")); + } + } + + Ok(username.to_string()) +} + +fn get_startup_dir() -> Result { + let appdata = std::env::var("APPDATA").map_err(|_| anyhow!("failed to read APPDATA env var"))?; + let startup_dir = Path::new(&appdata) + .join("Microsoft") + .join("Windows") + .join("Start Menu") + .join("Programs") + .join("Startup"); + + if !startup_dir.exists() { + return Err(anyhow!("startup folder does not exist: {:?}", startup_dir)); + } + + Ok(startup_dir) +} + +async fn cleanup_legacy_shortcuts() -> Result<()> { + let startup_dir = get_startup_dir()?; + let old_shortcut = startup_dir.join("Clash-Verge.lnk"); + let new_shortcut = startup_dir.join("Clash Verge.lnk"); + + old_shortcut.remove_if_exists().await?; + new_shortcut.remove_if_exists().await?; + Ok(()) +} + +fn task_xml_path(mode: TaskMode) -> Result { + let dir = dirs::app_home_dir()?.join(TASK_XML_DIR); + fs::create_dir_all(&dir).map_err(|e| anyhow!("failed to create task xml dir: {}", e))?; + Ok(dir.join(mode.xml_file_name())) +} + +fn xml_escape(value: &str) -> String { + let mut escaped = String::with_capacity(value.len()); + for ch in value.chars() { + match ch { + '&' => escaped.push_str("&"), + '<' => escaped.push_str("<"), + '>' => escaped.push_str(">"), + '"' => escaped.push_str("""), + '\'' => escaped.push_str("'"), + _ => escaped.push(ch), + } + } + escaped +} + +fn build_task_xml(mode: TaskMode) -> Result { + let exe_path = get_exe_path()?.to_string_lossy().to_string(); + let exe_path = xml_escape(&exe_path); + let user_id = xml_escape(&get_task_user_id()?); + Ok(format!( + r#" + + + + true + PT3S + {} + + + + + {} + InteractiveToken + {} + + + + Parallel + false + false + false + false + false + + false + false + + true + true + false + false + false + PT0S + 3 + + + + {} + + + +"#, + user_id, + user_id, + mode.xml_run_level(), + exe_path + )) +} + +fn encode_utf16le_with_bom(content: &str) -> Vec { + let mut bytes = Vec::with_capacity(2 + content.len() * 2); + bytes.extend_from_slice(&[0xFF, 0xFE]); + for unit in content.encode_utf16() { + bytes.extend_from_slice(&unit.to_le_bytes()); + } + bytes +} + +fn write_task_xml(mode: TaskMode) -> Result { + let task_xml = build_task_xml(mode)?; + let task_xml_path = task_xml_path(mode)?; + let encoded = encode_utf16le_with_bom(&task_xml); + fs::write(&task_xml_path, encoded).map_err(|e| anyhow!("failed to write task xml: {}", e))?; + Ok(task_xml_path) +} + +fn decode_with_code_page(bytes: &[u8], code_page: u32) -> Option { + if bytes.is_empty() { + return Some(String::new()); + } + + let len = bytes.len(); + if len > i32::MAX as usize { + return None; + } + + let required = unsafe { + MultiByteToWideChar( + code_page, + 0, + bytes.as_ptr() as *const i8, + len as i32, + std::ptr::null_mut(), + 0, + ) + }; + + if required == 0 { + return None; + } + + let mut wide = vec![0u16; required as usize]; + let written = unsafe { + MultiByteToWideChar( + code_page, + 0, + bytes.as_ptr() as *const i8, + len as i32, + wide.as_mut_ptr(), + required, + ) + }; + + if written == 0 { + return None; + } + + wide.truncate(written as usize); + Some(String::from_utf16_lossy(&wide)) +} + +fn decode_console_output(bytes: &[u8]) -> String { + if let Ok(text) = std::str::from_utf8(bytes) { + return text.to_string(); + } + + let oem = unsafe { GetOEMCP() }; + if let Some(text) = decode_with_code_page(bytes, oem) { + return text; + } + + let acp = unsafe { GetACP() }; + if let Some(text) = decode_with_code_page(bytes, acp) { + return text; + } + + String::from_utf8_lossy(bytes).to_string() +} + +fn output_message(output: &Output) -> String { + let stdout = decode_console_output(&output.stdout); + let stderr = decode_console_output(&output.stderr); + let stdout = stdout.trim(); + let stderr = stderr.trim(); + + match (stdout.is_empty(), stderr.is_empty()) { + (true, true) => "unknown error".to_string(), + (false, true) => stdout.to_string(), + (true, false) => stderr.to_string(), + (false, false) => format!("{stdout} | {stderr}"), + } +} + +fn schtasks_output(mut cmd: Command) -> Result { + cmd.creation_flags(CREATE_NO_WINDOW) + .output() + .map_err(|e| anyhow!("failed to execute schtasks: {}", e)) +} + +pub fn is_task_enabled(mode: TaskMode) -> Result { + let output = schtasks_output({ + let mut cmd = Command::new("schtasks"); + cmd.args(["/Query", "/TN", mode.name()]); + cmd + })?; + + Ok(output.status.success()) +} + +pub fn create_task(mode: TaskMode) -> Result<()> { + let task_xml_path = write_task_xml(mode)?; + let output = schtasks_output({ + let mut cmd = Command::new("schtasks"); + cmd.args(["/Create", "/TN", mode.name(), "/XML"]); + cmd.arg(&task_xml_path); + cmd.arg("/F"); + cmd + })?; + + if !output.status.success() { + return Err(anyhow!( + "failed to create {} task: {}", + mode.label(), + output_message(&output) + )); + } + + logging!(info, Type::Setup, "Created {} auto-launch task", mode.label()); + Ok(()) +} + +pub fn remove_task(mode: TaskMode) -> Result<()> { + let output = schtasks_output({ + let mut cmd = Command::new("schtasks"); + cmd.args(["/Delete", "/TN", mode.name(), "/F"]); + cmd + })?; + + if output.status.success() { + logging!(info, Type::Setup, "Removed {} auto-launch task", mode.label()); + return Ok(()); + } + + if !is_task_enabled(mode)? { + logging!( + info, + Type::Setup, + "{} auto-launch task not found, skipping removal", + mode.label() + ); + return Ok(()); + } + + Err(anyhow!( + "failed to remove {} task: {}", + mode.label(), + output_message(&output) + )) +} + +pub async fn set_auto_launch(is_enable: bool, is_admin: bool) -> Result<()> { + let target = if is_admin { TaskMode::Admin } else { TaskMode::User }; + let other = if is_admin { TaskMode::User } else { TaskMode::Admin }; + + if let Err(err) = cleanup_legacy_shortcuts().await { + logging!(warn, Type::Setup, "Failed to cleanup legacy startup shortcuts: {}", err); + } + + if is_enable { + if is_admin { + create_task(target)?; + if let Err(err) = remove_task(other) { + let _ = remove_task(target); + return Err(err); + } + } else { + if is_task_enabled(other)? { + return Err(anyhow!( + "admin auto-launch task exists; run the app as administrator to remove it before creating a user task" + )); + } + create_task(target)?; + } + return Ok(()); + } + + if is_admin { + let mut errors = Vec::new(); + if let Err(err) = remove_task(TaskMode::User) { + errors.push(err); + } + if let Err(err) = remove_task(TaskMode::Admin) { + errors.push(err); + } + + if let Some(err) = errors.into_iter().next() { + return Err(err); + } + + return Ok(()); + } + + remove_task(TaskMode::User)?; + if is_task_enabled(TaskMode::Admin)? { + return Err(anyhow!( + "admin auto-launch task exists; run the app as administrator to remove it" + )); + } + + Ok(()) +} + +pub fn is_auto_launch_enabled() -> Result { + if is_task_enabled(TaskMode::Admin)? { + return Ok(true); + } + + is_task_enabled(TaskMode::User) +} diff --git a/src/components/home/system-info-card.tsx b/src/components/home/system-info-card.tsx index 4ec5f3b14..4fd2fb3ed 100644 --- a/src/components/home/system-info-card.tsx +++ b/src/components/home/system-info-card.tsx @@ -1,19 +1,11 @@ import { InfoOutlined, SettingsOutlined, - WarningOutlined, AdminPanelSettingsOutlined, DnsOutlined, ExtensionOutlined, } from "@mui/icons-material"; -import { - Typography, - Stack, - Divider, - Chip, - IconButton, - Tooltip, -} from "@mui/material"; +import { Typography, Stack, Divider, Chip, IconButton } from "@mui/material"; import { useLockFn } from "ahooks"; import { useCallback, useEffect, useMemo, useReducer } from "react"; import { useTranslation } from "react-i18next"; @@ -303,13 +295,6 @@ export const SystemInfoCard = () => { {t("home.components.systemInfo.fields.autoLaunch")} - {isAdminMode && ( - - - - )} { const { verge, mutateVerge, patchVerge } = useVerge(); - const { isAdminMode } = useSystemState(); - const { enable_auto_launch, enable_silent_start } = verge ?? {}; const sysproxyRef = useRef(null); @@ -55,34 +49,16 @@ const SettingSystem = ({ onError }: Props) => { onError={onError} /> - - - - ) - } - > + { - // 移除管理员模式检查提示 onChangeData({ enable_auto_launch: e }); }} onGuard={async (e) => { - if (isAdminMode) { - showNotice.info( - "settings.sections.system.tooltips.autoLaunchAdmin", - ); - } - try { // 先触发UI更新立即看到反馈 onChangeData({ enable_auto_launch: e }); diff --git a/src/locales/ar/home.json b/src/locales/ar/home.json index 823a2f070..0862e0bff 100644 --- a/src/locales/ar/home.json +++ b/src/locales/ar/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "الإعدادات" }, - "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch" - }, "badges": { "adminMode": "Administrator Mode", "serviceMode": "وضع الخدمة", diff --git a/src/locales/ar/settings.json b/src/locales/ar/settings.json index 8261e6c5f..700ba9835 100644 --- a/src/locales/ar/settings.json +++ b/src/locales/ar/settings.json @@ -15,7 +15,6 @@ "systemProxy": "وكيل النظام" }, "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch", "silentStart": "بدء البرنامج في الخلفية دون عرض الواجهة" }, "fields": { diff --git a/src/locales/de/home.json b/src/locales/de/home.json index f657ae90f..77f5792b8 100644 --- a/src/locales/de/home.json +++ b/src/locales/de/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Einstellungen" }, - "tooltips": { - "autoLaunchAdmin": "Der Administrator-Modus unterstützt möglicherweise keine automatische Startfunktion." - }, "badges": { "adminMode": "Administrator-Modus", "serviceMode": "Service-Modus", diff --git a/src/locales/de/settings.json b/src/locales/de/settings.json index d33ec5a36..c92da3741 100644 --- a/src/locales/de/settings.json +++ b/src/locales/de/settings.json @@ -15,7 +15,6 @@ "systemProxy": "Systemproxy" }, "tooltips": { - "autoLaunchAdmin": "Der Administrator-Modus unterstützt möglicherweise keine automatische Startfunktion.", "silentStart": "Die Anwendung wird im Hintergrund gestartet, ohne dass das Programmfenster angezeigt wird." }, "fields": { diff --git a/src/locales/en/home.json b/src/locales/en/home.json index e1d9abbac..04655288b 100644 --- a/src/locales/en/home.json +++ b/src/locales/en/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Settings" }, - "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch" - }, "badges": { "adminMode": "Administrator Mode", "serviceMode": "Service Mode", diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index d588d8d4e..0667171f2 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -15,7 +15,6 @@ "systemProxy": "System Proxy" }, "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch", "silentStart": "Start the program in background mode without displaying the panel" }, "fields": { diff --git a/src/locales/es/home.json b/src/locales/es/home.json index 1c33c5d91..0dac1c7a8 100644 --- a/src/locales/es/home.json +++ b/src/locales/es/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Ajustes" }, - "tooltips": { - "autoLaunchAdmin": "El modo de administrador puede no admitir el inicio automático." - }, "badges": { "adminMode": "Modo de administrador", "serviceMode": "Modo de servicio", diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 96ee7be42..d4e45ea52 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -15,7 +15,6 @@ "systemProxy": "Proxy del sistema" }, "tooltips": { - "autoLaunchAdmin": "El modo de administrador puede no admitir el inicio automático.", "silentStart": "El programa se ejecutará en segundo plano al iniciarse y no mostrará el panel." }, "fields": { diff --git a/src/locales/fa/home.json b/src/locales/fa/home.json index 786dbc893..33d387ccc 100644 --- a/src/locales/fa/home.json +++ b/src/locales/fa/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "تنظیمات" }, - "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch" - }, "badges": { "adminMode": "Administrator Mode", "serviceMode": "حالت سرویس", diff --git a/src/locales/fa/settings.json b/src/locales/fa/settings.json index 70d5242d5..51ecd9859 100644 --- a/src/locales/fa/settings.json +++ b/src/locales/fa/settings.json @@ -15,7 +15,6 @@ "systemProxy": "پراکسی سیستم" }, "tooltips": { - "autoLaunchAdmin": "حالت ادمین ممکن است از راه‌اندازی خودکار پشتیبانی نکند", "silentStart": "برنامه را در حالت پس‌زمینه بدون نمایش پانل اجرا کنید" }, "fields": { diff --git a/src/locales/id/home.json b/src/locales/id/home.json index 89d28169e..3b3b94ae0 100644 --- a/src/locales/id/home.json +++ b/src/locales/id/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Pengaturan" }, - "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch" - }, "badges": { "adminMode": "Administrator Mode", "serviceMode": "Mode Layanan", diff --git a/src/locales/id/settings.json b/src/locales/id/settings.json index f70649c8e..3f06f1640 100644 --- a/src/locales/id/settings.json +++ b/src/locales/id/settings.json @@ -15,7 +15,6 @@ "systemProxy": "Proksi Sistem" }, "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch", "silentStart": "Mulai program dalam mode latar belakang tanpa menampilkan panel" }, "fields": { diff --git a/src/locales/jp/home.json b/src/locales/jp/home.json index e8d224c20..06445400e 100644 --- a/src/locales/jp/home.json +++ b/src/locales/jp/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "設定" }, - "tooltips": { - "autoLaunchAdmin": "管理者モードでは起動時の自動起動がサポートされない場合があります。" - }, "badges": { "adminMode": "管理者モード", "serviceMode": "サービスモード", diff --git a/src/locales/jp/settings.json b/src/locales/jp/settings.json index 0656fea43..de909944e 100644 --- a/src/locales/jp/settings.json +++ b/src/locales/jp/settings.json @@ -15,7 +15,6 @@ "systemProxy": "システムプロキシ" }, "tooltips": { - "autoLaunchAdmin": "管理者モードでは起動時の自動起動がサポートされない場合があります。", "silentStart": "アプリケーションを起動すると、バックグラウンドモードで実行され、アプリケーションパネルは表示されません。" }, "fields": { diff --git a/src/locales/ko/home.json b/src/locales/ko/home.json index 4cd949afc..f592723b0 100644 --- a/src/locales/ko/home.json +++ b/src/locales/ko/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "설정" }, - "tooltips": { - "autoLaunchAdmin": "관리자 모드에서는 자동 실행이 지원되지 않을 수 있습니다" - }, "badges": { "adminMode": "관리자 모드", "serviceMode": "서비스 모드", diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index af787c1b5..263956122 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -15,7 +15,6 @@ "systemProxy": "시스템 프록시" }, "tooltips": { - "autoLaunchAdmin": "관리자 모드에서는 자동 실행이 지원되지 않을 수 있습니다", "silentStart": "패널을 표시하지 않고 백그라운드에서 시작합니다" }, "fields": { diff --git a/src/locales/ru/home.json b/src/locales/ru/home.json index 064192974..e096a2afc 100644 --- a/src/locales/ru/home.json +++ b/src/locales/ru/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Настройки" }, - "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch" - }, "badges": { "adminMode": "Administrator Mode", "serviceMode": "Режим системной службы", diff --git a/src/locales/ru/settings.json b/src/locales/ru/settings.json index fb70f8cc9..38c775a56 100644 --- a/src/locales/ru/settings.json +++ b/src/locales/ru/settings.json @@ -15,7 +15,6 @@ "systemProxy": "Системный прокси" }, "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch", "silentStart": "Запускать программу в фоновом режиме без отображения панели" }, "fields": { diff --git a/src/locales/tr/home.json b/src/locales/tr/home.json index 2bcf868c7..d0e86a0cd 100644 --- a/src/locales/tr/home.json +++ b/src/locales/tr/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Ayarlar" }, - "tooltips": { - "autoLaunchAdmin": "Yönetici modu otomatik başlatmayı desteklemeyebilir" - }, "badges": { "adminMode": "Yönetici Modu", "serviceMode": "Hizmet Modu", diff --git a/src/locales/tr/settings.json b/src/locales/tr/settings.json index 1e3a12a66..ace28fb3d 100644 --- a/src/locales/tr/settings.json +++ b/src/locales/tr/settings.json @@ -15,7 +15,6 @@ "systemProxy": "Sistem Vekil'i" }, "tooltips": { - "autoLaunchAdmin": "Yönetici modu otomatik başlatmayı desteklemeyebilir", "silentStart": "Programı paneli görüntülemeden arka plan modunda başlatır" }, "fields": { diff --git a/src/locales/tt/home.json b/src/locales/tt/home.json index 1a92f39f4..1d1207aab 100644 --- a/src/locales/tt/home.json +++ b/src/locales/tt/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "Көйләүләр" }, - "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch" - }, "badges": { "adminMode": "Administrator Mode", "serviceMode": "Сервис режимы", diff --git a/src/locales/tt/settings.json b/src/locales/tt/settings.json index 9eb9dd195..7cd66c542 100644 --- a/src/locales/tt/settings.json +++ b/src/locales/tt/settings.json @@ -15,7 +15,6 @@ "systemProxy": "Системалы прокси" }, "tooltips": { - "autoLaunchAdmin": "Administrator mode may not support auto launch", "silentStart": "Программаны фоновый режимда, тәрәзәсез эшләтеп җибәрү" }, "fields": { diff --git a/src/locales/zh/home.json b/src/locales/zh/home.json index 7b11ef2f9..5a0cdd32f 100644 --- a/src/locales/zh/home.json +++ b/src/locales/zh/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "设置" }, - "tooltips": { - "autoLaunchAdmin": "管理员模式可能不支持开机自启" - }, "badges": { "adminMode": "管理员模式", "serviceMode": "服务模式", diff --git a/src/locales/zh/settings.json b/src/locales/zh/settings.json index 4a84d0573..d4a2d4037 100644 --- a/src/locales/zh/settings.json +++ b/src/locales/zh/settings.json @@ -15,7 +15,6 @@ "systemProxy": "系统代理" }, "tooltips": { - "autoLaunchAdmin": "管理员模式可能不支持开机自启", "silentStart": "程序启动时以后台模式运行,不显示程序面板" }, "fields": { diff --git a/src/locales/zhtw/home.json b/src/locales/zhtw/home.json index e4d82121e..ad9266305 100644 --- a/src/locales/zhtw/home.json +++ b/src/locales/zhtw/home.json @@ -62,9 +62,6 @@ "actions": { "settings": "設定" }, - "tooltips": { - "autoLaunchAdmin": "管理員模式可能不支援開機自啟" - }, "badges": { "adminMode": "管理員模式", "serviceMode": "服務模式", diff --git a/src/locales/zhtw/settings.json b/src/locales/zhtw/settings.json index e9ea5c15c..151f51c3d 100644 --- a/src/locales/zhtw/settings.json +++ b/src/locales/zhtw/settings.json @@ -15,7 +15,6 @@ "systemProxy": "系統代理" }, "tooltips": { - "autoLaunchAdmin": "管理員模式可能不支援開機自啟", "silentStart": "程序啟動時以後台模式執行,不顯示程序面板" }, "fields": { diff --git a/src/types/generated/i18n-keys.ts b/src/types/generated/i18n-keys.ts index 689f79f33..d78de5d05 100644 --- a/src/types/generated/i18n-keys.ts +++ b/src/types/generated/i18n-keys.ts @@ -59,7 +59,6 @@ export const translationKeys = [ "home.components.systemInfo.fields.lastCheckUpdate", "home.components.systemInfo.fields.vergeVersion", "home.components.systemInfo.actions.settings", - "home.components.systemInfo.tooltips.autoLaunchAdmin", "home.components.systemInfo.badges.adminMode", "home.components.systemInfo.badges.serviceMode", "home.components.systemInfo.badges.sidecarMode", @@ -320,7 +319,6 @@ export const translationKeys = [ "settings.sections.system.title", "settings.sections.system.toggles.tunMode", "settings.sections.system.toggles.systemProxy", - "settings.sections.system.tooltips.autoLaunchAdmin", "settings.sections.system.tooltips.silentStart", "settings.sections.system.fields.autoLaunch", "settings.sections.system.fields.silentStart", diff --git a/src/types/generated/i18n-resources.ts b/src/types/generated/i18n-resources.ts index e34cff332..3c428b969 100644 --- a/src/types/generated/i18n-resources.ts +++ b/src/types/generated/i18n-resources.ts @@ -124,9 +124,6 @@ export interface TranslationResources { vergeVersion: string; }; title: string; - tooltips: { - autoLaunchAdmin: string; - }; }; tests: { title: string; @@ -1166,7 +1163,6 @@ export interface TranslationResources { tunMode: string; }; tooltips: { - autoLaunchAdmin: string; silentStart: string; }; };