diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-10 12:58:44 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2026-09-10 12:58:44 -0400 |
| commit | de74f0d2499159ed1cf8f628a166302146ae1f13 (patch) | |
| tree | 7f9d1c26d1815a09a85652d9869b74d3bf92386d | |
| parent | 9902135e53be129bd6096d51d5e510ab298c1ae2 (diff) | |
| download | decky-lsfg-vk-de74f0d2499159ed1cf8f628a166302146ae1f13.tar.gz decky-lsfg-vk-de74f0d2499159ed1cf8f628a166302146ae1f13.zip | |
fix flatpak disable leftoverschore/migrate
| -rw-r--r-- | py_modules/lsfg_vk/plugin.py | 37 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/wrapper_service.py | 3 | ||||
| -rw-r--r-- | src/api/lsfgApi.ts | 14 | ||||
| -rw-r--r-- | src/components/ConfigFileTab.tsx | 119 | ||||
| -rw-r--r-- | src/components/ConfigurationTab.tsx | 50 | ||||
| -rw-r--r-- | src/components/Content.tsx | 34 |
6 files changed, 217 insertions, 40 deletions
diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index 071b19b..13df7a4 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -107,6 +107,43 @@ class Plugin: "error": f"Error reading config file: {error}", } + async def get_debug_file_contents(self): + files = ( + ("config", "LSFG-VK configuration", self.configuration_service.config_file_path), + ("workarounds", "Per-app workarounds", self.wrapper_service.sidecar_path), + ("wrapper", "Generated launch wrapper", self.wrapper_service.wrapper_path), + ("flatpak_extensions", "Flatpak extension ownership", self.flatpak_service.ownership_path), + ) + contents = [] + for file_id, label, path in files: + item = { + "id": file_id, + "label": label, + "path": str(path), + "exists": False, + "content": None, + "error": None, + } + try: + if path.is_symlink(): + item["error"] = "Path is a symlink; refusing to read it" + elif not path.exists(): + item["error"] = "File does not exist" + elif not path.is_file(): + item["error"] = "Path is not a regular file" + else: + item["exists"] = True + item["content"] = path.read_text(encoding="utf-8") + except Exception as error: + item["error"] = f"Error reading file: {error}" + contents.append(item) + return { + "success": True, + "message": "Debug file contents retrieved", + "error": None, + "files": contents, + } + async def get_lossless_scaling_branch_status(self): return self.steam_service.get_branch_status() diff --git a/py_modules/lsfg_vk/wrapper_service.py b/py_modules/lsfg_vk/wrapper_service.py index 1ed39bc..980f7ae 100644 --- a/py_modules/lsfg_vk/wrapper_service.py +++ b/py_modules/lsfg_vk/wrapper_service.py @@ -35,6 +35,8 @@ class WrapperService(BaseService): "DISABLE_GAMESCOPE_WSI", "DXVK_HDR", "SteamDeck", + "DISABLE_LSFGVK", + "DISABLE_LSFG", "DISABLE_VKBASALT", "ENABLE_VKBASALT", "MESA_LOADER_DRIVER_OVERRIDE", @@ -262,6 +264,7 @@ class WrapperService(BaseService): self._shell(f"--env=LSFGVK_CONFIG={config_file}"), '"--env=LSFGVK_FLATPAK=1"', '"--env=SteamAppId=$appid"', + '"--unset-env=DISABLE_LSFGVK" "--unset-env=DISABLE_LSFG"', '"--unset-env=DISABLE_GAMESCOPE_WSI"', '"--unset-env=ENABLE_GAMESCOPE_WSI"' if not state["disableGamescopeWsi"] else '"--env=ENABLE_GAMESCOPE_WSI=0"', diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts index b96acec..567e1ae 100644 --- a/src/api/lsfgApi.ts +++ b/src/api/lsfgApi.ts @@ -105,6 +105,19 @@ export interface FileContentResult extends ApiResult { path?: string; } +export interface DebugFileContent { + id: string; + label: string; + path: string; + exists: boolean; + content?: string | null; + error?: string | null; +} + +export interface DebugFileContentsResult extends ApiResult { + files?: DebugFileContent[]; +} + export interface FlatpakExtensionStatus extends ApiResult { message: string; available: boolean; @@ -143,3 +156,4 @@ export const setWorkaroundState = callable<[ TargetTransport | null | undefined, ], WorkaroundStateResult>("set_workaround_state"); export const removeWorkaroundState = callable<[string], WorkaroundStateResult>("remove_workaround_state"); +export const getDebugFileContents = callable<[], DebugFileContentsResult>("get_debug_file_contents"); diff --git a/src/components/ConfigFileTab.tsx b/src/components/ConfigFileTab.tsx index e3cc09d..07408d7 100644 --- a/src/components/ConfigFileTab.tsx +++ b/src/components/ConfigFileTab.tsx @@ -1,13 +1,79 @@ +import { ButtonItem, Field, PanelSection, PanelSectionRow, Spinner } from "@decky/ui"; import { useEffect, useState } from "react"; -import { Field, PanelSection, PanelSectionRow, Spinner } from "@decky/ui"; -import { getConfigFileContent, FileContentResult } from "../api/lsfgApi"; +import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri"; +import { getDebugFileContents, type DebugFileContent, type DebugFileContentsResult } from "../api/lsfgApi"; import t from "../i18n/i18n"; +function usePersistentCollapsed(key: string) { + const [collapsed, setCollapsed] = useState(() => { + try { + return localStorage.getItem(key) !== "false"; + } catch { + return true; + } + }); + + useEffect(() => { + try { + localStorage.setItem(key, String(collapsed)); + } catch { + // Persisting the view preference is optional. + } + }, [collapsed, key]); + + return [collapsed, () => setCollapsed((value) => !value)] as const; +} + +function DebugFileSection({ file }: { file: DebugFileContent }) { + const [collapsed, toggleCollapsed] = usePersistentCollapsed(`lsfg-debug-file-${file.id}-collapsed-v1`); + const status = file.exists ? "Present" : "Not present"; + + return ( + <> + <PanelSectionRow> + <Field + label={file.label} + description={`${file.path} ยท ${status}`} + bottomSeparator="none" + /> + </PanelSectionRow> + <PanelSectionRow> + <div + className="LSFG_DebugFileCollapseButton_Container" + style={{ marginTop: "-2px", marginBottom: "4px" }} + > + <ButtonItem + layout="below" + bottomSeparator={collapsed ? "standard" : "none"} + onClick={toggleCollapsed} + > + {collapsed ? <RiArrowDownSFill /> : <RiArrowUpSFill />} + </ButtonItem> + </div> + </PanelSectionRow> + {!collapsed && ( + <PanelSectionRow> + {file.exists && file.content !== null && file.content !== undefined ? ( + <pre style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}> + {file.content} + </pre> + ) : ( + <Field + label="File unavailable" + description={file.error || "The file has not been created yet."} + /> + )} + </PanelSectionRow> + )} + </> + ); +} + export function ConfigFileTab() { - const [result, setResult] = useState<FileContentResult | null>(null); + const [result, setResult] = useState<DebugFileContentsResult | null>(null); useEffect(() => { - getConfigFileContent().then(setResult).catch((error) => { + getDebugFileContents().then(setResult).catch((error) => { setResult({ success: false, error: String(error) }); }); }, []); @@ -23,24 +89,35 @@ export function ConfigFileTab() { } return ( - <PanelSection title={t("NERD_CONFIG_FILE", "Configuration File")}> - {result.error && ( - <PanelSectionRow> - <Field label="Error" description={result.error} /> - </PanelSectionRow> - )} - {result.success && result.content && ( - <> - <PanelSectionRow> - <Field label="Config file" description={result.path} /> - </PanelSectionRow> + <> + <style> + {` + .LSFG_DebugFileCollapseButton_Container > div > div > div > button, + .LSFG_DebugFileCollapseButton_Container > div > div > div > div > button { + height: 24px !important; + min-height: 24px !important; + padding: 0 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + } + + .LSFG_DebugFileCollapseButton_Container svg { + display: block; + margin: 0; + } + `} + </style> + <PanelSection title={t("NERD_CONFIG_FILE", "Configuration File")}> + {result.error && ( <PanelSectionRow> - <pre style={{ whiteSpace: "pre-wrap", overflowWrap: "anywhere" }}> - {result.content} - </pre> + <Field label="Error" description={result.error} /> </PanelSectionRow> - </> - )} - </PanelSection> + )} + {result.success && result.files?.map((file) => ( + <DebugFileSection key={file.id} file={file} /> + ))} + </PanelSection> + </> ); } diff --git a/src/components/ConfigurationTab.tsx b/src/components/ConfigurationTab.tsx index 5ae4a87..4e2d93d 100644 --- a/src/components/ConfigurationTab.tsx +++ b/src/components/ConfigurationTab.tsx @@ -1,4 +1,4 @@ -import { ButtonItem, ConfirmModal, DialogButton, Focusable, PanelSection, PanelSectionRow, gamepadDialogClasses, showModal } from "@decky/ui"; +import { ButtonItem, ConfirmModal, DialogButton, Focusable, PanelSection, PanelSectionRow, ToggleField, gamepadDialogClasses, showModal } from "@decky/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import { FaArrowLeft } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; @@ -11,6 +11,8 @@ interface ConfigurationTabProps { config: ConfigurationData; targets: GameTarget[]; runningGame: GameTarget | null; + showDebugTab: boolean; + onShowDebugTabChange: (value: boolean) => void; onSelect: (appid: string) => void; onConfigChange: (fieldName: keyof ConfigurationData, value: boolean | number | string | string[]) => Promise<void>; onEnable: (appid: string) => Promise<boolean>; @@ -24,6 +26,8 @@ export function ConfigurationTab({ config, targets, runningGame, + showDebugTab, + onShowDebugTabChange, onSelect, onConfigChange, onEnable, @@ -63,22 +67,34 @@ export function ConfigurationTab({ if (detailAppId === null) { return ( - <PanelSection title="Games"> - <GameConfigurationSelector - targets={targets} - runningGame={runningGame} - onSelect={(appid) => { - setFocusConfiguredToggle(false); - setFocusDetailAction(targets.find((target) => target.appid === appid)?.configured ? "fps" : "enable"); - onSelect(appid); - setDetailAppId(appid); - }} - onEnableAll={onEnableAll} - onResetAll={onResetAll} - focusConfiguredToggle={focusConfiguredToggle} - onConfiguredToggleFocused={clearConfiguredToggleFocusRequest} - /> - </PanelSection> + <> + <PanelSection title="Games"> + <GameConfigurationSelector + targets={targets} + runningGame={runningGame} + onSelect={(appid) => { + setFocusConfiguredToggle(false); + setFocusDetailAction(targets.find((target) => target.appid === appid)?.configured ? "fps" : "enable"); + onSelect(appid); + setDetailAppId(appid); + }} + onEnableAll={onEnableAll} + onResetAll={onResetAll} + focusConfiguredToggle={focusConfiguredToggle} + onConfiguredToggleFocused={clearConfiguredToggleFocusRequest} + /> + </PanelSection> + <PanelSection title="Settings"> + <PanelSectionRow> + <ToggleField + label="Show debug tab" + description="Show the raw configuration and generated files tab for troubleshooting." + checked={showDebugTab} + onChange={onShowDebugTabChange} + /> + </PanelSectionRow> + </PanelSection> + </> ); } diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 578f984..43720c0 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -17,6 +17,29 @@ const tabIcons = { setup: <FaTools size={18} />, }; +const DEBUG_TAB_VISIBILITY_KEY = "lsfg-debug-tab-visible-v1"; + +function usePersistentBoolean(key: string, defaultValue: boolean) { + const [value, setValue] = useState(() => { + try { + const stored = localStorage.getItem(key); + return stored === null ? defaultValue : stored === "true"; + } catch { + return defaultValue; + } + }); + + useEffect(() => { + try { + localStorage.setItem(key, String(value)); + } catch { + // Persisting the visibility preference is optional. + } + }, [key, value]); + + return [value, setValue] as const; +} + export function Content() { const { config, @@ -43,6 +66,7 @@ export function Content() { uninstall, } = useInstallation(reload); const [tab, setTab] = useState("Setup"); + const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, true); const previousRunningAppId = useRef<string | null>(null); const setupComplete = isInstalled && @@ -74,6 +98,10 @@ export function Content() { if (isInstalled) void reload(); }, [isInstalled, reload]); + useEffect(() => { + if (!showDebugTab && tab === "ConfigFile") setTab("Games"); + }, [showDebugTab, tab]); + const handleConfigChange = async ( fieldName: keyof ConfigurationData, value: boolean | number | string | string[], @@ -116,6 +144,8 @@ export function Content() { config={config} targets={targets} runningGame={runningGame} + showDebugTab={showDebugTab} + onShowDebugTabChange={setShowDebugTab} onSelect={setSelectedAppId} onConfigChange={(field, value) => handleConfigChange(field, value, true)} onEnable={enable} @@ -126,7 +156,7 @@ export function Content() { /> ), }, - { id: "ConfigFile", title: tabIcons.configFile, content: <ConfigFileTab /> }, + ...(showDebugTab ? [{ id: "ConfigFile", title: tabIcons.configFile, content: <ConfigFileTab /> }] : []), { id: "Setup", title: tabIcons.setup, content: setup }, ] : [{ id: "Setup", title: tabIcons.setup, content: setup }]; @@ -137,7 +167,7 @@ export function Content() { style={{ height: "95%", width: "300px", position: "fixed", marginTop: "-12px", overflow: "hidden" }} > <style>{tabStyles}</style> - <Tabs activeTab={tab} onShowTab={setTab} tabs={tabs} /> + <Tabs activeTab={!showDebugTab && tab === "ConfigFile" ? "Games" : tab} onShowTab={setTab} tabs={tabs} /> </div> ); } |
