From e8e469f99078858dc953663cba6f3428e80b1c5d Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sat, 5 Sep 2026 23:44:37 -0400 Subject: refactor: delegate runtime checks to lsfg-vk --- src/components/Content.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 5988e79..aab2fb8 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,6 +1,6 @@ import { useEffect } from "react"; import { PanelSection, showModal, ButtonItem, PanelSectionRow } from "@decky/ui"; -import { useInstallationStatus, useDllDetection, useLsfgConfig } from "../hooks/useLsfgHooks"; +import { useInstallationStatus, useLsfgConfig } from "../hooks/useLsfgHooks"; import { useProfileManagement } from "../hooks/useProfileManagement"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { StatusDisplay } from "./StatusDisplay"; @@ -21,11 +21,12 @@ export function Content() { isInstalled, installationStatus, setIsInstalled, - setInstallationStatus + setInstallationStatus, + losslessScalingInstalled, + losslessScalingStatus, + checkInstallation } = useInstallationStatus(); - const { dllDetected, dllDetectionStatus } = useDllDetection(); - const { config, loadLsfgConfig, @@ -59,11 +60,11 @@ export function Content() { }; const onInstall = () => { - handleInstall(setIsInstalled, setInstallationStatus, loadLsfgConfig); + handleInstall(setIsInstalled, setInstallationStatus, loadLsfgConfig, checkInstallation); }; const onUninstall = () => { - handleUninstall(setIsInstalled, setInstallationStatus); + handleUninstall(setIsInstalled, setInstallationStatus, checkInstallation); }; const handleShowNerdStuff = () => { @@ -87,10 +88,10 @@ export function Content() { /> )} @@ -167,10 +168,10 @@ export function Content() { {isInstalled && ( <> Date: Sat, 5 Sep 2026 23:58:16 -0400 Subject: feat: select Lossless Scaling lsfg-vk branch --- src/components/Content.tsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index aab2fb8..4e4e33a 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -15,6 +15,7 @@ import { NerdStuffModal } from "./NerdStuffModal"; import { FlatpaksModal } from "./FlatpaksModal"; import { ConfigurationData } from "../config/configSchema"; import t from '../i18n/i18n'; +import { showErrorToast, showSuccessToast } from "../utils/toastUtils"; export function Content() { const { @@ -24,6 +25,9 @@ export function Content() { setInstallationStatus, losslessScalingInstalled, losslessScalingStatus, + steamBranchStatus, + isSwitchingSteamBranch, + selectLosslessScalingBranch, checkInstallation } = useInstallationStatus(); @@ -67,6 +71,18 @@ export function Content() { handleUninstall(setIsInstalled, setInstallationStatus, checkInstallation); }; + const onSelectLosslessScalingBranch = async () => { + const result = await selectLosslessScalingBranch(); + if (result.success) { + showSuccessToast("Steam branch selected", result.message); + } else { + showErrorToast( + "Steam branch selection failed", + result.error || "Unable to select the lsfg-vk Steam branch" + ); + } + }; + const handleShowNerdStuff = () => { showModal(); }; @@ -92,6 +108,9 @@ export function Content() { installationStatus={installationStatus} losslessScalingInstalled={losslessScalingInstalled} losslessScalingStatus={losslessScalingStatus} + steamBranchStatus={steamBranchStatus} + isSwitchingSteamBranch={isSwitchingSteamBranch} + onSelectLosslessScalingBranch={onSelectLosslessScalingBranch} /> )} @@ -172,6 +191,9 @@ export function Content() { installationStatus={installationStatus} losslessScalingInstalled={losslessScalingInstalled} losslessScalingStatus={losslessScalingStatus} + steamBranchStatus={steamBranchStatus} + isSwitchingSteamBranch={isSwitchingSteamBranch} + onSelectLosslessScalingBranch={onSelectLosslessScalingBranch} /> Date: Sun, 6 Sep 2026 00:16:19 -0400 Subject: refactor: make Steam branch integration read-only --- src/components/Content.tsx | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 4e4e33a..01f94c9 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -15,7 +15,6 @@ import { NerdStuffModal } from "./NerdStuffModal"; import { FlatpaksModal } from "./FlatpaksModal"; import { ConfigurationData } from "../config/configSchema"; import t from '../i18n/i18n'; -import { showErrorToast, showSuccessToast } from "../utils/toastUtils"; export function Content() { const { @@ -26,8 +25,6 @@ export function Content() { losslessScalingInstalled, losslessScalingStatus, steamBranchStatus, - isSwitchingSteamBranch, - selectLosslessScalingBranch, checkInstallation } = useInstallationStatus(); @@ -71,18 +68,6 @@ export function Content() { handleUninstall(setIsInstalled, setInstallationStatus, checkInstallation); }; - const onSelectLosslessScalingBranch = async () => { - const result = await selectLosslessScalingBranch(); - if (result.success) { - showSuccessToast("Steam branch selected", result.message); - } else { - showErrorToast( - "Steam branch selection failed", - result.error || "Unable to select the lsfg-vk Steam branch" - ); - } - }; - const handleShowNerdStuff = () => { showModal(); }; @@ -109,8 +94,6 @@ export function Content() { losslessScalingInstalled={losslessScalingInstalled} losslessScalingStatus={losslessScalingStatus} steamBranchStatus={steamBranchStatus} - isSwitchingSteamBranch={isSwitchingSteamBranch} - onSelectLosslessScalingBranch={onSelectLosslessScalingBranch} /> )} @@ -192,8 +175,6 @@ export function Content() { losslessScalingInstalled={losslessScalingInstalled} losslessScalingStatus={losslessScalingStatus} steamBranchStatus={steamBranchStatus} - isSwitchingSteamBranch={isSwitchingSteamBranch} - onSelectLosslessScalingBranch={onSelectLosslessScalingBranch} /> Date: Sun, 6 Sep 2026 15:08:45 -0400 Subject: refactor: offload configuration to lsfg-vk --- src/components/Content.tsx | 48 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 01f94c9..bdb3a04 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,12 +1,12 @@ import { useEffect } from "react"; import { PanelSection, showModal, ButtonItem, PanelSectionRow } from "@decky/ui"; -import { useInstallationStatus, useLsfgConfig } from "../hooks/useLsfgHooks"; -import { useProfileManagement } from "../hooks/useProfileManagement"; +import { useInstallationStatus } from "../hooks/useLsfgHooks"; +import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { StatusDisplay } from "./StatusDisplay"; import { InstallationButton } from "./InstallationButton"; import { ConfigurationSection } from "./ConfigurationSection"; -import { ProfileManagement } from "./ProfileManagement"; +import { GameConfigurationSelector } from "./GameConfigurationSelector"; import { UsageInstructions } from "./UsageInstructions"; import { SmartClipboardButton } from "./SmartClipboardButton"; import { FgmodClipboardButton } from "./FgmodClipboardButton"; @@ -28,40 +28,20 @@ export function Content() { checkInstallation } = useInstallationStatus(); - const { - config, - loadLsfgConfig, - updateField - } = useLsfgConfig(); - - const { - currentProfile, - updateProfileConfig, - loadProfiles - } = useProfileManagement(); + const { config, targets, runningGame, selectedAppId, setSelectedAppId, save, resetSelected, resetAll, reload } = useGameConfiguration(); const { isInstalling, isUninstalling, handleInstall, handleUninstall } = useInstallationActions(); useEffect(() => { - if (isInstalled) { - loadLsfgConfig(); - } - }, [isInstalled, loadLsfgConfig]); - - const handleConfigChange = async (fieldName: keyof ConfigurationData, value: boolean | number | string) => { - if (currentProfile) { - const newConfig = { ...config, [fieldName]: value }; - const result = await updateProfileConfig(currentProfile, newConfig); - if (result.success) { - await loadLsfgConfig(); - } - } else { - await updateField(fieldName, value); - } + if (isInstalled) void reload(); + }, [isInstalled, reload]); + + const handleConfigChange = async (fieldName: keyof ConfigurationData, value: boolean | number | string | string[]) => { + await save({ ...config, [fieldName]: value }); }; const onInstall = () => { - handleInstall(setIsInstalled, setInstallationStatus, loadLsfgConfig, checkInstallation); + handleInstall(setIsInstalled, setInstallationStatus, reload, checkInstallation); }; const onUninstall = () => { @@ -124,13 +104,7 @@ export function Content() { )} {isInstalled && ( - { - await loadProfiles(); - await loadLsfgConfig(); - }} - /> + )} {isInstalled && ( -- cgit v1.2.3 From c9be32287ad5b72fcd86b10fa726d09dbd97d7fd Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sun, 6 Sep 2026 17:09:54 -0400 Subject: refactor: organize plugin into native tabs --- src/components/Content.tsx | 223 ++++++++++++++++++--------------------------- 1 file changed, 91 insertions(+), 132 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index bdb3a04..de8f996 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,20 +1,22 @@ -import { useEffect } from "react"; -import { PanelSection, showModal, ButtonItem, PanelSectionRow } from "@decky/ui"; -import { useInstallationStatus } from "../hooks/useLsfgHooks"; +import { Tabs } from "@decky/ui"; +import { useEffect, useState } from "react"; +import { FaFileAlt, FaGamepad, FaLayerGroup, FaTools } from "react-icons/fa"; +import { ConfigurationData } from "../config/configSchema"; +import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallationActions } from "../hooks/useInstallationActions"; -import { StatusDisplay } from "./StatusDisplay"; -import { InstallationButton } from "./InstallationButton"; -import { ConfigurationSection } from "./ConfigurationSection"; -import { GameConfigurationSelector } from "./GameConfigurationSelector"; -import { UsageInstructions } from "./UsageInstructions"; -import { SmartClipboardButton } from "./SmartClipboardButton"; -import { FgmodClipboardButton } from "./FgmodClipboardButton"; -import { FpsMultiplierControl } from "./FpsMultiplierControl"; -import { NerdStuffModal } from "./NerdStuffModal"; -import { FlatpaksModal } from "./FlatpaksModal"; -import { ConfigurationData } from "../config/configSchema"; -import t from '../i18n/i18n'; +import { useInstallationStatus } from "../hooks/useLsfgHooks"; +import { ConfigFileTab } from "./ConfigFileTab"; +import { ConfigurationTab } from "./ConfigurationTab"; +import { FlatpaksTab } from "./FlatpaksTab"; +import { SetupTab } from "./SetupTab"; + +const tabIcons = { + configuration: , + flatpak: , + configFile: , + setup: , +}; export function Content() { const { @@ -25,141 +27,98 @@ export function Content() { losslessScalingInstalled, losslessScalingStatus, steamBranchStatus, - checkInstallation + checkInstallation, } = useInstallationStatus(); - - const { config, targets, runningGame, selectedAppId, setSelectedAppId, save, resetSelected, resetAll, reload } = useGameConfiguration(); - + const { + config, + targets, + runningGame, + selectedAppId, + setSelectedAppId, + save, + resetSelected, + resetAll, + reload, + } = useGameConfiguration(); const { isInstalling, isUninstalling, handleInstall, handleUninstall } = useInstallationActions(); + const [tab, setTab] = useState("Setup"); + const setupComplete = + isInstalled && + losslessScalingInstalled && + steamBranchStatus?.success === true && + steamBranchStatus.installed && + !steamBranchStatus.needs_switch; + + useEffect(() => { + setTab(setupComplete ? "Configuration" : "Setup"); + }, [setupComplete]); useEffect(() => { if (isInstalled) void reload(); }, [isInstalled, reload]); - const handleConfigChange = async (fieldName: keyof ConfigurationData, value: boolean | number | string | string[]) => { + const handleConfigChange = async ( + fieldName: keyof ConfigurationData, + value: boolean | number | string | string[], + ) => { await save({ ...config, [fieldName]: value }); }; const onInstall = () => { - handleInstall(setIsInstalled, setInstallationStatus, reload, checkInstallation); + void handleInstall(setIsInstalled, setInstallationStatus, reload, checkInstallation); }; const onUninstall = () => { - handleUninstall(setIsInstalled, setInstallationStatus, checkInstallation); + void handleUninstall(setIsInstalled, setInstallationStatus, checkInstallation); }; - const handleShowNerdStuff = () => { - showModal(); - }; + const setupContent = ( + + ); - const handleShowFlatpaks = () => { - showModal(); - }; + const tabs = setupComplete + ? [ + { + id: "Configuration", + title: tabIcons.configuration, + content: ( + + ), + }, + { id: "Flatpak", title: tabIcons.flatpak, content: }, + { id: "ConfigFile", title: tabIcons.configFile, content: }, + { id: "Setup", title: tabIcons.setup, content: setupContent }, + ] + : [ + { id: "Setup", title: tabIcons.setup, content: setupContent }, + ]; return ( - - {!isInstalled && ( - <> - - - - - )} - - {isInstalled && ( - <> - -
- {t('CONTENT_FPS_MULTIPLIER', 'FPS Multiplier')} -
-
- - - - )} - - {isInstalled && ( - - )} - - {isInstalled && ( - - )} - - {isInstalled && ( - <> - - - - )} - - - - - - {t('CONTENT_NERD_STUFF', 'Nerd Stuff')} - - - - - - {t('CONTENT_FLATPAK_SETUP', 'Flatpak Setup')} - - - - {isInstalled && ( - <> - - - - - )} -
+
+ + +
); } -- cgit v1.2.3 From 7bc5f685186b1ff03ce0f7c99e7bec411beabaeb Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Sun, 6 Sep 2026 22:04:54 -0400 Subject: feat: make ui suck less, frfr --- src/components/Content.tsx | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index de8f996..9281d39 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,6 +1,6 @@ import { Tabs } from "@decky/ui"; -import { useEffect, useState } from "react"; -import { FaFileAlt, FaGamepad, FaLayerGroup, FaTools } from "react-icons/fa"; +import { useEffect, useRef, useState } from "react"; +import { FaFileAlt, FaGamepad, FaLayerGroup, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; @@ -9,10 +9,12 @@ import { useInstallationStatus } from "../hooks/useLsfgHooks"; import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; import { FlatpaksTab } from "./FlatpaksTab"; +import { NowPlayingTab } from "./NowPlayingTab"; import { SetupTab } from "./SetupTab"; const tabIcons = { - configuration: , + nowPlaying: , + configuration: , flatpak: , configFile: , setup: , @@ -33,9 +35,9 @@ export function Content() { config, targets, runningGame, - selectedAppId, setSelectedAppId, save, + enable, resetSelected, resetAll, reload, @@ -48,10 +50,27 @@ export function Content() { steamBranchStatus?.success === true && steamBranchStatus.installed && !steamBranchStatus.needs_switch; + const previousRunningState = useRef<{ appid: string; configured: boolean } | null>(null); useEffect(() => { - setTab(setupComplete ? "Configuration" : "Setup"); - }, [setupComplete]); + if (!setupComplete) { + setTab("Setup"); + return; + } + setTab((current) => current === "Setup" ? (runningGame?.configured ? "NowPlaying" : "Configuration") : current); + }, [runningGame?.configured, setupComplete]); + + useEffect(() => { + if (!setupComplete) return; + const current = runningGame ? { appid: runningGame.appid, configured: runningGame.configured } : null; + const previous = previousRunningState.current; + previousRunningState.current = current; + if (current?.appid && (current.appid !== previous?.appid || current.configured !== previous?.configured)) { + setTab(current.configured ? "NowPlaying" : "Configuration"); + } else if (!current && previous) { + setTab((currentTab) => currentTab === "NowPlaying" ? "Configuration" : currentTab); + } + }, [runningGame?.appid, runningGame?.configured, setupComplete]); useEffect(() => { if (isInstalled) void reload(); @@ -88,6 +107,18 @@ export function Content() { const tabs = setupComplete ? [ + ...(runningGame?.configured ? [{ + id: "NowPlaying", + title: tabIcons.nowPlaying, + content: ( + + ), + }] : []), { id: "Configuration", title: tabIcons.configuration, @@ -96,9 +127,9 @@ export function Content() { config={config} targets={targets} runningGame={runningGame} - selectedAppId={selectedAppId} onSelect={setSelectedAppId} onConfigChange={handleConfigChange} + onEnable={enable} onReset={resetSelected} onResetAll={resetAll} /> -- cgit v1.2.3 From 62531dd08e5f12f1f0533ccf8fa6b84c81eb63d4 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 7 Sep 2026 00:50:43 -0400 Subject: chore: hide config file tab and clarify flatpak runtimes --- src/components/Content.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 9281d39..1bc6e95 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -6,7 +6,7 @@ import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { useInstallationStatus } from "../hooks/useLsfgHooks"; -import { ConfigFileTab } from "./ConfigFileTab"; +// import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; import { FlatpaksTab } from "./FlatpaksTab"; import { NowPlayingTab } from "./NowPlayingTab"; @@ -136,7 +136,8 @@ export function Content() { ), }, { id: "Flatpak", title: tabIcons.flatpak, content: }, - { id: "ConfigFile", title: tabIcons.configFile, content: }, + // Keep the configuration-file view available for future use without exposing it in the UI. + // { id: "ConfigFile", title: tabIcons.configFile, content: }, { id: "Setup", title: tabIcons.setup, content: setupContent }, ] : [ -- cgit v1.2.3 From 22db1125238e54b29538102727c36284e122e703 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Mon, 7 Sep 2026 16:02:26 -0400 Subject: feat: refine game discovery and profile UX --- src/components/Content.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 1bc6e95..f1c8c11 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -6,7 +6,7 @@ import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { useInstallationStatus } from "../hooks/useLsfgHooks"; -// import { ConfigFileTab } from "./ConfigFileTab"; +import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; import { FlatpaksTab } from "./FlatpaksTab"; import { NowPlayingTab } from "./NowPlayingTab"; @@ -38,6 +38,7 @@ export function Content() { setSelectedAppId, save, enable, + enableAll, resetSelected, resetAll, reload, @@ -115,7 +116,6 @@ export function Content() { game={runningGame} config={config} onConfigChange={handleConfigChange} - onRemove={resetSelected} /> ), }] : []), @@ -130,14 +130,14 @@ export function Content() { onSelect={setSelectedAppId} onConfigChange={handleConfigChange} onEnable={enable} + onEnableAll={enableAll} onReset={resetSelected} onResetAll={resetAll} /> ), }, { id: "Flatpak", title: tabIcons.flatpak, content: }, - // Keep the configuration-file view available for future use without exposing it in the UI. - // { id: "ConfigFile", title: tabIcons.configFile, content: }, + { id: "ConfigFile", title: tabIcons.configFile, content: }, // comment out for prod { id: "Setup", title: tabIcons.setup, content: setupContent }, ] : [ -- cgit v1.2.3 From bec26fe025c97c00d398e7a4fb571195706b9e76 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Tue, 8 Sep 2026 16:56:57 -0400 Subject: feat: more launch arg janitoring --- src/components/Content.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index f1c8c11..1d7d2d1 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -80,8 +80,9 @@ export function Content() { const handleConfigChange = async ( fieldName: keyof ConfigurationData, value: boolean | number | string | string[], + cleanupLaunchOptions = false, ) => { - await save({ ...config, [fieldName]: value }); + await save({ ...config, [fieldName]: value }, cleanupLaunchOptions); }; const onInstall = () => { @@ -115,7 +116,7 @@ export function Content() { handleConfigChange(fieldName, value)} /> ), }] : []), @@ -128,7 +129,7 @@ export function Content() { targets={targets} runningGame={runningGame} onSelect={setSelectedAppId} - onConfigChange={handleConfigChange} + onConfigChange={(fieldName, value) => handleConfigChange(fieldName, value, true)} onEnable={enable} onEnableAll={enableAll} onReset={resetSelected} -- cgit v1.2.3 From 1b932fd69c3dba925e0cbf027e05508b2daf5e8c Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Wed, 9 Sep 2026 01:01:42 -0400 Subject: add back launcher script and correct pathing --- src/components/Content.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 1d7d2d1..59dc514 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -39,6 +39,7 @@ export function Content() { save, enable, enableAll, + repair, resetSelected, resetAll, reload, @@ -132,6 +133,7 @@ export function Content() { onConfigChange={(fieldName, value) => handleConfigChange(fieldName, value, true)} onEnable={enable} onEnableAll={enableAll} + onRepair={repair} onReset={resetSelected} onResetAll={resetAll} /> -- cgit v1.2.3 From cc1e6f47dd9838b066822162a607d2859c043aff Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Wed, 9 Sep 2026 19:16:30 -0400 Subject: refactor: unify flatpak targets with steam profiles --- src/components/Content.tsx | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 59dc514..ff2376b 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,22 +1,18 @@ import { Tabs } from "@decky/ui"; import { useEffect, useRef, useState } from "react"; -import { FaFileAlt, FaGamepad, FaLayerGroup, FaList, FaTools } from "react-icons/fa"; +import { FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { useInstallationStatus } from "../hooks/useLsfgHooks"; -import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; -import { FlatpaksTab } from "./FlatpaksTab"; import { NowPlayingTab } from "./NowPlayingTab"; import { SetupTab } from "./SetupTab"; const tabIcons = { nowPlaying: , - configuration: , - flatpak: , - configFile: , + games: , setup: , }; @@ -38,7 +34,6 @@ export function Content() { setSelectedAppId, save, enable, - enableAll, repair, resetSelected, resetAll, @@ -52,25 +47,25 @@ export function Content() { steamBranchStatus?.success === true && steamBranchStatus.installed && !steamBranchStatus.needs_switch; - const previousRunningState = useRef<{ appid: string; configured: boolean } | null>(null); + const previousRunningAppId = useRef(null); useEffect(() => { if (!setupComplete) { setTab("Setup"); return; } - setTab((current) => current === "Setup" ? (runningGame?.configured ? "NowPlaying" : "Configuration") : current); - }, [runningGame?.configured, setupComplete]); + setTab((current) => current === "Setup" ? (runningGame ? "NowPlaying" : "Games") : current); + }, [runningGame?.appid, setupComplete]); useEffect(() => { if (!setupComplete) return; - const current = runningGame ? { appid: runningGame.appid, configured: runningGame.configured } : null; - const previous = previousRunningState.current; - previousRunningState.current = current; - if (current?.appid && (current.appid !== previous?.appid || current.configured !== previous?.configured)) { - setTab(current.configured ? "NowPlaying" : "Configuration"); - } else if (!current && previous) { - setTab((currentTab) => currentTab === "NowPlaying" ? "Configuration" : currentTab); + const appid = runningGame?.appid || null; + const previous = previousRunningAppId.current; + previousRunningAppId.current = appid; + if (appid && appid !== previous) { + setTab("NowPlaying"); + } else if (!appid && previous) { + setTab((currentTab) => currentTab === "NowPlaying" ? "Games" : currentTab); } }, [runningGame?.appid, runningGame?.configured, setupComplete]); @@ -105,12 +100,13 @@ export function Content() { isUninstalling={isUninstalling} onInstall={onInstall} onUninstall={onUninstall} + flatpakRelevant={targets.some((target) => target.transport.kind === "flatpak")} /> ); const tabs = setupComplete ? [ - ...(runningGame?.configured ? [{ + ...(runningGame ? [{ id: "NowPlaying", title: tabIcons.nowPlaying, content: ( @@ -118,12 +114,14 @@ export function Content() { game={runningGame} config={config} onConfigChange={(fieldName, value) => handleConfigChange(fieldName, value)} + onEnable={enable} + onRepair={repair} /> ), }] : []), { - id: "Configuration", - title: tabIcons.configuration, + id: "Games", + title: tabIcons.games, content: ( handleConfigChange(fieldName, value, true)} onEnable={enable} - onEnableAll={enableAll} onRepair={repair} onReset={resetSelected} onResetAll={resetAll} /> ), }, - { id: "Flatpak", title: tabIcons.flatpak, content: }, - { id: "ConfigFile", title: tabIcons.configFile, content: }, // comment out for prod { id: "Setup", title: tabIcons.setup, content: setupContent }, ] : [ -- cgit v1.2.3 From fb4d053213bdbda271a54b517a11a89c4780f80a Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Wed, 9 Sep 2026 20:45:20 -0400 Subject: fix: restore profile and Flatpak controls --- src/components/Content.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index ff2376b..58512d5 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,18 +1,20 @@ import { Tabs } from "@decky/ui"; import { useEffect, useRef, useState } from "react"; -import { FaGamepad, FaList, FaTools } from "react-icons/fa"; +import { FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallationActions } from "../hooks/useInstallationActions"; import { useInstallationStatus } from "../hooks/useLsfgHooks"; import { ConfigurationTab } from "./ConfigurationTab"; +import { ConfigFileTab } from "./ConfigFileTab"; import { NowPlayingTab } from "./NowPlayingTab"; import { SetupTab } from "./SetupTab"; const tabIcons = { nowPlaying: , games: , + configFile: , setup: , }; @@ -34,6 +36,7 @@ export function Content() { setSelectedAppId, save, enable, + enableAll, repair, resetSelected, resetAll, @@ -130,12 +133,18 @@ export function Content() { onSelect={setSelectedAppId} onConfigChange={(fieldName, value) => handleConfigChange(fieldName, value, true)} onEnable={enable} + onEnableAll={enableAll} onRepair={repair} onReset={resetSelected} onResetAll={resetAll} /> ), }, + { + id: "ConfigFile", + title: tabIcons.configFile, + content: , + }, { id: "Setup", title: tabIcons.setup, content: setupContent }, ] : [ -- cgit v1.2.3 From 28ebc17785a8cca52f289b74261d1f310fd35904 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Thu, 10 Sep 2026 07:08:19 -0400 Subject: fix: detect wrapped Flatpak shortcuts --- src/components/Content.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 58512d5..d4f54e9 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -103,7 +103,6 @@ export function Content() { isUninstalling={isUninstalling} onInstall={onInstall} onUninstall={onUninstall} - flatpakRelevant={targets.some((target) => target.transport.kind === "flatpak")} /> ); -- cgit v1.2.3 From 450d3e5e6612d467a00bb937538fded640c66ecb Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 07:24:05 -0400 Subject: refactor: simplify migration implementation --- src/components/Content.tsx | 73 ++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 45 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index d4f54e9..d92ef86 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -2,12 +2,11 @@ import { Tabs } from "@decky/ui"; import { useEffect, useRef, useState } from "react"; import { FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; -import { tabStyles } from "../styles"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; -import { useInstallationActions } from "../hooks/useInstallationActions"; -import { useInstallationStatus } from "../hooks/useLsfgHooks"; -import { ConfigurationTab } from "./ConfigurationTab"; +import { useInstallation } from "../hooks/useLsfgHooks"; +import { tabStyles } from "../styles"; import { ConfigFileTab } from "./ConfigFileTab"; +import { ConfigurationTab } from "./ConfigurationTab"; import { NowPlayingTab } from "./NowPlayingTab"; import { SetupTab } from "./SetupTab"; @@ -19,16 +18,6 @@ const tabIcons = { }; export function Content() { - const { - isInstalled, - installationStatus, - setIsInstalled, - setInstallationStatus, - losslessScalingInstalled, - losslessScalingStatus, - steamBranchStatus, - checkInstallation, - } = useInstallationStatus(); const { config, targets, @@ -42,15 +31,25 @@ export function Content() { resetAll, reload, } = useGameConfiguration(); - const { isInstalling, isUninstalling, handleInstall, handleUninstall } = useInstallationActions(); + const { + isInstalled, + installationStatus, + losslessScalingInstalled, + losslessScalingStatus, + steamBranchStatus, + isInstalling, + isUninstalling, + install, + uninstall, + } = useInstallation(reload); const [tab, setTab] = useState("Setup"); + const previousRunningAppId = useRef(null); const setupComplete = isInstalled && losslessScalingInstalled && steamBranchStatus?.success === true && steamBranchStatus.installed && !steamBranchStatus.needs_switch; - const previousRunningAppId = useRef(null); useEffect(() => { if (!setupComplete) { @@ -65,10 +64,9 @@ export function Content() { const appid = runningGame?.appid || null; const previous = previousRunningAppId.current; previousRunningAppId.current = appid; - if (appid && appid !== previous) { - setTab("NowPlaying"); - } else if (!appid && previous) { - setTab((currentTab) => currentTab === "NowPlaying" ? "Games" : currentTab); + if (appid && appid !== previous) setTab("NowPlaying"); + else if (!appid && previous) { + setTab((current) => current === "NowPlaying" ? "Games" : current); } }, [runningGame?.appid, runningGame?.configured, setupComplete]); @@ -80,19 +78,9 @@ export function Content() { fieldName: keyof ConfigurationData, value: boolean | number | string | string[], cleanupLaunchOptions = false, - ) => { - await save({ ...config, [fieldName]: value }, cleanupLaunchOptions); - }; - - const onInstall = () => { - void handleInstall(setIsInstalled, setInstallationStatus, reload, checkInstallation); - }; - - const onUninstall = () => { - void handleUninstall(setIsInstalled, setInstallationStatus, checkInstallation); - }; + ) => save({ ...config, [fieldName]: value }, cleanupLaunchOptions); - const setupContent = ( + const setup = ( void install()} + onUninstall={() => void uninstall()} + flatpakRelevant={targets.some((target) => target.transport.kind === "flatpak")} /> ); @@ -115,7 +104,7 @@ export function Content() { handleConfigChange(fieldName, value)} + onConfigChange={(field, value) => handleConfigChange(field, value)} onEnable={enable} onRepair={repair} /> @@ -130,7 +119,7 @@ export function Content() { targets={targets} runningGame={runningGame} onSelect={setSelectedAppId} - onConfigChange={(fieldName, value) => handleConfigChange(fieldName, value, true)} + onConfigChange={(field, value) => handleConfigChange(field, value, true)} onEnable={enable} onEnableAll={enableAll} onRepair={repair} @@ -139,16 +128,10 @@ export function Content() { /> ), }, - { - id: "ConfigFile", - title: tabIcons.configFile, - content: , - }, - { id: "Setup", title: tabIcons.setup, content: setupContent }, + { id: "ConfigFile", title: tabIcons.configFile, content: }, + { id: "Setup", title: tabIcons.setup, content: setup }, ] - : [ - { id: "Setup", title: tabIcons.setup, content: setupContent }, - ]; + : [{ id: "Setup", title: tabIcons.setup, content: setup }]; return (
Date: Thu, 10 Sep 2026 07:57:03 -0400 Subject: fix: keep Flatpak runtime controls simple --- src/components/Content.tsx | 1 - 1 file changed, 1 deletion(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index d92ef86..e5216ca 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -91,7 +91,6 @@ export function Content() { isUninstalling={isUninstalling} onInstall={() => void install()} onUninstall={() => void uninstall()} - flatpakRelevant={targets.some((target) => target.transport.kind === "flatpak")} /> ); -- cgit v1.2.3 From e6efa4f0ff32d520982860180a29472068aab8e2 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Thu, 10 Sep 2026 09:53:55 -0400 Subject: simplify unhooked game behavior on mount --- src/components/Content.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index e5216ca..578f984 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -56,15 +56,15 @@ export function Content() { setTab("Setup"); return; } - setTab((current) => current === "Setup" ? (runningGame ? "NowPlaying" : "Games") : current); - }, [runningGame?.appid, setupComplete]); + setTab((current) => current === "Setup" ? (runningGame?.configured ? "NowPlaying" : "Games") : current); + }, [runningGame?.appid, runningGame?.configured, setupComplete]); useEffect(() => { if (!setupComplete) return; const appid = runningGame?.appid || null; const previous = previousRunningAppId.current; previousRunningAppId.current = appid; - if (appid && appid !== previous) setTab("NowPlaying"); + if (appid && appid !== previous) setTab(runningGame?.configured ? "NowPlaying" : "Games"); else if (!appid && previous) { setTab((current) => current === "NowPlaying" ? "Games" : current); } @@ -96,7 +96,7 @@ export function Content() { const tabs = setupComplete ? [ - ...(runningGame ? [{ + ...(runningGame?.configured ? [{ id: "NowPlaying", title: tabIcons.nowPlaying, content: ( @@ -104,7 +104,6 @@ export function Content() { game={runningGame} config={config} onConfigChange={(field, value) => handleConfigChange(field, value)} - onEnable={enable} onRepair={repair} /> ), -- cgit v1.2.3 From de74f0d2499159ed1cf8f628a166302146ae1f13 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Thu, 10 Sep 2026 12:58:44 -0400 Subject: fix flatpak disable leftovers --- src/components/Content.tsx | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'src/components/Content.tsx') 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: , }; +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(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: }, + ...(showDebugTab ? [{ id: "ConfigFile", title: tabIcons.configFile, content: }] : []), { 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" }} > - +
); } -- cgit v1.2.3 From f83e6cacec84ccc8e17be50d68e228aab996cee5 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 15:46:51 -0400 Subject: refactor: remove now-playing flatpak repair coupling --- src/components/Content.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 43720c0..8f83e85 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -32,9 +32,7 @@ function usePersistentBoolean(key: string, defaultValue: boolean) { useEffect(() => { try { localStorage.setItem(key, String(value)); - } catch { - // Persisting the visibility preference is optional. - } + } catch {} }, [key, value]); return [value, setValue] as const; @@ -132,7 +130,6 @@ export function Content() { game={runningGame} config={config} onConfigChange={(field, value) => handleConfigChange(field, value)} - onRepair={repair} /> ), }] : []), -- cgit v1.2.3 From 64e3a2db3a538b4b538e6b0520943b5a90eff5a5 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:32:58 -0400 Subject: feat: add flatpak profiles tab and now playing --- src/components/Content.tsx | 84 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 25 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 8f83e85..ffb6eef 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,18 +1,22 @@ import { Tabs } from "@decky/ui"; import { useEffect, useRef, useState } from "react"; -import { FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; +import { FaCube, FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; +import { useFlatpakConfiguration } from "../hooks/useFlatpakConfiguration"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallation } from "../hooks/useLsfgHooks"; import { tabStyles } from "../styles"; import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; +import { FlatpakNowPlayingTab } from "./FlatpakNowPlayingTab"; +import { FlatpakTab } from "./FlatpakTab"; import { NowPlayingTab } from "./NowPlayingTab"; import { SetupTab } from "./SetupTab"; const tabIcons = { nowPlaying: , games: , + flatpak: , configFile: , setup: , }; @@ -63,38 +67,46 @@ export function Content() { install, uninstall, } = useInstallation(reload); - const [tab, setTab] = useState("Setup"); - const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, true); - const previousRunningAppId = useRef(null); const setupComplete = isInstalled && losslessScalingInstalled && steamBranchStatus?.success === true && steamBranchStatus.installed && !steamBranchStatus.needs_switch; + const flatpak = useFlatpakConfiguration(setupComplete); + const [tab, setTab] = useState("Setup"); + const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, true); + const previousRunningWorkload = useRef(null); + const runningFlatpak = flatpak.runningApp; + const hasNowPlaying = Boolean(runningGame?.configured || runningFlatpak); + const runningWorkload = runningGame?.configured + ? `steam:${runningGame.appid}` + : runningFlatpak ? `flatpak:${runningFlatpak.app_id}` : null; useEffect(() => { if (!setupComplete) { setTab("Setup"); return; } - setTab((current) => current === "Setup" ? (runningGame?.configured ? "NowPlaying" : "Games") : current); - }, [runningGame?.appid, runningGame?.configured, setupComplete]); + setTab((current) => current === "Setup" ? (hasNowPlaying ? "NowPlaying" : "Games") : current); + }, [hasNowPlaying, setupComplete]); useEffect(() => { if (!setupComplete) return; - const appid = runningGame?.appid || null; - const previous = previousRunningAppId.current; - previousRunningAppId.current = appid; - if (appid && appid !== previous) setTab(runningGame?.configured ? "NowPlaying" : "Games"); - else if (!appid && previous) { + const previous = previousRunningWorkload.current; + previousRunningWorkload.current = runningWorkload; + if (runningWorkload && runningWorkload !== previous) setTab("NowPlaying"); + else if (!runningWorkload && previous) { setTab((current) => current === "NowPlaying" ? "Games" : current); } - }, [runningGame?.appid, runningGame?.configured, setupComplete]); + }, [runningWorkload, setupComplete]); useEffect(() => { - if (isInstalled) void reload(); - }, [isInstalled, reload]); + if (isInstalled) { + void reload(); + void flatpak.reload(); + } + }, [isInstalled, reload, flatpak.reload]); useEffect(() => { if (!showDebugTab && tab === "ConfigFile") setTab("Games"); @@ -120,19 +132,24 @@ export function Content() { /> ); + const nowPlaying = runningGame?.configured ? ( + handleConfigChange(field, value)} + /> + ) : runningFlatpak ? ( + + ) : null; + const tabs = setupComplete ? [ - ...(runningGame?.configured ? [{ - id: "NowPlaying", - title: tabIcons.nowPlaying, - content: ( - handleConfigChange(field, value)} - /> - ), - }] : []), + ...(nowPlaying ? [{ id: "NowPlaying", title: tabIcons.nowPlaying, content: nowPlaying }] : []), { id: "Games", title: tabIcons.games, @@ -153,6 +170,23 @@ export function Content() { /> ), }, + { + id: "Flatpak", + title: tabIcons.flatpak, + content: ( + + ), + }, ...(showDebugTab ? [{ id: "ConfigFile", title: tabIcons.configFile, content: }] : []), { id: "Setup", title: tabIcons.setup, content: setup }, ] -- cgit v1.2.3 From 69f5b6f884064e93f15570d8c7ceda5d610c39dd Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:46:41 -0400 Subject: fix: use running steam config in now playing --- src/components/Content.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index ffb6eef..241ab92 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -45,6 +45,7 @@ function usePersistentBoolean(key: string, defaultValue: boolean) { export function Content() { const { config, + runningConfig, targets, runningGame, setSelectedAppId, @@ -135,8 +136,10 @@ export function Content() { const nowPlaying = runningGame?.configured ? ( handleConfigChange(field, value)} + config={runningConfig} + onConfigChange={async (field, value) => { + await save({ ...runningConfig, [field]: value }, true); + }} /> ) : runningFlatpak ? ( Date: Thu, 10 Sep 2026 16:47:38 -0400 Subject: fix: save now playing steam profile by app id --- src/components/Content.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 241ab92..a300f6f 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -50,6 +50,7 @@ export function Content() { runningGame, setSelectedAppId, save, + saveFor, enable, enableAll, repair, @@ -138,7 +139,7 @@ export function Content() { game={runningGame} config={runningConfig} onConfigChange={async (field, value) => { - await save({ ...runningConfig, [field]: value }, true); + await saveFor(runningGame.appid, { ...runningConfig, [field]: value }, true); }} /> ) : runningFlatpak ? ( -- cgit v1.2.3 From c3778706ab420494479740e19df08c0c3c5b2693 Mon Sep 17 00:00:00 2001 From: Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:48:39 -0400 Subject: fix: preserve config callback return type --- src/components/Content.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index a300f6f..ce3c018 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -118,7 +118,9 @@ export function Content() { fieldName: keyof ConfigurationData, value: boolean | number | string | string[], cleanupLaunchOptions = false, - ) => save({ ...config, [fieldName]: value }, cleanupLaunchOptions); + ) => { + await save({ ...config, [fieldName]: value }, cleanupLaunchOptions); + }; const setup = ( Date: Fri, 11 Sep 2026 09:05:14 -0400 Subject: flatpak correctness, ui alignment, tests --- src/components/Content.tsx | 56 +++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index ce3c018..0d49f89 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,4 +1,4 @@ -import { Tabs } from "@decky/ui"; +import { Field, PanelSection, PanelSectionRow, Tabs } from "@decky/ui"; import { useEffect, useRef, useState } from "react"; import { FaCube, FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; @@ -6,6 +6,7 @@ import { useFlatpakConfiguration } from "../hooks/useFlatpakConfiguration"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallation } from "../hooks/useLsfgHooks"; import { tabStyles } from "../styles"; +import { resolveNowPlayingTarget } from "../utils/nowPlaying"; import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; import { FlatpakNowPlayingTab } from "./FlatpakNowPlayingTab"; @@ -80,10 +81,13 @@ export function Content() { const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, true); const previousRunningWorkload = useRef(null); const runningFlatpak = flatpak.runningApp; - const hasNowPlaying = Boolean(runningGame?.configured || runningFlatpak); - const runningWorkload = runningGame?.configured - ? `steam:${runningGame.appid}` - : runningFlatpak ? `flatpak:${runningFlatpak.app_id}` : null; + const nowPlayingTarget = resolveNowPlayingTarget(runningGame, runningFlatpak); + const hasNowPlaying = Boolean(nowPlayingTarget); + const runningWorkload = nowPlayingTarget + ? nowPlayingTarget.kind === "flatpak" + ? `flatpak:${nowPlayingTarget.app.app_id}` + : `steam:${nowPlayingTarget.game.appid}` + : null; useEffect(() => { if (!setupComplete) { @@ -136,26 +140,27 @@ export function Content() { /> ); - const nowPlaying = runningGame?.configured ? ( + const nowPlaying = nowPlayingTarget?.kind === "steam" ? ( { - await saveFor(runningGame.appid, { ...runningConfig, [field]: value }, true); + await saveFor(nowPlayingTarget.game.appid, { ...runningConfig, [field]: value }, true); }} /> - ) : runningFlatpak ? ( + ) : nowPlayingTarget?.kind === "flatpak" ? ( - ) : null; + ) : ( + + ); const tabs = setupComplete ? [ - ...(nowPlaying ? [{ id: "NowPlaying", title: tabIcons.nowPlaying, content: nowPlaying }] : []), + { id: "NowPlaying", title: tabIcons.nowPlaying, content: nowPlaying }, { id: "Games", title: tabIcons.games, @@ -198,13 +203,34 @@ export function Content() { ] : [{ id: "Setup", title: tabIcons.setup, content: setup }]; + const availableTabIds = new Set(tabs.map(({ id }) => id)); + const activeTab = availableTabIds.has(tab) ? tab : setupComplete ? "Games" : "Setup"; + return (
- + { + if (availableTabIds.has(nextTab)) setTab(nextTab); + }} + tabs={tabs} + /> +
+ ); +} + +function NowPlayingTabPlaceholder() { + return ( +
+ + + + +
); } -- cgit v1.2.3 From 18af8d1de5c7d12af674018ce819cc91480cf51a Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 09:09:29 -0400 Subject: fix tab change jutter --- src/components/Content.tsx | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 0d49f89..dc8c54f 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,5 +1,5 @@ -import { Field, PanelSection, PanelSectionRow, Tabs } from "@decky/ui"; -import { useEffect, useRef, useState } from "react"; +import { Tabs } from "@decky/ui"; +import { useEffect, useRef, useState, type FocusEvent } from "react"; import { FaCube, FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; import { useFlatpakConfiguration } from "../hooks/useFlatpakConfiguration"; @@ -79,6 +79,7 @@ export function Content() { const flatpak = useFlatpakConfiguration(setupComplete); const [tab, setTab] = useState("Setup"); const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, true); + const [contentFocused, setContentFocused] = useState(false); const previousRunningWorkload = useRef(null); const runningFlatpak = flatpak.runningApp; const nowPlayingTarget = resolveNowPlayingTarget(runningGame, runningFlatpak); @@ -154,13 +155,11 @@ export function Content() { launcher={nowPlayingTarget.launcher} onConfigChange={flatpak.updateConfig} /> - ) : ( - - ); + ) : null; const tabs = setupComplete ? [ - { id: "NowPlaying", title: tabIcons.nowPlaying, content: nowPlaying }, + ...(nowPlaying ? [{ id: "NowPlaying", title: tabIcons.nowPlaying, content: nowPlaying }] : []), { id: "Games", title: tabIcons.games, @@ -205,11 +204,16 @@ export function Content() { const availableTabIds = new Set(tabs.map(({ id }) => id)); const activeTab = availableTabIds.has(tab) ? tab : setupComplete ? "Games" : "Setup"; + const handleFocusCapture = (event: FocusEvent) => { + const focusedElement = event.target as HTMLElement | null; + setContentFocused(!focusedElement?.closest?.('[role="tab"]')); + }; return (
); } - -function NowPlayingTabPlaceholder() { - return ( -
- - - - - -
- ); -} -- cgit v1.2.3 From a8388009b2ba141caafe7f2e35f7029d93e7811f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 09:38:35 -0400 Subject: handle edge cases on flatpaks and ordering --- src/components/Content.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index dc8c54f..422eec8 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -86,7 +86,7 @@ export function Content() { const hasNowPlaying = Boolean(nowPlayingTarget); const runningWorkload = nowPlayingTarget ? nowPlayingTarget.kind === "flatpak" - ? `flatpak:${nowPlayingTarget.app.app_id}` + ? `flatpak:${nowPlayingTarget.app.app_id}:${nowPlayingTarget.launcher?.appid ?? ""}` : `steam:${nowPlayingTarget.game.appid}` : null; -- cgit v1.2.3 From bc2669b6050da7acdf9e1dda2b77444734852559 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 10:00:19 -0400 Subject: workaround for new client update, ui cuts off in game --- src/components/Content.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 422eec8..32e9bf8 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,5 +1,5 @@ import { Tabs } from "@decky/ui"; -import { useEffect, useRef, useState, type FocusEvent } from "react"; +import { useEffect, useRef, useState, type FocusEvent, type ReactNode } from "react"; import { FaCube, FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; import { useFlatpakConfiguration } from "../hooks/useFlatpakConfiguration"; @@ -141,6 +141,10 @@ export function Content() { /> ); + const tabContent = (content: ReactNode) => ( +
{content}
+ ); + const nowPlaying = nowPlayingTarget?.kind === "steam" ? ( + />, ), }, { id: "Flatpak", title: tabIcons.flatpak, - content: ( + content: tabContent( + />, ), }, - ...(showDebugTab ? [{ id: "ConfigFile", title: tabIcons.configFile, content: }] : []), - { id: "Setup", title: tabIcons.setup, content: setup }, + ...(showDebugTab ? [{ id: "ConfigFile", title: tabIcons.configFile, content: tabContent() }] : []), + { id: "Setup", title: tabIcons.setup, content: tabContent(setup) }, ] - : [{ id: "Setup", title: tabIcons.setup, content: setup }]; + : [{ id: "Setup", title: tabIcons.setup, content: tabContent(setup) }]; const availableTabIds = new Set(tabs.map(({ id }) => id)); const activeTab = availableTabIds.has(tab) ? tab : setupComplete ? "Games" : "Setup"; -- cgit v1.2.3 From 954b2abc47d8772211cb8ecee523900f823184e8 Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 11:08:12 -0400 Subject: better uninstall cleanup --- src/components/Content.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 32e9bf8..91775df 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -47,16 +47,19 @@ export function Content() { const { config, runningConfig, + globalConfig, targets, runningGame, setSelectedAppId, save, saveFor, + updateGlobal, enable, enableAll, repair, resetSelected, resetAll, + cleanupAllWorkarounds, reload, } = useGameConfiguration(); const { @@ -69,7 +72,7 @@ export function Content() { isUninstalling, install, uninstall, - } = useInstallation(reload); + } = useInstallation(reload, cleanupAllWorkarounds); const setupComplete = isInstalled && losslessScalingInstalled && @@ -78,7 +81,7 @@ export function Content() { !steamBranchStatus.needs_switch; const flatpak = useFlatpakConfiguration(setupComplete); const [tab, setTab] = useState("Setup"); - const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, true); + const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, false); const [contentFocused, setContentFocused] = useState(false); const previousRunningWorkload = useRef(null); const runningFlatpak = flatpak.runningApp; @@ -136,6 +139,10 @@ export function Content() { steamBranchStatus={steamBranchStatus} isInstalling={isInstalling} isUninstalling={isUninstalling} + globalConfig={globalConfig} + showDebugTab={showDebugTab} + onGlobalConfigChange={updateGlobal} + onShowDebugTabChange={setShowDebugTab} onInstall={() => void install()} onUninstall={() => void uninstall()} /> @@ -172,8 +179,6 @@ export function Content() { config={config} targets={targets} runningGame={runningGame} - showDebugTab={showDebugTab} - onShowDebugTabChange={setShowDebugTab} onSelect={setSelectedAppId} onConfigChange={(field, value) => handleConfigChange(field, value, true)} onEnable={enable} -- cgit v1.2.3 From 4d14bc1086a1ba05acd01d7466749ff3cffba75f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 12:58:16 -0400 Subject: enable all and disable all for flatpak --- src/components/Content.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 91775df..3f25320 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -200,7 +200,9 @@ export function Content() { busyAppId={flatpak.busyAppId} onRefresh={flatpak.reload} onEnable={flatpak.enableApp} + onEnableAll={flatpak.enableAll} onRemove={flatpak.removeApp} + onRemoveAll={flatpak.removeAll} onConfigChange={flatpak.updateConfig} onWorkaroundChange={flatpak.updateWorkarounds} />, -- cgit v1.2.3 From f3074fbe1427411dc3b5597d2e87918383299e3f Mon Sep 17 00:00:00 2001 From: xXJSONDeruloXx Date: Fri, 11 Sep 2026 13:47:52 -0400 Subject: feat: non steam tab, faster bulk actions --- src/components/Content.tsx | 98 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 29 deletions(-) (limited to 'src/components/Content.tsx') diff --git a/src/components/Content.tsx b/src/components/Content.tsx index 3f25320..470db95 100644 --- a/src/components/Content.tsx +++ b/src/components/Content.tsx @@ -1,28 +1,37 @@ import { Tabs } from "@decky/ui"; import { useEffect, useRef, useState, type FocusEvent, type ReactNode } from "react"; -import { FaCube, FaFileAlt, FaGamepad, FaList, FaTools } from "react-icons/fa"; +import { FaCube, FaExternalLinkAlt, FaFileAlt, FaGamepad, FaSteam, FaTools } from "react-icons/fa"; import { ConfigurationData } from "../config/configSchema"; import { useFlatpakConfiguration } from "../hooks/useFlatpakConfiguration"; import { useGameConfiguration } from "../hooks/useGameConfiguration"; import { useInstallation } from "../hooks/useLsfgHooks"; import { tabStyles } from "../styles"; -import { resolveNowPlayingTarget } from "../utils/nowPlaying"; +import { targetsForSource } from "../utils/gameTargets"; +import { resolveNowPlayingTarget, type NowPlayingTarget } from "../utils/nowPlaying"; import { ConfigFileTab } from "./ConfigFileTab"; import { ConfigurationTab } from "./ConfigurationTab"; import { FlatpakNowPlayingTab } from "./FlatpakNowPlayingTab"; import { FlatpakTab } from "./FlatpakTab"; import { NowPlayingTab } from "./NowPlayingTab"; -import { SetupTab } from "./SetupTab"; +import { SettingsTab } from "./SettingsTab"; const tabIcons = { nowPlaying: , - games: , + steam: , + nonSteam: , flatpak: , configFile: , - setup: , + settings: , }; const DEBUG_TAB_VISIBILITY_KEY = "lsfg-debug-tab-visible-v1"; +type GameTabId = "Steam" | "NonSteam" | "Flatpak"; + +function tabForNowPlaying(target: NowPlayingTarget | null): GameTabId { + if (!target) return "Steam"; + if (target.kind === "flatpak") return target.launcher?.source === "nonSteam" ? "NonSteam" : "Flatpak"; + return target.game.source === "nonSteam" ? "NonSteam" : "Steam"; +} function usePersistentBoolean(key: string, defaultValue: boolean) { const [value, setValue] = useState(() => { @@ -56,6 +65,7 @@ export function Content() { updateGlobal, enable, enableAll, + bulkOperationBusy, repair, resetSelected, resetAll, @@ -80,34 +90,40 @@ export function Content() { steamBranchStatus.installed && !steamBranchStatus.needs_switch; const flatpak = useFlatpakConfiguration(setupComplete); - const [tab, setTab] = useState("Setup"); + const [tab, setTab] = useState("Settings"); const [showDebugTab, setShowDebugTab] = usePersistentBoolean(DEBUG_TAB_VISIBILITY_KEY, false); const [contentFocused, setContentFocused] = useState(false); const previousRunningWorkload = useRef(null); + const previousNowPlayingTab = useRef("Steam"); const runningFlatpak = flatpak.runningApp; const nowPlayingTarget = resolveNowPlayingTarget(runningGame, runningFlatpak); const hasNowPlaying = Boolean(nowPlayingTarget); const runningWorkload = nowPlayingTarget ? nowPlayingTarget.kind === "flatpak" ? `flatpak:${nowPlayingTarget.app.app_id}:${nowPlayingTarget.launcher?.appid ?? ""}` - : `steam:${nowPlayingTarget.game.appid}` + : `${nowPlayingTarget.game.source}:${nowPlayingTarget.game.appid}` : null; + const steamTargets = targetsForSource(targets, "steam"); + const nonSteamTargets = targetsForSource(targets, "nonSteam"); useEffect(() => { if (!setupComplete) { - setTab("Setup"); + setTab("Settings"); return; } - setTab((current) => current === "Setup" ? (hasNowPlaying ? "NowPlaying" : "Games") : current); + setTab((current) => current === "Settings" ? (hasNowPlaying ? "NowPlaying" : "Steam") : current); }, [hasNowPlaying, setupComplete]); useEffect(() => { if (!setupComplete) return; const previous = previousRunningWorkload.current; previousRunningWorkload.current = runningWorkload; - if (runningWorkload && runningWorkload !== previous) setTab("NowPlaying"); + if (runningWorkload && runningWorkload !== previous) { + previousNowPlayingTab.current = tabForNowPlaying(nowPlayingTarget); + setTab("NowPlaying"); + } else if (!runningWorkload && previous) { - setTab((current) => current === "NowPlaying" ? "Games" : current); + setTab((current) => current === "NowPlaying" ? previousNowPlayingTab.current : current); } }, [runningWorkload, setupComplete]); @@ -119,8 +135,8 @@ export function Content() { }, [isInstalled, reload, flatpak.reload]); useEffect(() => { - if (!showDebugTab && tab === "ConfigFile") setTab("Games"); - }, [showDebugTab, tab]); + if (!showDebugTab && tab === "ConfigFile") setTab(setupComplete ? "Steam" : "Settings"); + }, [setupComplete, showDebugTab, tab]); const handleConfigChange = async ( fieldName: keyof ConfigurationData, @@ -130,8 +146,8 @@ export function Content() { await save({ ...config, [fieldName]: value }, cleanupLaunchOptions); }; - const setup = ( - {content}
); - const nowPlaying = nowPlayingTarget?.kind === "steam" ? ( + const nowPlaying = nowPlayingTarget?.kind === "flatpak" ? ( + + ) : nowPlayingTarget ? ( - ) : nowPlayingTarget?.kind === "flatpak" ? ( - ) : null; const tabs = setupComplete ? [ ...(nowPlaying ? [{ id: "NowPlaying", title: tabIcons.nowPlaying, content: tabContent(nowPlaying) }] : []), { - id: "Games", - title: tabIcons.games, + id: "Steam", + title: tabIcons.steam, content: tabContent( handleConfigChange(field, value, true)} + onConfigChange={handleConfigChange} onEnable={enable} onEnableAll={enableAll} + bulkOperationBusy={bulkOperationBusy} onRepair={repair} onReset={resetSelected} onResetAll={resetAll} />, ), }, + { + id: "NonSteam", + title: tabIcons.nonSteam, + content: tabContent( + + ), + }, { id: "Flatpak", title: tabIcons.flatpak, @@ -209,12 +249,12 @@ export function Content() { ), }, ...(showDebugTab ? [{ id: "ConfigFile", title: tabIcons.configFile, content: tabContent() }] : []), - { id: "Setup", title: tabIcons.setup, content: tabContent(setup) }, + { id: "Settings", title: tabIcons.settings, content: tabContent(settings) }, ] - : [{ id: "Setup", title: tabIcons.setup, content: tabContent(setup) }]; + : [{ id: "Settings", title: tabIcons.settings, content: tabContent(settings) }]; const availableTabIds = new Set(tabs.map(({ id }) => id)); - const activeTab = availableTabIds.has(tab) ? tab : setupComplete ? "Games" : "Setup"; + const activeTab = availableTabIds.has(tab) ? tab : setupComplete ? "Steam" : "Settings"; const handleFocusCapture = (event: FocusEvent) => { const focusedElement = event.target as HTMLElement | null; setContentFocused(!focusedElement?.closest?.('[role="tab"]')); -- cgit v1.2.3