From 8df8edb6ddb1049d95d7ed0b9f283368fbaba497 Mon Sep 17 00:00:00 2001 From: wonfen Date: Mon, 24 Aug 2026 19:32:29 +0800 Subject: [PATCH] fix(sysproxy): keep the system proxy usable when the core is not ready --- src-tauri/src/core/manager/lifecycle.rs | 31 ++++++++++++++----- src-tauri/src/core/proxy_control.rs | 4 +++ src-tauri/src/feat/backup.rs | 30 +++++++++++++++--- src-tauri/src/feat/config.rs | 18 +++++++++-- .../shared/proxy-control-switches.tsx | 13 ++++++-- src/locales/ar/settings.json | 3 +- src/locales/de/settings.json | 3 +- src/locales/en/settings.json | 3 +- src/locales/es/settings.json | 3 +- src/locales/fa/settings.json | 3 +- src/locales/id/settings.json | 3 +- src/locales/jp/settings.json | 3 +- src/locales/ko/settings.json | 3 +- src/locales/ru/settings.json | 3 +- src/locales/tr/settings.json | 3 +- src/locales/tt/settings.json | 3 +- src/locales/zh/settings.json | 3 +- src/locales/zhtw/settings.json | 3 +- src/services/notice-service.ts | 1 + src/types/generated/i18n-keys.ts | 1 + src/types/generated/i18n-resources.ts | 1 + 21 files changed, 108 insertions(+), 30 deletions(-) diff --git a/src-tauri/src/core/manager/lifecycle.rs b/src-tauri/src/core/manager/lifecycle.rs index b462433b7..41e048b58 100644 --- a/src-tauri/src/core/manager/lifecycle.rs +++ b/src-tauri/src/core/manager/lifecycle.rs @@ -2,7 +2,7 @@ use super::{CoreManager, RunningMode}; use crate::config::{Config, IVerge}; use crate::core::handle::Handle; use crate::core::manager::CLASH_LOGGER; -use crate::core::proxy_control; +use crate::core::proxy_control::{self, SysproxyFailure}; use crate::core::service::{SERVICE_MANAGER, ServiceStatus}; use anyhow::{Result, ensure}; use clash_verge_logging::{Type, logging}; @@ -304,7 +304,10 @@ impl CoreManager { let _life = self.lifecycle_lock.lock().await; run_core_start_transition( || self.start_core_inner(), - || !matches!(*self.get_running_mode(), RunningMode::NotRunning), + || { + !matches!(*self.get_running_mode(), RunningMode::NotRunning) + && self.current_core_readiness_generation().is_some() + }, || self.apply_proxy_after_start(), ) .await @@ -330,7 +333,9 @@ impl CoreManager { proxy_control::stop_guard().await; let result = async { self.start_core_inner().await?; - if !matches!(*self.get_running_mode(), RunningMode::Sidecar) { + if !matches!(*self.get_running_mode(), RunningMode::Sidecar) + || self.current_core_readiness_generation().is_none() + { anyhow::bail!("Sidecar did not become ready"); } self.apply_proxy_after_start().await @@ -371,7 +376,9 @@ impl CoreManager { }, || async { self.start_core_inner().await?; - if !matches!(*self.get_running_mode(), RunningMode::Sidecar) { + if !matches!(*self.get_running_mode(), RunningMode::Sidecar) + || self.current_core_readiness_generation().is_none() + { anyhow::bail!("Sidecar did not become ready after service uninstall"); } self.apply_proxy_after_start().await @@ -423,7 +430,9 @@ impl CoreManager { self.current_core_readiness_generation(), crate::core::service::owner_monitor_generation(), ) - .ok_or_else(|| anyhow::anyhow!("cannot apply system proxy before core readiness"))?; + .ok_or_else(|| { + anyhow::anyhow!("cannot apply system proxy before core readiness").context(SysproxyFailure::CoreNotReady) + })?; proxy_control::apply().await?; if !expectation.is_valid( self.get_running_mode().as_ref(), @@ -433,7 +442,8 @@ impl CoreManager { let clear_result = proxy_control::clear().await; proxy_control::stop_guard().await; clear_result?; - anyhow::bail!("core readiness changed while applying system proxy"); + return Err(anyhow::anyhow!("core readiness changed while applying system proxy") + .context(SysproxyFailure::CoreNotReady)); } proxy_control::refresh_guard().await?; if !expectation.is_valid( @@ -444,7 +454,10 @@ impl CoreManager { let clear_result = proxy_control::clear().await; proxy_control::stop_guard().await; clear_result?; - anyhow::bail!("core readiness changed while refreshing the system proxy guard"); + return Err( + anyhow::anyhow!("core readiness changed while refreshing the system proxy guard") + .context(SysproxyFailure::CoreNotReady), + ); } Ok(()) } @@ -553,7 +566,9 @@ impl CoreManager { || self.controlled_stop_core_with_intent(proxy_intent), || async { self.start_core_inner().await?; - if matches!(*self.get_running_mode(), RunningMode::NotRunning) { + if matches!(*self.get_running_mode(), RunningMode::NotRunning) + || self.current_core_readiness_generation().is_none() + { anyhow::bail!("core did not become ready after restart"); } Ok(()) diff --git a/src-tauri/src/core/proxy_control.rs b/src-tauri/src/core/proxy_control.rs index b8e2fddc3..f6f11e30d 100644 --- a/src-tauri/src/core/proxy_control.rs +++ b/src-tauri/src/core/proxy_control.rs @@ -38,6 +38,8 @@ pub enum SysproxyFailure { SidecarWhileServiceReady, /// The proxy guard stopped after repeated failures. GuardStopped { detail: String }, + /// The Core was not ready, so enabling the system proxy was refused. + CoreNotReady, } impl SysproxyFailure { @@ -48,6 +50,7 @@ impl SysproxyFailure { Self::DirectFallback { .. } => "SYSPROXY_DIRECT_FALLBACK", Self::SidecarWhileServiceReady => "SYSPROXY_SIDECAR_WHILE_SERVICE_READY", Self::GuardStopped { .. } => "SYSPROXY_GUARD_STOPPED", + Self::CoreNotReady => "SYSPROXY_CORE_NOT_READY", } } @@ -70,6 +73,7 @@ impl std::fmt::Display for SysproxyFailure { Self::GuardStopped { detail } => { write!(f, "system proxy guard stopped after repeated failures: {detail}") } + Self::CoreNotReady => f.write_str("the core is not ready, so the system proxy was not enabled"), } } } diff --git a/src-tauri/src/feat/backup.rs b/src-tauri/src/feat/backup.rs index 7dff211c6..4652c2aa4 100644 --- a/src-tauri/src/feat/backup.rs +++ b/src-tauri/src/feat/backup.rs @@ -1,6 +1,6 @@ use crate::{ config::{Config, IClashTemp, IProfiles, IVerge}, - core::{backup, proxy_control::SystemProxyStateUnknown}, + core::{CoreManager, backup, proxy_control, proxy_control::SystemProxyStateUnknown}, process::AsyncHandler, utils::{ dirs::{PathBufExec as _, app_home_dir, local_backup_dir, verge_path}, @@ -52,10 +52,30 @@ async fn finalize_restored_verge_config( profiles_draft.apply(); let verge_draft = Config::verge().await; - verge_draft.edit_draft(|d| { - *d = restored.clone(); - }); - verge_draft.apply(); + { + // Hold the write lock so a concurrent patch cannot stage a draft the proxy write reads. + let _config_write = Config::lock_config_write().await; + verge_draft.edit_draft(|d| { + *d = restored.clone(); + }); + verge_draft.apply(); + + // Turn it off here; a failing core side effect below would otherwise skip this step. + if !restored.enable_system_proxy.unwrap_or_default() { + let result = async { + let _lifecycle = CoreManager::global().lifecycle_lock.lock().await; + proxy_control::apply().await?; + proxy_control::refresh_guard().await + } + .await; + if let Err(err) = result { + logging!(error, Type::Backup, "Failed to turn the system proxy off: {err:#?}"); + if SystemProxyStateUnknown::is_in(&err) { + return Err(err); + } + } + } + } // Run configuration side effects without rewriting the already-restored file. if let Err(err) = super::patch_verge(&restored, true).await { diff --git a/src-tauri/src/feat/config.rs b/src-tauri/src/feat/config.rs index 1719b6a59..c8fc630af 100644 --- a/src-tauri/src/feat/config.rs +++ b/src-tauri/src/feat/config.rs @@ -1,6 +1,9 @@ use crate::{ config::{Config, IVerge}, - core::{CoreManager, autostart, handle, hotkey, logger::Logger, proxy_control::SystemProxyStateUnknown, tray}, + core::{ + CoreManager, autostart, handle, hotkey, logger::Logger, proxy_control, proxy_control::SystemProxyStateUnknown, + tray, + }, module::{auto_backup::AutoBackupManager, lightweight}, }; use anyhow::Result; @@ -223,7 +226,18 @@ async fn process_terminated_flags(update_flags: UpdateFlags, patch: &IVerge) -> if update_flags.contains(UpdateFlags::SYS_PROXY) { let manager = CoreManager::global(); let _lifecycle = manager.lifecycle_lock.lock().await; - manager.apply_proxy_after_start().await?; + // Turning it off only writes OS state, so it must stay available while the Core is down. + if Config::verge() + .await + .latest_arc() + .enable_system_proxy + .unwrap_or_default() + { + manager.apply_proxy_after_start().await?; + } else { + proxy_control::apply().await?; + proxy_control::refresh_guard().await?; + } } if update_flags.contains(UpdateFlags::HOTKEY) && let Some(hotkeys) = &patch.hotkeys diff --git a/src/components/shared/proxy-control-switches.tsx b/src/components/shared/proxy-control-switches.tsx index dbd3f66d7..bc920266d 100644 --- a/src/components/shared/proxy-control-switches.tsx +++ b/src/components/shared/proxy-control-switches.tsx @@ -137,7 +137,7 @@ const ProxyControlSwitches = ({ const { uninstallServiceAndStartSidecar } = useServiceUninstaller() const { indicator: systemProxyIndicator, toggleSystemProxy } = useSystemProxyState() - const { runState, isTunModeAvailable } = useSystemState() + const { runState, isTunModeAvailable, isLoading } = useSystemState() // Offer to uninstall only a service that is actually there and working. const isServiceInstallReady = runState.serviceUsable @@ -146,6 +146,15 @@ const ProxyControlSwitches = ({ const { enable_tun_mode } = verge ?? {} + // Enabling needs a running core; disabling only writes OS state and must stay available. + const handleSystemProxyToggle = async (value: boolean) => { + if (value && !isLoading && runState.mode === 'NotRunning') { + showNotice.error('settings.feedback.errors.sysproxy.coreNotReady') + return false + } + await toggleSystemProxy(value) + } + const handleTunToggle = async (value: boolean) => { if (value && !isTunModeAvailable) { requestService({ @@ -178,7 +187,7 @@ const ProxyControlSwitches = ({ active={systemProxyIndicator} infoTitle={t('settings.sections.proxyControl.tooltips.systemProxy')} onInfoClick={() => sysproxyRef.current?.open()} - onToggle={(value) => toggleSystemProxy(value)} + onToggle={handleSystemProxyToggle} onError={onError} highlight={systemProxyIndicator} /> diff --git a/src/locales/ar/settings.json b/src/locales/ar/settings.json index d37a9c5e2..5faec8714 100644 --- a/src/locales/ar/settings.json +++ b/src/locales/ar/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "تعذّر ضبط وكيل النظام: رفض macOS التغيير لعدم كفاية الصلاحيات. ثبّت خدمة Clash Verge؛ وإن كنت مسؤولًا فيمكنك أيضًا إيقاف اشتراط كلمة سر المسؤول للإعدادات على مستوى النظام من الخيارات المتقدمة في إعدادات النظام ← الخصوصية والأمان.", "directFallback": "تعذّر ضبط وكيل النظام؛ تمت إعادة حركة البيانات إلى اتصال مباشر.", "guardStopped": "توقف حارس وكيل النظام بعد عدة إخفاقات متتالية. لم يعد وكيل النظام يُحفَظ تلقائيًا.", - "sidecarWhileServiceReady": "تعذّر ضبط وكيل النظام: خدمة Clash Verge جاهزة لكن النواة ما زالت تعمل بوضع Sidecar. أعد تشغيل النواة للتبديل إلى وضع الخدمة." + "sidecarWhileServiceReady": "تعذّر ضبط وكيل النظام: خدمة Clash Verge جاهزة لكن النواة ما زالت تعمل بوضع Sidecar. أعد تشغيل النواة للتبديل إلى وضع الخدمة.", + "coreNotReady": "تعذّر تفعيل وكيل النظام لأن النواة لا تعمل. شغّل النواة (أو ثبّت/أصلح خدمة Clash Verge) ثم أعد المحاولة." } }, "notifications": { diff --git a/src/locales/de/settings.json b/src/locales/de/settings.json index c55b5af80..fe34e47e5 100644 --- a/src/locales/de/settings.json +++ b/src/locales/de/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "Systemproxy kann nicht gesetzt werden: macOS hat die Änderung mangels Berechtigung abgelehnt. Installieren Sie den Clash-Verge-Dienst; als Administrator können Sie in den erweiterten Optionen unter Systemeinstellungen → Datenschutz & Sicherheit auch die Passwortpflicht für systemweite Einstellungen deaktivieren.", "directFallback": "Systemproxy konnte nicht gesetzt werden; der Datenverkehr wurde auf eine direkte Verbindung zurückgesetzt.", "guardStopped": "Der Systemproxy-Wächter wurde nach mehreren Fehlversuchen in Folge gestoppt. Der Systemproxy wird nicht mehr automatisch aufrechterhalten.", - "sidecarWhileServiceReady": "Der Systemproxy konnte nicht gesetzt werden: Der Clash-Verge-Dienst ist bereit, der Kern läuft aber noch als Sidecar. Starten Sie den Kern neu, um in den Dienstmodus zu wechseln." + "sidecarWhileServiceReady": "Der Systemproxy konnte nicht gesetzt werden: Der Clash-Verge-Dienst ist bereit, der Kern läuft aber noch als Sidecar. Starten Sie den Kern neu, um in den Dienstmodus zu wechseln.", + "coreNotReady": "Der Systemproxy konnte nicht aktiviert werden, da der Kern nicht läuft. Starten Sie den Kern (oder installieren/reparieren Sie den Clash-Verge-Dienst) und versuchen Sie es erneut." } }, "notifications": { diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index b26f1161e..0fb601618 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "Cannot set the system proxy: macOS refused the change for lack of privileges. Install the Clash Verge service; an administrator can also turn off the administrator-password requirement for system-wide settings, in the advanced options of System Settings → Privacy & Security.", "directFallback": "Failed to set the system proxy; traffic has been switched back to a direct connection.", "guardStopped": "The system proxy guard stopped after failing several times running. The system proxy is no longer being kept in place.", - "sidecarWhileServiceReady": "The system proxy could not be set because the core is still running as a Sidecar, although the Clash Verge service is ready. Restart the core to switch to service mode." + "sidecarWhileServiceReady": "The system proxy could not be set because the core is still running as a Sidecar, although the Clash Verge service is ready. Restart the core to switch to service mode.", + "coreNotReady": "The system proxy could not be enabled because the core is not running. Start the core (or install/repair the Clash Verge service) and try again." } }, "notifications": { diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 2c4682bc9..aca3c690b 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "No se puede establecer el proxy del sistema: macOS rechazó el cambio por falta de permisos. Instale el servicio de Clash Verge; si es administrador, también puede desactivar la exigencia de contraseña de administrador para los ajustes de todo el sistema en las opciones avanzadas de Ajustes del Sistema → Privacidad y seguridad.", "directFallback": "No se pudo establecer el proxy del sistema; el tráfico volvió a una conexión directa.", "guardStopped": "El guardián del proxy del sistema se detuvo tras varios fallos seguidos. El proxy del sistema ya no se mantiene automáticamente.", - "sidecarWhileServiceReady": "No se pudo establecer el proxy del sistema: el servicio de Clash Verge está listo, pero el núcleo sigue ejecutándose como Sidecar. Reinicie el núcleo para cambiar al modo de servicio." + "sidecarWhileServiceReady": "No se pudo establecer el proxy del sistema: el servicio de Clash Verge está listo, pero el núcleo sigue ejecutándose como Sidecar. Reinicie el núcleo para cambiar al modo de servicio.", + "coreNotReady": "No se pudo activar el proxy del sistema porque el núcleo no está en ejecución. Inicie el núcleo (o instale/repare el servicio de Clash Verge) e inténtelo de nuevo." } }, "notifications": { diff --git a/src/locales/fa/settings.json b/src/locales/fa/settings.json index 66dd02515..a176fee7b 100644 --- a/src/locales/fa/settings.json +++ b/src/locales/fa/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "تنظیم پراکسی سیستم ممکن نیست: macOS به دلیل نداشتن دسترسی، این تغییر را رد کرد. سرویس Clash Verge را نصب کنید؛ اگر مدیر هستید می‌توانید در گزینه‌های پیشرفتهٔ تنظیمات سیستم ← حریم خصوصی و امنیت، الزام رمز مدیر برای تنظیمات سراسری سیستم را نیز خاموش کنید.", "directFallback": "تنظیم پراکسی سیستم ناموفق بود؛ ترافیک به اتصال مستقیم بازگردانده شد.", "guardStopped": "نگهبان پراکسی سیستم پس از چند شکست پیاپی متوقف شد. پراکسی سیستم دیگر به‌طور خودکار نگه داشته نمی‌شود.", - "sidecarWhileServiceReady": "تنظیم پراکسی سیستم ممکن نشد: سرویس Clash Verge آماده است اما هسته هنوز به‌صورت Sidecar اجرا می‌شود. برای رفتن به حالت سرویس، هسته را دوباره راه‌اندازی کنید." + "sidecarWhileServiceReady": "تنظیم پراکسی سیستم ممکن نشد: سرویس Clash Verge آماده است اما هسته هنوز به‌صورت Sidecar اجرا می‌شود. برای رفتن به حالت سرویس، هسته را دوباره راه‌اندازی کنید.", + "coreNotReady": "فعال‌سازی پراکسی سیستم ممکن نشد: هسته در حال اجرا نیست. ابتدا هسته را اجرا کنید (یا سرویس Clash Verge را نصب/تعمیر کنید) و دوباره تلاش کنید." } }, "notifications": { diff --git a/src/locales/id/settings.json b/src/locales/id/settings.json index 52e451b38..bdc835adc 100644 --- a/src/locales/id/settings.json +++ b/src/locales/id/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "Tidak dapat mengatur proxy sistem: macOS menolak perubahan ini karena hak akses tidak mencukupi. Pasang layanan Clash Verge; jika Anda administrator, Anda juga dapat mematikan keharusan kata sandi administrator untuk pengaturan seluruh sistem di opsi lanjutan Pengaturan Sistem → Privasi & Keamanan.", "directFallback": "Gagal mengatur proxy sistem; lalu lintas dikembalikan ke koneksi langsung.", "guardStopped": "Penjaga proxy sistem berhenti setelah gagal beberapa kali berturut-turut. Proxy sistem tidak lagi dipertahankan secara otomatis.", - "sidecarWhileServiceReady": "Proxy sistem tidak dapat diatur: layanan Clash Verge sudah siap, tetapi inti masih berjalan sebagai Sidecar. Mulai ulang inti untuk beralih ke mode layanan." + "sidecarWhileServiceReady": "Proxy sistem tidak dapat diatur: layanan Clash Verge sudah siap, tetapi inti masih berjalan sebagai Sidecar. Mulai ulang inti untuk beralih ke mode layanan.", + "coreNotReady": "Proxy sistem tidak dapat diaktifkan karena inti tidak berjalan. Jalankan inti (atau pasang/perbaiki layanan Clash Verge) lalu coba lagi." } }, "notifications": { diff --git a/src/locales/jp/settings.json b/src/locales/jp/settings.json index bfdb7fbda..fe4421152 100644 --- a/src/locales/jp/settings.json +++ b/src/locales/jp/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "システムプロキシを設定できません: 権限が不足しているため macOS が変更を拒否しました。Clash Verge サービスをインストールしてください。管理者であれば、システム設定 → プライバシーとセキュリティの詳細オプションで、システム全体の設定に管理者パスワードを要求する設定をオフにすることもできます。", "directFallback": "システムプロキシの設定に失敗したため、直接接続に戻しました。", "guardStopped": "システムプロキシガードが連続して失敗したため停止しました。システムプロキシは自動的に維持されなくなりました。", - "sidecarWhileServiceReady": "システムプロキシを設定できませんでした: Clash Verge サービスは準備できていますが、コアはまだ Sidecar で動作しています。コアを再起動するとサービスモードに切り替わります。" + "sidecarWhileServiceReady": "システムプロキシを設定できませんでした: Clash Verge サービスは準備できていますが、コアはまだ Sidecar で動作しています。コアを再起動するとサービスモードに切り替わります。", + "coreNotReady": "コアが実行されていないため、システムプロキシを有効にできませんでした。コアを起動する(または Clash Verge サービスをインストール/修復する)してから再試行してください。" } }, "notifications": { diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index 554334d3f..b2b82c9e9 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "시스템 프록시를 설정할 수 없습니다: 권한이 부족하여 macOS가 변경을 거부했습니다. Clash Verge 서비스를 설치하십시오. 관리자라면 시스템 설정 → 개인 정보 보호 및 보안의 고급 옵션에서, 시스템 전체 설정에 관리자 암호를 요구하는 설정을 끌 수도 있습니다.", "directFallback": "시스템 프록시 설정에 실패하여 직접 연결로 되돌렸습니다.", "guardStopped": "시스템 프록시 가드가 연속으로 실패하여 중지되었습니다. 시스템 프록시가 더 이상 자동으로 유지되지 않습니다.", - "sidecarWhileServiceReady": "시스템 프록시를 설정하지 못했습니다: Clash Verge 서비스는 준비되었지만 코어가 아직 Sidecar로 실행 중입니다. 코어를 재시작하면 서비스 모드로 전환됩니다." + "sidecarWhileServiceReady": "시스템 프록시를 설정하지 못했습니다: Clash Verge 서비스는 준비되었지만 코어가 아직 Sidecar로 실행 중입니다. 코어를 재시작하면 서비스 모드로 전환됩니다.", + "coreNotReady": "코어가 실행 중이 아니어서 시스템 프록시를 켤 수 없습니다. 코어를 시작한 뒤(또는 Clash Verge 서비스를 설치/복구한 뒤) 다시 시도하세요." } }, "notifications": { diff --git a/src/locales/ru/settings.json b/src/locales/ru/settings.json index f2226a3e5..0ebf2563e 100644 --- a/src/locales/ru/settings.json +++ b/src/locales/ru/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "Не удалось настроить системный прокси: macOS отклонил изменение из-за нехватки прав. Установите службу Clash Verge; администратор также может отключить требование пароля администратора для общесистемных настроек в дополнительных параметрах раздела «Системные настройки → Конфиденциальность и безопасность».", "directFallback": "Не удалось настроить системный прокси; трафик возвращён к прямому подключению.", "guardStopped": "Страж системного прокси остановился после нескольких неудач подряд. Системный прокси больше не поддерживается автоматически.", - "sidecarWhileServiceReady": "Не удалось настроить системный прокси: служба Clash Verge готова, но ядро всё ещё работает в режиме Sidecar. Перезапустите ядро, чтобы переключиться в режим службы." + "sidecarWhileServiceReady": "Не удалось настроить системный прокси: служба Clash Verge готова, но ядро всё ещё работает в режиме Sidecar. Перезапустите ядро, чтобы переключиться в режим службы.", + "coreNotReady": "Не удалось включить системный прокси: ядро не запущено. Запустите ядро (или установите/восстановите службу Clash Verge) и повторите попытку." } }, "notifications": { diff --git a/src/locales/tr/settings.json b/src/locales/tr/settings.json index d5babd1e1..62316780e 100644 --- a/src/locales/tr/settings.json +++ b/src/locales/tr/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "Sistem vekil sunucusu ayarlanamıyor: macOS yetki yetersizliği nedeniyle değişikliği reddetti. Clash Verge hizmetini kurun; yöneticiyseniz Sistem Ayarları → Gizlilik ve Güvenlik bölümündeki gelişmiş seçeneklerden, sistem genelindeki ayarlar için yönetici parolası zorunluluğunu da kapatabilirsiniz.", "directFallback": "Sistem vekil sunucusu ayarlanamadı; trafik doğrudan bağlantıya geri döndürüldü.", "guardStopped": "Sistem vekil sunucusu koruması art arda birkaç başarısızlıktan sonra durdu. Sistem vekil sunucusu artık otomatik olarak korunmuyor.", - "sidecarWhileServiceReady": "Sistem vekil sunucusu ayarlanamadı: Clash Verge hizmeti hazır ancak çekirdek hâlâ Sidecar olarak çalışıyor. Hizmet moduna geçmek için çekirdeği yeniden başlatın." + "sidecarWhileServiceReady": "Sistem vekil sunucusu ayarlanamadı: Clash Verge hizmeti hazır ancak çekirdek hâlâ Sidecar olarak çalışıyor. Hizmet moduna geçmek için çekirdeği yeniden başlatın.", + "coreNotReady": "Çekirdek çalışmadığı için sistem vekil sunucusu etkinleştirilemedi. Çekirdeği başlatın (veya Clash Verge hizmetini kurun/onarın) ve yeniden deneyin." } }, "notifications": { diff --git a/src/locales/tt/settings.json b/src/locales/tt/settings.json index dc2a3a1fc..288631ba0 100644 --- a/src/locales/tt/settings.json +++ b/src/locales/tt/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "Система проксиен көйләп булмый: хокуклар җитмәгәнгә күрә macOS бу үзгәрешне кире какты. Clash Verge хезмәтен урнаштырыгыз; әгәр сез администратор булсагыз, Система көйләнмәләре → Шәхсилек вә иминлек бүлегенең өстәмә параметрларында, система дәрәҗәсендәге көйләнмәләр өчен администратор паролен таләп итүне сүндерә аласыз.", "directFallback": "Система проксиен көйләү уңышсыз булды; трафик туры тоташуга кайтарылды.", "guardStopped": "Система проксие сакчысы берничә тапкыр рәттән уңышсыз булганнан соң туктады. Система проксие бүтән автоматик рәвештә саклап торылмый.", - "sidecarWhileServiceReady": "Система проксиен көйләп булмады: Clash Verge хезмәте әзер, әмма ядро һаман Sidecar буларак эшли. Хезмәт режимына күчәр өчен ядрону яңадан эшләтегез." + "sidecarWhileServiceReady": "Система проксиен көйләп булмады: Clash Verge хезмәте әзер, әмма ядро һаман Sidecar буларак эшли. Хезмәт режимына күчәр өчен ядрону яңадан эшләтегез.", + "coreNotReady": "Система проксиен кабызып булмады: ядро эшләми. Ядрону җибәрегез (яки Clash Verge хезмәтен урнаштырыгыз/төзәтегез) һәм тагын бер тапкыр карагыз." } }, "notifications": { diff --git a/src/locales/zh/settings.json b/src/locales/zh/settings.json index ae72e5fdf..e22a7cea8 100644 --- a/src/locales/zh/settings.json +++ b/src/locales/zh/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "无法设置系统代理:macOS 以权限不足为由拒绝了这次修改。请安装 Clash Verge 服务;管理员也可以在系统设置 → 隐私与安全性的高级选项中,关闭访问系统范围设置需要管理员密码的要求。", "directFallback": "系统代理设置失败,已回退为直连。", "guardStopped": "系统代理守卫连续多次失败后已停止,系统代理不再被自动维持。", - "sidecarWhileServiceReady": "系统代理设置失败:Clash Verge 服务已就绪,但内核仍以 Sidecar 运行。重启内核即可切换到服务模式。" + "sidecarWhileServiceReady": "系统代理设置失败:Clash Verge 服务已就绪,但内核仍以 Sidecar 运行。重启内核即可切换到服务模式。", + "coreNotReady": "无法开启系统代理:内核尚未运行。请先启动内核(或安装/修复 Clash Verge 服务)后重试。" } }, "notifications": { diff --git a/src/locales/zhtw/settings.json b/src/locales/zhtw/settings.json index 7edde889d..1232181ad 100644 --- a/src/locales/zhtw/settings.json +++ b/src/locales/zhtw/settings.json @@ -660,7 +660,8 @@ "privilegeRequired": "無法設定系統代理:macOS 以權限不足為由拒絕了這次修改。請安裝 Clash Verge 服務;管理者也可以在系統設定 → 隱私權與安全性的進階選項中,關閉存取系統層級設定需要管理者密碼的要求。", "directFallback": "系統代理設定失敗,已回退為直連。", "guardStopped": "系統代理守衛連續多次失敗後已停止,系統代理不再被自動維持。", - "sidecarWhileServiceReady": "系統代理設定失敗:Clash Verge 服務已就緒,但內核仍以 Sidecar 執行。重新啟動內核即可切換到服務模式。" + "sidecarWhileServiceReady": "系統代理設定失敗:Clash Verge 服務已就緒,但內核仍以 Sidecar 執行。重新啟動內核即可切換到服務模式。", + "coreNotReady": "無法開啟系統代理:內核尚未執行。請先啟動內核(或安裝/修復 Clash Verge 服務)後重試。" } }, "notifications": { diff --git a/src/services/notice-service.ts b/src/services/notice-service.ts index aa9ccfd39..3bebe0eb6 100644 --- a/src/services/notice-service.ts +++ b/src/services/notice-service.ts @@ -82,6 +82,7 @@ const CODED_ERROR_TRANSLATION_KEYS: Readonly> = { SERVICE_SIDECAR_FAILED: 'settings.feedback.errors.clashService.sidecarFailed', SERVICE_UNINSTALL_FAILED: 'settings.feedback.errors.clashService.uninstallFailed', + SYSPROXY_CORE_NOT_READY: 'settings.feedback.errors.sysproxy.coreNotReady', SYSPROXY_DIRECT_FALLBACK: 'settings.feedback.errors.sysproxy.directFallback', SYSPROXY_GUARD_STOPPED: 'settings.feedback.errors.sysproxy.guardStopped', SYSPROXY_SIDECAR_WHILE_SERVICE_READY: diff --git a/src/types/generated/i18n-keys.ts b/src/types/generated/i18n-keys.ts index ef5d659dc..d34826676 100644 --- a/src/types/generated/i18n-keys.ts +++ b/src/types/generated/i18n-keys.ts @@ -779,6 +779,7 @@ export const translationKeys = [ 'settings.feedback.errors.sysproxy.directFallback', 'settings.feedback.errors.sysproxy.guardStopped', 'settings.feedback.errors.sysproxy.sidecarWhileServiceReady', + 'settings.feedback.errors.sysproxy.coreNotReady', 'settings.feedback.notifications.clash.restartSuccess', 'settings.feedback.notifications.clash.versionUpdated', 'settings.feedback.notifications.clash.alreadyLatestVersion', diff --git a/src/types/generated/i18n-resources.ts b/src/types/generated/i18n-resources.ts index 0f1c20900..2063308b8 100644 --- a/src/types/generated/i18n-resources.ts +++ b/src/types/generated/i18n-resources.ts @@ -812,6 +812,7 @@ export interface TranslationResources { uninstallFailed: string } sysproxy: { + coreNotReady: string directFallback: string guardStopped: string privilegeRequired: string