From f0bd2a28a2ad01fb7127052a4fc04e88dc965f5f Mon Sep 17 00:00:00 2001 From: xintaofei Date: Sat, 25 Apr 2026 11:57:35 +0800 Subject: [PATCH] feat(settings): add Windows toggle to disable WebView2 hardware acceleration --- src-tauri/src/commands/system_settings.rs | 27 +++++ src-tauri/src/lib.rs | 41 +++++++ src-tauri/src/models/mod.rs | 2 + src-tauri/src/models/system.rs | 7 ++ src-tauri/src/preferences.rs | 56 +++++++++ .../settings/system-network-settings.tsx | 112 +++++++++++++++++- src/i18n/messages/ar.json | 7 ++ src/i18n/messages/de.json | 7 ++ src/i18n/messages/en.json | 7 ++ src/i18n/messages/es.json | 7 ++ src/i18n/messages/fr.json | 7 ++ src/i18n/messages/ja.json | 7 ++ src/i18n/messages/ko.json | 7 ++ src/i18n/messages/pt.json | 7 ++ src/i18n/messages/zh-CN.json | 7 ++ src/i18n/messages/zh-TW.json | 7 ++ src/lib/api.ts | 11 ++ src/lib/tauri.ts | 11 ++ src/lib/types.ts | 4 + 19 files changed, 338 insertions(+), 3 deletions(-) create mode 100644 src-tauri/src/preferences.rs diff --git a/src-tauri/src/commands/system_settings.rs b/src-tauri/src/commands/system_settings.rs index e82a649..4953062 100644 --- a/src-tauri/src/commands/system_settings.rs +++ b/src-tauri/src/commands/system_settings.rs @@ -8,6 +8,10 @@ use crate::db::service::app_metadata_service; use crate::db::AppDatabase; use crate::models::{SystemLanguageSettings, SystemProxySettings}; #[cfg(feature = "tauri-runtime")] +use crate::models::SystemRenderingSettings; +#[cfg(feature = "tauri-runtime")] +use crate::preferences; +#[cfg(feature = "tauri-runtime")] use crate::network::proxy; const SYSTEM_PROXY_SETTINGS_KEY: &str = "system_proxy_settings"; @@ -147,3 +151,26 @@ pub async fn update_system_language_settings( Ok(settings) } + +#[cfg(feature = "tauri-runtime")] +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn get_system_rendering_settings() -> Result { + let prefs = preferences::load(); + Ok(SystemRenderingSettings { + disable_hardware_acceleration: prefs.disable_hardware_acceleration, + }) +} + +#[cfg(feature = "tauri-runtime")] +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn update_system_rendering_settings( + settings: SystemRenderingSettings, +) -> Result { + let mut prefs = preferences::load(); + prefs.disable_hardware_acceleration = settings.disable_hardware_acceleration; + preferences::save(&prefs).map_err(|err| { + AppCommandError::io_error("Failed to persist rendering settings") + .with_detail(err.to_string()) + })?; + Ok(settings) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1b4e0db..e8594f2 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -10,6 +10,8 @@ pub mod keyring_store; mod models; mod network; mod parsers; +#[cfg(feature = "tauri-runtime")] +pub mod preferences; pub mod process; mod terminal; pub mod web; @@ -35,8 +37,45 @@ mod tauri_app { static APP_QUITTING: AtomicBool = AtomicBool::new(false); + /// On Windows, opt-out users can disable WebView2 hardware acceleration to + /// work around AMD/Intel GPU driver bugs that produce a black-screen + /// webview. The flag is stored in a tiny sidecar file at + /// `~/.codeg/preferences.json` so it can be read **before** the Tauri + /// builder, plugins, or tokio runtime start — once a tokio worker is alive, + /// `std::env::set_var` would race with concurrent `getenv` calls from + /// libraries like reqwest/rustls that read `HTTP_PROXY` etc. + #[cfg(target_os = "windows")] + fn apply_webview2_rendering_override() { + const DISABLE_GPU_ARGS: [&str; 2] = ["--disable-gpu", "--disable-software-rasterizer"]; + const ENV_KEY: &str = "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"; + + let prefs = crate::preferences::load(); + if !prefs.disable_hardware_acceleration { + return; + } + + let mut tokens: Vec = match std::env::var(ENV_KEY) { + Ok(prev) => prev + .split_whitespace() + .map(str::to_string) + .collect(), + Err(_) => Vec::new(), + }; + for arg in DISABLE_GPU_ARGS { + if !tokens.iter().any(|t| t == arg) { + tokens.push(arg.to_string()); + } + } + std::env::set_var(ENV_KEY, tokens.join(" ")); + } + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + // Apply the WebView2 rendering override before *any* tokio worker + // exists or any plugin reads the env. See doc comment above. + #[cfg(target_os = "windows")] + apply_webview2_rendering_override(); + if let Err(err) = fix_path_env::fix() { eprintln!("[PATH] fix_path_env failed: {err}"); } @@ -302,6 +341,8 @@ mod tauri_app { system_settings::update_system_proxy_settings, system_settings::get_system_language_settings, system_settings::update_system_language_settings, + system_settings::get_system_rendering_settings, + system_settings::update_system_rendering_settings, version_control::detect_git, version_control::test_git_path, version_control::get_git_settings, diff --git a/src-tauri/src/models/mod.rs b/src-tauri/src/models/mod.rs index 8c56b3d..96f3078 100644 --- a/src-tauri/src/models/mod.rs +++ b/src-tauri/src/models/mod.rs @@ -25,3 +25,5 @@ pub use system::{ GitCredentials, GitDetectResult, GitHubAccountsSettings, GitHubTokenValidation, GitSettings, SystemLanguageSettings, SystemProxySettings, }; +#[cfg(feature = "tauri-runtime")] +pub use system::SystemRenderingSettings; diff --git a/src-tauri/src/models/system.rs b/src-tauri/src/models/system.rs index 3d979cd..864b6ae 100644 --- a/src-tauri/src/models/system.rs +++ b/src-tauri/src/models/system.rs @@ -37,6 +37,13 @@ pub struct SystemLanguageSettings { pub language: AppLocale, } +#[cfg(feature = "tauri-runtime")] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default)] +pub struct SystemRenderingSettings { + pub disable_hardware_acceleration: bool, +} + // --- Version Control --- /// Explicit credentials for a single git remote operation. diff --git a/src-tauri/src/preferences.rs b/src-tauri/src/preferences.rs new file mode 100644 index 0000000..1904121 --- /dev/null +++ b/src-tauri/src/preferences.rs @@ -0,0 +1,56 @@ +//! User-scoped preferences stored at `~/.codeg/preferences.json`. +//! +//! These are settings that must be readable **before** the Tauri builder and +//! tokio runtime start (e.g. WebView2 rendering flags applied via +//! `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS`). All access is synchronous I/O so +//! the data must stay tiny. + +use std::fs; +use std::io; +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +const PREFERENCES_FILE_NAME: &str = "preferences.json"; +const CODEG_DIR_NAME: &str = ".codeg"; + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default)] +pub struct AppPreferences { + pub disable_hardware_acceleration: bool, +} + +pub fn preferences_file_path() -> Option { + dirs::home_dir().map(|h| h.join(CODEG_DIR_NAME).join(PREFERENCES_FILE_NAME)) +} + +/// Read preferences synchronously. Missing / unreadable / malformed file +/// returns `Default::default()`. Errors are intentionally swallowed because +/// this is called on the startup hot-path; callers log if needed. +pub fn load() -> AppPreferences { + let Some(path) = preferences_file_path() else { + return AppPreferences::default(); + }; + match fs::read_to_string(&path) { + Ok(raw) => serde_json::from_str(&raw).unwrap_or_default(), + Err(err) if err.kind() == io::ErrorKind::NotFound => AppPreferences::default(), + Err(err) => { + eprintln!( + "[Preferences] failed to read {}: {err}", + path.display() + ); + AppPreferences::default() + } + } +} + +pub fn save(prefs: &AppPreferences) -> io::Result<()> { + let path = preferences_file_path() + .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "home directory unavailable"))?; + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + let serialized = serde_json::to_string_pretty(prefs) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))?; + fs::write(&path, serialized) +} diff --git a/src/components/settings/system-network-settings.tsx b/src/components/settings/system-network-settings.tsx index 28b0b2a..2eca233 100644 --- a/src/components/settings/system-network-settings.tsx +++ b/src/components/settings/system-network-settings.tsx @@ -6,6 +6,7 @@ import { CheckCircle2, Languages, Loader2, + MonitorCog, RefreshCw, Wifi, } from "lucide-react" @@ -29,11 +30,14 @@ import { } from "@/components/ui/select" import { getSystemProxySettings, + getSystemRenderingSettings, updateSystemLanguageSettings, updateSystemProxySettings, + updateSystemRenderingSettings, } from "@/lib/api" -import { openUrl } from "@/lib/platform" +import { isDesktop, openUrl } from "@/lib/platform" import type { AppLocale } from "@/lib/types" +import { usePlatform } from "@/hooks/use-platform" import { checkAppUpdate, closeAppUpdate, @@ -62,12 +66,21 @@ function isAppLocale(value: string): value is AppLocale { type UpdateAction = "check" | "install" +// Captured the first time settings page loads: represents the value that the +// running webview process was launched with. Survives settings-shell remounts +// so the "Restart now" banner doesn't vanish if the user navigates away and +// back without restarting. +let processStartDisableHwAccel: boolean | null = null + export function SystemNetworkSettings() { const t = useTranslations("SystemSettings") const tLanguage = useTranslations("Language") const locale = useLocale() const { languageSettings, languageSettingsLoaded, setLanguageSettings } = useAppI18n() + const { isWindows } = usePlatform() + const renderingSettingsLoadable = isDesktop() + const renderingSectionVisible = renderingSettingsLoadable && isWindows const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) @@ -75,6 +88,14 @@ export function SystemNetworkSettings() { const [enabled, setEnabled] = useState(false) const [proxyUrl, setProxyUrl] = useState("") const [loadError, setLoadError] = useState(null) + const [disableHwAccel, setDisableHwAccel] = useState(false) + const [savingRendering, setSavingRendering] = useState(false) + const [persistedDisableHwAccel, setPersistedDisableHwAccel] = useState(false) + const [processStartLoaded, setProcessStartLoaded] = useState( + processStartDisableHwAccel !== null + ) + const renderingDirty = + processStartLoaded && persistedDisableHwAccel !== processStartDisableHwAccel const [currentVersion, setCurrentVersion] = useState("") const [availableUpdate, setAvailableUpdate] = useState(null) const [checkingUpdate, setCheckingUpdate] = useState(false) @@ -150,14 +171,26 @@ export function SystemNetworkSettings() { setLoadError(null) try { - const [proxySettings, version] = await Promise.all([ + const [proxySettings, version, renderingSettings] = await Promise.all([ getSystemProxySettings(), getCurrentAppVersion(), + renderingSettingsLoadable + ? getSystemRenderingSettings() + : Promise.resolve(null), ]) setEnabled(proxySettings.enabled) setProxyUrl(proxySettings.proxy_url ?? "") setCurrentVersion(version) + if (renderingSettings) { + const value = renderingSettings.disable_hardware_acceleration + setDisableHwAccel(value) + setPersistedDisableHwAccel(value) + if (processStartDisableHwAccel === null) { + processStartDisableHwAccel = value + setProcessStartLoaded(true) + } + } } catch (err) { const message = err instanceof Error ? err.message : String(err) setLoadError(message) @@ -165,7 +198,7 @@ export function SystemNetworkSettings() { } finally { setLoading(false) } - }, []) + }, [renderingSettingsLoadable]) useEffect(() => { loadSettings().catch((err) => { @@ -208,6 +241,35 @@ export function SystemNetworkSettings() { [t] ) + const saveRenderingSettings = useCallback( + async (next: boolean, prev: boolean) => { + setSavingRendering(true) + try { + const result = await updateSystemRenderingSettings({ + disable_hardware_acceleration: next, + }) + setDisableHwAccel(result.disable_hardware_acceleration) + setPersistedDisableHwAccel(result.disable_hardware_acceleration) + } catch (err) { + setDisableHwAccel(prev) + const message = err instanceof Error ? err.message : String(err) + toast.error(t("renderingSaveFailed", { message })) + } finally { + setSavingRendering(false) + } + }, + [t] + ) + + const restartNow = useCallback(async () => { + try { + await relaunchApp() + } catch (err) { + const message = err instanceof Error ? err.message : String(err) + toast.error(t("restartFailed", { message })) + } + }, [t]) + const saveLanguage = useCallback( async (lang: LanguageSelectValue) => { setSavingLanguage(true) @@ -559,6 +621,50 @@ export function SystemNetworkSettings() { + {renderingSectionVisible && ( +
+
+ +

