feat(settings): add Windows toggle to disable WebView2 hardware acceleration
This commit is contained in:
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user