import { ButtonItem, Field, PanelSectionRow, SliderField, ToggleField } from "@decky/ui"; import { useEffect, useState } from "react"; import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri"; import { usePerAppWorkarounds } from "../hooks/usePerAppWorkarounds"; import t from "../i18n/i18n"; import type { WorkaroundField } from "../utils/steamLaunchOptions"; interface WorkaroundsSectionProps { appId: string; nonSteam: boolean; } const WORKAROUNDS_COLLAPSED_KEY = "lsfg-workarounds-collapsed-v2"; type ToggleWorkaroundField = Exclude; const TOGGLE_ROWS: readonly { field: ToggleWorkaroundField; labelKey: string; label: string; descriptionKey: string; description: string; }[] = [ { field: "disableSteamdeckMode", labelKey: "CONFIG_DISABLE_STEAMDECK_MODE", label: "Disable Steam Deck Mode", descriptionKey: "CONFIG_DISABLE_STEAMDECK_MODE_DESC", description: "Disables a game-specific Steam Deck compatibility switch. Requires game restart to apply.", }, { field: "disableGamescopeWsi", labelKey: "CONFIG_DISABLE_GAMESCOPE_WSI", label: "Disable Gamescope WSI", descriptionKey: "CONFIG_DISABLE_GAMESCOPE_WSI_DESC", description: "Adds ENABLE_GAMESCOPE_WSI=0. Requires game restart to apply.", }, { field: "disableHdr", labelKey: "CONFIG_DISABLE_HDR", label: "Disable HDR", descriptionKey: "CONFIG_DISABLE_HDR_DESC", description: "Prevents DXVK from exposing HDR to the game. Requires game restart to apply.", }, { field: "disableVkbasalt", labelKey: "CONFIG_DISABLE_VKBASALT", label: "Disable vkBasalt", descriptionKey: "CONFIG_DISABLE_VKBASALT_DESC", description: "Disables vkBasalt layer which can conflict with LSFG (Reshade, some Decky plugins)", }, { field: "enableZink", labelKey: "CONFIG_ENABLE_ZINK", label: "Force Zink for OpenGL Games", descriptionKey: "CONFIG_ENABLE_ZINK_DESC", description: "Uses Mesa's Zink OpenGL-to-Vulkan driver. May cause crashes or freezes with some games. Requires game restart to apply.", }, ]; function usePersistentCollapsed() { const [collapsed, setCollapsed] = useState(() => { try { const saved = localStorage.getItem(WORKAROUNDS_COLLAPSED_KEY); return saved !== null ? JSON.parse(saved) === true : true; } catch { return true; } }); useEffect(() => { try { localStorage.setItem(WORKAROUNDS_COLLAPSED_KEY, JSON.stringify(collapsed)); } catch { // Persisting the view preference is optional. } }, [collapsed]); return [collapsed, () => setCollapsed((value) => !value)] as const; } export function WorkaroundsSection({ appId, nonSteam }: WorkaroundsSectionProps) { const [collapsed, toggleCollapsed] = usePersistentCollapsed(); const { status, snapshot, refresh, update, error } = usePerAppWorkarounds(appId, nonSteam); const state = snapshot?.parsed.state; const issues = snapshot?.parsed.issues || []; const controlsDisabled = status !== "ready" || state === undefined; const [fpsValue, setFpsValue] = useState(null); const effectiveFpsValue = fpsValue ?? state?.dxvkFrameRate ?? 0; const fpsLabel = effectiveFpsValue > 0 ? `${effectiveFpsValue} FPS` : t("CONFIG_BASE_FPS_CAP_OFF", "Off"); useEffect(() => { setFpsValue(state?.dxvkFrameRate ?? null); }, [state?.dxvkFrameRate, status]); return ( <>
{t("CONFIG_WORKAROUNDS_TITLE", "Workarounds")}
{collapsed ? : }
{!collapsed && ( <> {status === "loading" && ( )} {status === "error" && ( <> void refresh()}>Retry )} {status === "ready" && issues.length > 0 && ( )} { setFpsValue(value); void update("dxvkFrameRate", value); }} disabled={controlsDisabled} /> {TOGGLE_ROWS.map((row) => ( void update(row.field, value)} disabled={controlsDisabled} /> ))} )} ); }