{t("renderingTitle")}

+
+ +

+ {t("renderingDescription")} +

+ + + + {renderingDirty && ( +
+ + {t("restartRequired")} + + +
+ )} +
+ )} +
diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 0eda751..66b6715 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -114,6 +114,13 @@ "appLanguage": "لغة التطبيق", "languageSaveSuccess": "تم حفظ إعدادات اللغة", "languageSaveFailed": "فشل حفظ إعدادات اللغة: {message}", + "renderingTitle": "العرض", + "renderingDescription": "أوقف تشغيل تسريع الأجهزة إذا ظهرت شاشة سوداء أو أخطاء في العرض (شائع على بعض كروت AMD أو كروت Intel المدمجة). يسري على إصدار سطح المكتب لويندوز فقط.", + "disableHardwareAcceleration": "تعطيل تسريع الأجهزة", + "renderingSaveFailed": "فشل حفظ إعدادات العرض: {message}", + "restartRequired": "تم الحفظ. أعد تشغيل التطبيق ليصبح التغيير نافذًا.", + "restartNow": "إعادة التشغيل الآن", + "restartFailed": "فشل إعادة التشغيل: {message}", "updateTitle": "تحديث التطبيق", "versionTitle": "تحديث البرنامج", "updateDescription": "تحقق من المصدر المهيأ للإصدارات الأحدث وثبّت التحديث مباشرة عند توفره.", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index e449623..70220a0 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -114,6 +114,13 @@ "appLanguage": "App-Sprache", "languageSaveSuccess": "Spracheinstellungen wurden gespeichert", "languageSaveFailed": "Spracheinstellungen konnten nicht gespeichert werden: {message}", + "renderingTitle": "Rendering", + "renderingDescription": "Deaktivieren Sie die Hardwarebeschleunigung, wenn die App einen schwarzen Bildschirm oder Render-Fehler zeigt (häufig bei bestimmten AMD-GPUs oder integrierten Intel-GPUs). Wirkt sich nur auf die Windows-Desktop-Version aus.", + "disableHardwareAcceleration": "Hardwarebeschleunigung deaktivieren", + "renderingSaveFailed": "Render-Einstellungen konnten nicht gespeichert werden: {message}", + "restartRequired": "Gespeichert. Starten Sie die App neu, damit die Änderung wirksam wird.", + "restartNow": "Jetzt neu starten", + "restartFailed": "Neustart fehlgeschlagen: {message}", "updateTitle": "App-Update", "versionTitle": "Softwareupdate", "updateDescription": "Prüft die konfigurierte Release-Quelle auf neue Versionen und installiert sie bei Verfügbarkeit direkt.", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 447ead4..524c454 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -114,6 +114,13 @@ "appLanguage": "App language", "languageSaveSuccess": "Language settings saved", "languageSaveFailed": "Failed to save language settings: {message}", + "renderingTitle": "Rendering", + "renderingDescription": "Disable hardware acceleration if the app shows a black screen or rendering glitches (common on certain AMD GPUs or Intel integrated GPUs). Only takes effect on the Windows desktop build.", + "disableHardwareAcceleration": "Disable hardware acceleration", + "renderingSaveFailed": "Failed to save rendering settings: {message}", + "restartRequired": "Saved. Restart the app for the change to take effect.", + "restartNow": "Restart now", + "restartFailed": "Failed to restart: {message}", "updateTitle": "App Update", "versionTitle": "Software Update", "updateDescription": "Check the configured release source for newer versions and install directly when available.", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 7291b92..d13f2f5 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -114,6 +114,13 @@ "appLanguage": "Idioma de la app", "languageSaveSuccess": "La configuración de idioma se guardó", "languageSaveFailed": "No se pudo guardar la configuración de idioma: {message}", + "renderingTitle": "Renderizado", + "renderingDescription": "Desactiva la aceleración por hardware si la aplicación muestra pantalla negra o fallos de renderizado (común en algunas GPU AMD o Intel integradas). Solo afecta a la versión de escritorio en Windows.", + "disableHardwareAcceleration": "Desactivar aceleración por hardware", + "renderingSaveFailed": "No se pudo guardar la configuración de renderizado: {message}", + "restartRequired": "Guardado. Reinicia la aplicación para aplicar el cambio.", + "restartNow": "Reiniciar ahora", + "restartFailed": "No se pudo reiniciar: {message}", "updateTitle": "Actualización de la app", "versionTitle": "Actualización de software", "updateDescription": "Comprueba versiones más nuevas en la fuente de versiones configurada e instálalas directamente cuando estén disponibles.", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index d6455ab..c5862a3 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -114,6 +114,13 @@ "appLanguage": "Langue de l’app", "languageSaveSuccess": "Les paramètres de langue ont été enregistrés", "languageSaveFailed": "Échec de l’enregistrement des paramètres de langue : {message}", + "renderingTitle": "Rendu", + "renderingDescription": "Désactivez l’accélération matérielle si l’application affiche un écran noir ou des défauts de rendu (fréquent sur certaines GPU AMD ou GPU Intel intégrées). N’a d’effet que sur la version Windows de bureau.", + "disableHardwareAcceleration": "Désactiver l’accélération matérielle", + "renderingSaveFailed": "Échec de l’enregistrement des paramètres de rendu : {message}", + "restartRequired": "Enregistré. Redémarrez l’application pour appliquer la modification.", + "restartNow": "Redémarrer maintenant", + "restartFailed": "Échec du redémarrage : {message}", "updateTitle": "Mise à jour de l’app", "versionTitle": "Mise à jour logicielle", "updateDescription": "Vérifiez les nouvelles versions depuis la source de publication configurée et installez-les directement si disponibles.", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index ba93a72..30b1df5 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -114,6 +114,13 @@ "appLanguage": "アプリ言語", "languageSaveSuccess": "言語設定を保存しました", "languageSaveFailed": "言語設定の保存に失敗しました: {message}", + "renderingTitle": "レンダリング", + "renderingDescription": "アプリで黒画面や描画不具合が発生する場合(一部の AMD GPU や Intel 内蔵 GPU で報告あり)、ハードウェアアクセラレーションを無効化してください。Windows デスクトップ版のみ有効です。", + "disableHardwareAcceleration": "ハードウェアアクセラレーションを無効化", + "renderingSaveFailed": "レンダリング設定の保存に失敗しました: {message}", + "restartRequired": "保存しました。再起動後に反映されます。", + "restartNow": "今すぐ再起動", + "restartFailed": "再起動に失敗しました: {message}", "updateTitle": "アプリ更新", "versionTitle": "ソフトウェアアップデート", "updateDescription": "設定されたリリースソースで新しいバージョンを確認し、利用可能なら直接インストールします。", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 2efe6ce..5ee5b3a 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -114,6 +114,13 @@ "appLanguage": "앱 언어", "languageSaveSuccess": "언어 설정이 저장되었습니다", "languageSaveFailed": "언어 설정 저장 실패: {message}", + "renderingTitle": "렌더링", + "renderingDescription": "앱이 검은 화면이 되거나 렌더링 문제가 발생하면(일부 AMD GPU 또는 Intel 내장 GPU에서 보고됨) 하드웨어 가속을 끄세요. Windows 데스크톱에서만 적용됩니다.", + "disableHardwareAcceleration": "하드웨어 가속 비활성화", + "renderingSaveFailed": "렌더링 설정 저장 실패: {message}", + "restartRequired": "저장되었습니다. 변경 사항을 적용하려면 앱을 다시 시작하세요.", + "restartNow": "지금 다시 시작", + "restartFailed": "다시 시작 실패: {message}", "updateTitle": "앱 업데이트", "versionTitle": "소프트웨어 업데이트", "updateDescription": "설정된 릴리스 소스에서 새 버전을 확인하고 가능하면 바로 설치합니다.", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 9a0b289..0d5c3b4 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -114,6 +114,13 @@ "appLanguage": "Idioma do app", "languageSaveSuccess": "As configurações de idioma foram salvas", "languageSaveFailed": "Falha ao salvar as configurações de idioma: {message}", + "renderingTitle": "Renderização", + "renderingDescription": "Desative a aceleração de hardware se o aplicativo apresentar tela preta ou falhas de renderização (comum em certas GPUs AMD ou GPUs Intel integradas). Aplica-se apenas à versão desktop do Windows.", + "disableHardwareAcceleration": "Desativar aceleração de hardware", + "renderingSaveFailed": "Falha ao salvar as configurações de renderização: {message}", + "restartRequired": "Salvo. Reinicie o aplicativo para aplicar a alteração.", + "restartNow": "Reiniciar agora", + "restartFailed": "Falha ao reiniciar: {message}", "updateTitle": "Atualização do app", "versionTitle": "Atualização de software", "updateDescription": "Verifique versões mais novas na fonte de releases configurada e instale diretamente quando disponíveis.", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index cbfac08..8abe47e 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -114,6 +114,13 @@ "appLanguage": "应用语言", "languageSaveSuccess": "语言设置已保存", "languageSaveFailed": "语言设置保存失败:{message}", + "renderingTitle": "渲染加速", + "renderingDescription": "如果应用出现黑屏或渲染异常(常见于 AMD 显卡或部分 Intel 集成显卡),可关闭硬件加速。仅 Windows 桌面端生效。", + "disableHardwareAcceleration": "禁用硬件加速", + "renderingSaveFailed": "渲染设置保存失败:{message}", + "restartRequired": "已保存,重启应用后生效。", + "restartNow": "立即重启", + "restartFailed": "重启失败:{message}", "updateTitle": "应用升级", "versionTitle": "软件更新", "updateDescription": "点击检查后会从配置的发布源拉取最新版本信息,有新版本时可直接下载并安装。", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 7ccb007..828b755 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -114,6 +114,13 @@ "appLanguage": "應用語言", "languageSaveSuccess": "語言設定已儲存", "languageSaveFailed": "語言設定儲存失敗:{message}", + "renderingTitle": "渲染加速", + "renderingDescription": "若應用出現黑屏或渲染異常(常見於 AMD 顯示卡或部分 Intel 內顯),可關閉硬體加速。僅 Windows 桌面端生效。", + "disableHardwareAcceleration": "停用硬體加速", + "renderingSaveFailed": "渲染設定儲存失敗:{message}", + "restartRequired": "已儲存,需重新啟動應用程式才會生效。", + "restartNow": "立即重新啟動", + "restartFailed": "重新啟動失敗:{message}", "updateTitle": "應用升級", "versionTitle": "軟體更新", "updateDescription": "點擊檢查後會從設定的發佈來源拉取最新版本資訊,有新版本時可直接下載並安裝。", diff --git a/src/lib/api.ts b/src/lib/api.ts index ec5de4c..ec91105 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -47,6 +47,7 @@ import type { GitLogResult, SystemLanguageSettings, SystemProxySettings, + SystemRenderingSettings, GitCredentials, GitDetectResult, PackageManagerInfo, @@ -458,6 +459,16 @@ export async function updateSystemLanguageSettings( return getTransport().call("update_system_language_settings", { settings }) } +export async function getSystemRenderingSettings(): Promise { + return getTransport().call("get_system_rendering_settings") +} + +export async function updateSystemRenderingSettings( + settings: SystemRenderingSettings +): Promise { + return getTransport().call("update_system_rendering_settings", { settings }) +} + // --- Version Control --- export async function detectGit(): Promise { diff --git a/src/lib/tauri.ts b/src/lib/tauri.ts index 16c0438..9053cbe 100644 --- a/src/lib/tauri.ts +++ b/src/lib/tauri.ts @@ -45,6 +45,7 @@ import type { GitLogResult, SystemLanguageSettings, SystemProxySettings, + SystemRenderingSettings, GitCredentials, GitDetectResult, GitSettings, @@ -319,6 +320,16 @@ export async function updateSystemLanguageSettings( return invoke("update_system_language_settings", { settings }) } +export async function getSystemRenderingSettings(): Promise { + return invoke("get_system_rendering_settings") +} + +export async function updateSystemRenderingSettings( + settings: SystemRenderingSettings +): Promise { + return invoke("update_system_rendering_settings", { settings }) +} + // --- Version Control --- export async function detectGit(): Promise { diff --git a/src/lib/types.ts b/src/lib/types.ts index 3e8d9d3..369832c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -634,6 +634,10 @@ export interface SystemLanguageSettings { language: AppLocale } +export interface SystemRenderingSettings { + disable_hardware_acceleration: boolean +} + // --- Version Control --- export interface GitCredentials {