feat(settings): add Windows toggle to disable WebView2 hardware acceleration

This commit is contained in:
xintaofei
2026-04-25 11:57:35 +08:00
parent 5ae081e87a
commit f0bd2a28a2
19 changed files with 338 additions and 3 deletions

View File

@@ -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<string | null>(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<string>("")
const [availableUpdate, setAvailableUpdate] = useState<Update | null>(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() {
</div>
</section>
{renderingSectionVisible && (
<section className="rounded-xl border bg-card p-4 space-y-4">
<div className="flex items-center gap-2">
<MonitorCog className="h-4 w-4 text-muted-foreground" />
<h2 className="text-sm font-semibold">{t("renderingTitle")}</h2>
</div>
<p className="text-xs text-muted-foreground leading-5">
{t("renderingDescription")}
</p>
<label className="inline-flex items-center gap-2 text-sm">
<input
type="checkbox"
checked={disableHwAccel}
disabled={savingRendering}
onChange={(event) => {
const next = event.target.checked
const prev = disableHwAccel
setDisableHwAccel(next)
saveRenderingSettings(next, prev)
}}
/>
{t("disableHardwareAcceleration")}
</label>
{renderingDirty && (
<div className="flex items-center justify-between gap-3 rounded-md border bg-muted/20 px-3 py-2 text-xs">
<span className="text-muted-foreground">
{t("restartRequired")}
</span>
<Button
size="sm"
onClick={restartNow}
disabled={savingRendering}
>
<RefreshCw className="h-3.5 w-3.5" />
{t("restartNow")}
</Button>
</div>
)}
</section>
)}
<section className="rounded-xl border bg-card p-4 space-y-4">
<div className="flex items-center gap-2">
<Languages className="h-4 w-4 text-muted-foreground" />