From 234ac6e00c4e9e337e4c88829deee665d1a5303c Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 18 Aug 2025 12:08:21 -0400 Subject: lock profile selection if game is running --- package.json | 2 +- src/components/ProfileManagement.tsx | 47 +++++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 9751add..7fcd3bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "decky-lossless-scaling-vk", - "version": "0.10.1", + "version": "0.10.2", "description": "Use Lossless Scaling on the Steam Deck using the lsfg-vk vulkan layer", "type": "module", "scripts": { diff --git a/src/components/ProfileManagement.tsx b/src/components/ProfileManagement.tsx index 67f0645..46c507c 100644 --- a/src/components/ProfileManagement.tsx +++ b/src/components/ProfileManagement.tsx @@ -11,7 +11,9 @@ import { ButtonItem, ModalRoot, TextField, - Focusable + Focusable, + AppOverview, + Router } from "@decky/ui"; import { getProfiles, @@ -24,11 +26,6 @@ import { } from "../api/lsfgApi"; import { showSuccessToast, showErrorToast } from "../utils/toastUtils"; -interface ProfileManagementProps { - currentProfile?: string; - onProfileChange?: (profileName: string) => void; -} - interface TextInputModalProps { title: string; description: string; @@ -110,6 +107,7 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa const [profiles, setProfiles] = useState([]); const [selectedProfile, setSelectedProfile] = useState(currentProfile || "decky-lsfg-vk"); const [isLoading, setIsLoading] = useState(false); + const [mainRunningApp, setMainRunningApp] = useState(undefined); // Load profiles on component mount useEffect(() => { @@ -123,6 +121,22 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa } }, [currentProfile]); + // Poll for running app every 2 seconds + useEffect(() => { + const checkRunningApp = () => { + setMainRunningApp(Router.MainRunningApp); + }; + + // Check immediately + checkRunningApp(); + + // Set up polling interval + const interval = setInterval(checkRunningApp, 2000); + + // Cleanup interval on unmount + return () => clearInterval(interval); + }, []); + const loadProfiles = async () => { try { const result: ProfilesResult = await getProfiles(); @@ -301,6 +315,21 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa return ( + {/* Display currently running game info */} + {mainRunningApp && ( + +
+ {mainRunningApp.display_name} running. Close game to change profile. +
+
+ )} + @@ -320,7 +349,7 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa Rename @@ -330,7 +359,7 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa Delete -- cgit v1.2.3 From c6092d861cc3fb4d333dd47ec135474b030fb9b4 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 18 Aug 2025 12:33:26 -0400 Subject: make all sections collapsible --- src/components/ConfigurationSection.tsx | 162 ++++++++++++++++++++----------- src/components/Content.tsx | 16 +-- src/components/ProfileManagement.tsx | 166 +++++++++++++++++++++++--------- 3 files changed, 233 insertions(+), 111 deletions(-) diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index 92d1867..51fe47c 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -15,6 +15,7 @@ interface ConfigurationSectionProps { } const WORKAROUNDS_COLLAPSED_KEY = 'lsfg-workarounds-collapsed'; +const CONFIG_COLLAPSED_KEY = 'lsfg-config-collapsed'; export function ConfigurationSection({ config, @@ -30,6 +31,16 @@ export function ConfigurationSection({ } }); + // Initialize with localStorage value, fallback to false (expanded) if not found + const [configCollapsed, setConfigCollapsed] = useState(() => { + try { + const saved = localStorage.getItem(CONFIG_COLLAPSED_KEY); + return saved !== null ? JSON.parse(saved) : false; + } catch { + return false; + } + }); + // Persist workarounds collapse state to localStorage useEffect(() => { try { @@ -39,6 +50,15 @@ export function ConfigurationSection({ } }, [workaroundsCollapsed]); + // Persist config collapse state to localStorage + useEffect(() => { + try { + localStorage.setItem(CONFIG_COLLAPSED_KEY, JSON.stringify(configCollapsed)); + } catch (error) { + console.warn('Failed to save config collapse state:', error); + } + }, [configCollapsed]); + return ( <> @@ -68,69 +94,93 @@ export function ConfigurationSection({ - {/* FPS Multiplier */} - - - onConfigChange(FLOW_SCALE, value)} - /> +
+ setConfigCollapsed(!configCollapsed)} + > + {configCollapsed ? ( + + ) : ( + + )} + +
- - 0 ? ` (${config.dxvk_frame_rate} FPS)` : ' (Off)'}`} - description="Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)" - value={config.dxvk_frame_rate} - min={0} - max={60} - step={1} - onChange={(value) => onConfigChange(DXVK_FRAME_RATE, value)} - /> - + {!configCollapsed && ( + <> + {/* FPS Multiplier */} + - - onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")} - /> - + + onConfigChange(FLOW_SCALE, value)} + /> + - - onConfigChange(PERFORMANCE_MODE, value)} - /> - + + 0 ? ` (${config.dxvk_frame_rate} FPS)` : ' (Off)'}`} + description="Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)" + value={config.dxvk_frame_rate} + min={0} + max={60} + step={1} + onChange={(value) => onConfigChange(DXVK_FRAME_RATE, value)} + /> + - {/* - onConfigChange(NO_FP16, value)} - /> - */} + + onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")} + /> + - - onConfigChange(HDR_MODE, value)} - /> - + + onConfigChange(PERFORMANCE_MODE, value)} + /> + + + {/* + onConfigChange(NO_FP16, value)} + /> + */} + + + onConfigChange(HDR_MODE, value)} + /> + + + )} {/* Workarounds Section */} diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 7815951..142f111 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -93,6 +93,14 @@ export function Content() { + {/* Configuration Section - only show if installed */} + {isInstalled && ( + + )} + {/* Profile Management - only show if installed */} {isInstalled && ( )} - {/* Configuration Section - only show if installed */} - {isInstalled && ( - - )} - diff --git a/src/components/ProfileManagement.tsx b/src/components/ProfileManagement.tsx index 46c507c..4a75484 100644 --- a/src/components/ProfileManagement.tsx +++ b/src/components/ProfileManagement.tsx @@ -1,6 +1,5 @@ import { useState, useEffect } from "react"; import { - PanelSection, PanelSectionRow, Dropdown, DropdownOption, @@ -15,6 +14,7 @@ import { AppOverview, Router } from "@decky/ui"; +import { RiArrowDownSFill, RiArrowUpSFill } from "react-icons/ri"; import { getProfiles, createProfile, @@ -26,6 +26,8 @@ import { } from "../api/lsfgApi"; import { showSuccessToast, showErrorToast } from "../utils/toastUtils"; +const PROFILES_COLLAPSED_KEY = 'lsfg-profiles-collapsed'; + interface TextInputModalProps { title: string; description: string; @@ -108,6 +110,25 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa const [selectedProfile, setSelectedProfile] = useState(currentProfile || "decky-lsfg-vk"); const [isLoading, setIsLoading] = useState(false); const [mainRunningApp, setMainRunningApp] = useState(undefined); + + // Initialize with localStorage value, fallback to false (expanded) if not found + const [profilesCollapsed, setProfilesCollapsed] = useState(() => { + try { + const saved = localStorage.getItem(PROFILES_COLLAPSED_KEY); + return saved !== null ? JSON.parse(saved) : false; + } catch { + return false; + } + }); + + // Persist profiles collapse state to localStorage + useEffect(() => { + try { + localStorage.setItem(PROFILES_COLLAPSED_KEY, JSON.stringify(profilesCollapsed)); + } catch (error) { + console.warn('Failed to save profiles collapse state:', error); + } + }, [profilesCollapsed]); // Load profiles on component mount useEffect(() => { @@ -314,56 +335,107 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa ]; return ( - - {/* Display currently running game info */} - {mainRunningApp && ( - -
- {mainRunningApp.display_name} running. Close game to change profile. -
-
- )} - - - - - - - + <> + + - - Rename - + Select Profile + - + - - Delete - +
+ setProfilesCollapsed(!profilesCollapsed)} + > + {profilesCollapsed ? ( + + ) : ( + + )} + +
-
+ + {!profilesCollapsed && ( + <> + {/* Display currently running game info */} + {mainRunningApp && ( + +
+ {mainRunningApp.display_name} running. Close game to change profile. +
+
+ )} + + + + + + + + + + Rename + + + + + + Delete + + + + )} + ); } -- cgit v1.2.3 From 119d3d7cb21fb96f220ec25f478d4b3621ae402f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 18 Aug 2025 12:36:35 -0400 Subject: profile at top and readout in header --- src/components/Content.tsx | 16 ++++++++-------- src/components/ProfileManagement.tsx | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 142f111..7815951 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -93,14 +93,6 @@ export function Content() { - {/* Configuration Section - only show if installed */} - {isInstalled && ( - - )} - {/* Profile Management - only show if installed */} {isInstalled && ( )} + {/* Configuration Section - only show if installed */} + {isInstalled && ( + + )} + diff --git a/src/components/ProfileManagement.tsx b/src/components/ProfileManagement.tsx index 4a75484..7c8966b 100644 --- a/src/components/ProfileManagement.tsx +++ b/src/components/ProfileManagement.tsx @@ -359,7 +359,7 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa color: "white" }} > - Select Profile + Profile: {selectedProfile === "decky-lsfg-vk" ? "Default" : selectedProfile}
-- cgit v1.2.3 From 3c008cfc633701ad75207f94b9c694bd217e763a Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 18 Aug 2025 12:41:41 -0400 Subject: move install uninstall button and status to bottom to buy ui realestate --- src/components/Content.tsx | 57 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 7815951..e0adf3f 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -76,20 +76,25 @@ export function Content() { return ( - - - + {/* Show installation components at top when not fully installed */} + {!isInstalled && ( + <> + + + + + )} @@ -118,7 +123,29 @@ export function Content() { {/* Plugin Update Checker */} - {/* Nerd Stuff Button */} + + + {/* Show installation components at bottom when fully installed */} + {isInstalled && ( + <> + + + + + )} + + {/* Nerd Stuff Button */} Date: Mon, 18 Aug 2025 12:46:49 -0400 Subject: rm collapse for core configs, buy more ui room --- src/components/ConfigurationSection.tsx | 174 ++++++++++---------------------- 1 file changed, 54 insertions(+), 120 deletions(-) diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index 51fe47c..c14a2fd 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -15,7 +15,6 @@ interface ConfigurationSectionProps { } const WORKAROUNDS_COLLAPSED_KEY = 'lsfg-workarounds-collapsed'; -const CONFIG_COLLAPSED_KEY = 'lsfg-config-collapsed'; export function ConfigurationSection({ config, @@ -31,16 +30,6 @@ export function ConfigurationSection({ } }); - // Initialize with localStorage value, fallback to false (expanded) if not found - const [configCollapsed, setConfigCollapsed] = useState(() => { - try { - const saved = localStorage.getItem(CONFIG_COLLAPSED_KEY); - return saved !== null ? JSON.parse(saved) : false; - } catch { - return false; - } - }); - // Persist workarounds collapse state to localStorage useEffect(() => { try { @@ -50,15 +39,6 @@ export function ConfigurationSection({ } }, [workaroundsCollapsed]); - // Persist config collapse state to localStorage - useEffect(() => { - try { - localStorage.setItem(CONFIG_COLLAPSED_KEY, JSON.stringify(configCollapsed)); - } catch (error) { - console.warn('Failed to save config collapse state:', error); - } - }, [configCollapsed]); - return ( <> + {/* FPS Multiplier */} + + -
- LSFG Configuration -
+ onConfigChange(FLOW_SCALE, value)} + />
-
- setConfigCollapsed(!configCollapsed)} - > - {configCollapsed ? ( - - ) : ( - - )} - -
+ 0 ? ` (${config.dxvk_frame_rate} FPS)` : ' (Off)'}`} + description="Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)" + value={config.dxvk_frame_rate} + min={0} + max={60} + step={1} + onChange={(value) => onConfigChange(DXVK_FRAME_RATE, value)} + />
- {!configCollapsed && ( - <> - {/* FPS Multiplier */} - - - - onConfigChange(FLOW_SCALE, value)} - /> - - - - 0 ? ` (${config.dxvk_frame_rate} FPS)` : ' (Off)'}`} - description="Base framerate cap for DirectX games, before frame multiplier. (Requires game restart to apply)" - value={config.dxvk_frame_rate} - min={0} - max={60} - step={1} - onChange={(value) => onConfigChange(DXVK_FRAME_RATE, value)} - /> - - - - onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")} - /> - + + onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")} + /> + - - onConfigChange(PERFORMANCE_MODE, value)} - /> - + + onConfigChange(PERFORMANCE_MODE, value)} + /> + - {/* - onConfigChange(NO_FP16, value)} - /> - */} + {/* + onConfigChange(NO_FP16, value)} + /> + */} - - onConfigChange(HDR_MODE, value)} - /> - - - )} + + onConfigChange(HDR_MODE, value)} + /> + {/* Workarounds Section */} -- cgit v1.2.3 From 687d017c02d5a7dc37fde941c583ff74d8dd6363 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 18 Aug 2025 12:50:42 -0400 Subject: visibility for profile lock indicator, ver bump --- package.json | 2 +- src/components/ProfileManagement.tsx | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 7fcd3bd..bcc9af4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "decky-lossless-scaling-vk", - "version": "0.10.2", + "version": "0.10.3", "description": "Use Lossless Scaling on the Steam Deck using the lsfg-vk vulkan layer", "type": "module", "scripts": { diff --git a/src/components/ProfileManagement.tsx b/src/components/ProfileManagement.tsx index 7c8966b..6e2a8f8 100644 --- a/src/components/ProfileManagement.tsx +++ b/src/components/ProfileManagement.tsx @@ -347,6 +347,21 @@ export function ProfileManagement({ currentProfile, onProfileChange }: ProfileMa `} + {/* Display currently running game info - always visible */} + {mainRunningApp && ( + +
+ {mainRunningApp.display_name} running. Close game to change profile. +
+
+ )} +
- {/* Display currently running game info */} - {mainRunningApp && ( - -
- {mainRunningApp.display_name} running. Close game to change profile. -
-
- )} -