summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/ConfigurationSection.tsx162
-rw-r--r--src/components/Content.tsx16
-rw-r--r--src/components/ProfileManagement.tsx166
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 (
<>
<style>
@@ -49,6 +69,12 @@ export function ConfigurationSection({
.LSFG_WorkaroundsCollapseButton_Container > div > div > div > div > button {
height: 10px !important;
}
+ .LSFG_ConfigCollapseButton_Container > div > div > div > button {
+ height: 10px !important;
+ }
+ .LSFG_ConfigCollapseButton_Container > div > div > div > div > button {
+ height: 10px !important;
+ }
`}
</style>
@@ -68,69 +94,93 @@ export function ConfigurationSection({
</div>
</PanelSectionRow>
- {/* FPS Multiplier */}
- <FpsMultiplierControl config={config} onConfigChange={onConfigChange} />
-
<PanelSectionRow>
- <SliderField
- label={`Flow Scale (${Math.round(config.flow_scale * 100)}%)`}
- description="Lowers internal motion estimation resolution, improving performance slightly"
- value={config.flow_scale}
- min={0.25}
- max={1.0}
- step={0.01}
- onChange={(value) => onConfigChange(FLOW_SCALE, value)}
- />
+ <div className="LSFG_ConfigCollapseButton_Container">
+ <ButtonItem
+ layout="below"
+ bottomSeparator={configCollapsed ? "standard" : "none"}
+ onClick={() => setConfigCollapsed(!configCollapsed)}
+ >
+ {configCollapsed ? (
+ <RiArrowDownSFill
+ style={{ transform: "translate(0, -13px)", fontSize: "1.5em" }}
+ />
+ ) : (
+ <RiArrowUpSFill
+ style={{ transform: "translate(0, -12px)", fontSize: "1.5em" }}
+ />
+ )}
+ </ButtonItem>
+ </div>
</PanelSectionRow>
- <PanelSectionRow>
- <SliderField
- label={`Base FPS Cap${config.dxvk_frame_rate > 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)}
- />
- </PanelSectionRow>
+ {!configCollapsed && (
+ <>
+ {/* FPS Multiplier */}
+ <FpsMultiplierControl config={config} onConfigChange={onConfigChange} />
- <PanelSectionRow>
- <ToggleField
- label={`Present Mode (${(config.experimental_present_mode || "fifo") === "fifo" ? "FIFO - VSync" : "Mailbox"})`}
- description="Toggle between FIFO - VSync (default) and Mailbox presentation modes for better performance or compatibility"
- checked={(config.experimental_present_mode || "fifo") === "fifo"}
- onChange={(value) => onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")}
- />
- </PanelSectionRow>
+ <PanelSectionRow>
+ <SliderField
+ label={`Flow Scale (${Math.round(config.flow_scale * 100)}%)`}
+ description="Lowers internal motion estimation resolution, improving performance slightly"
+ value={config.flow_scale}
+ min={0.25}
+ max={1.0}
+ step={0.01}
+ onChange={(value) => onConfigChange(FLOW_SCALE, value)}
+ />
+ </PanelSectionRow>
- <PanelSectionRow>
- <ToggleField
- label="Performance Mode"
- description="Uses a lighter model for FG (Recommended for most games)"
- checked={config.performance_mode}
- onChange={(value) => onConfigChange(PERFORMANCE_MODE, value)}
- />
- </PanelSectionRow>
+ <PanelSectionRow>
+ <SliderField
+ label={`Base FPS Cap${config.dxvk_frame_rate > 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)}
+ />
+ </PanelSectionRow>
- {/* <PanelSectionRow>
- <ToggleField
- label="Force Disable FP16"
- description="Force-disable FP16 acceleration"
- checked={config.no_fp16}
- onChange={(value) => onConfigChange(NO_FP16, value)}
- />
- </PanelSectionRow> */}
+ <PanelSectionRow>
+ <ToggleField
+ label={`Present Mode (${(config.experimental_present_mode || "fifo") === "fifo" ? "FIFO - VSync" : "Mailbox"})`}
+ description="Toggle between FIFO - VSync (default) and Mailbox presentation modes for better performance or compatibility"
+ checked={(config.experimental_present_mode || "fifo") === "fifo"}
+ onChange={(value) => onConfigChange(EXPERIMENTAL_PRESENT_MODE, value ? "fifo" : "mailbox")}
+ />
+ </PanelSectionRow>
- <PanelSectionRow>
- <ToggleField
- label="HDR Mode"
- description={config.enable_wsi ? "Enables HDR mode (only for games that support HDR)" : "Enable WSI in the workarounds menu to unlock HDR toggle"}
- checked={config.hdr_mode}
- disabled={!config.enable_wsi}
- onChange={(value) => onConfigChange(HDR_MODE, value)}
- />
- </PanelSectionRow>
+ <PanelSectionRow>
+ <ToggleField
+ label="Performance Mode"
+ description="Uses a lighter model for FG (Recommended for most games)"
+ checked={config.performance_mode}
+ onChange={(value) => onConfigChange(PERFORMANCE_MODE, value)}
+ />
+ </PanelSectionRow>
+
+ {/* <PanelSectionRow>
+ <ToggleField
+ label="Force Disable FP16"
+ description="Force-disable FP16 acceleration"
+ checked={config.no_fp16}
+ onChange={(value) => onConfigChange(NO_FP16, value)}
+ />
+ </PanelSectionRow> */}
+
+ <PanelSectionRow>
+ <ToggleField
+ label="HDR Mode"
+ description={config.enable_wsi ? "Enables HDR mode (only for games that support HDR)" : "Enable WSI in the workarounds menu to unlock HDR toggle"}
+ checked={config.hdr_mode}
+ disabled={!config.enable_wsi}
+ onChange={(value) => onConfigChange(HDR_MODE, value)}
+ />
+ </PanelSectionRow>
+ </>
+ )}
{/* Workarounds Section */}
<PanelSectionRow>
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() {
<SmartClipboardButton />
+ {/* Configuration Section - only show if installed */}
+ {isInstalled && (
+ <ConfigurationSection
+ config={config}
+ onConfigChange={handleConfigChange}
+ />
+ )}
+
{/* Profile Management - only show if installed */}
{isInstalled && (
<ProfileManagement
@@ -104,14 +112,6 @@ export function Content() {
/>
)}
- {/* Configuration Section - only show if installed */}
- {isInstalled && (
- <ConfigurationSection
- config={config}
- onConfigChange={handleConfigChange}
- />
- )}
-
<UsageInstructions config={config} />
<WikiButton />
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<string>(currentProfile || "decky-lsfg-vk");
const [isLoading, setIsLoading] = useState(false);
const [mainRunningApp, setMainRunningApp] = useState<AppOverview | undefined>(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 (
- <PanelSection title="Select Profile">
- {/* Display currently running game info */}
- {mainRunningApp && (
- <PanelSectionRow>
- <div style={{
- padding: "8px 12px",
- backgroundColor: "rgba(0, 255, 0, 0.1)",
- borderRadius: "4px",
- border: "1px solid rgba(0, 255, 0, 0.3)",
- fontSize: "13px"
- }}>
- <strong>{mainRunningApp.display_name}</strong> running. Close game to change profile.
- </div>
- </PanelSectionRow>
- )}
-
- <PanelSectionRow>
- <Field
- label=""
- childrenLayout="below"
- childrenContainerWidth="max"
- >
- <Dropdown
- rgOptions={profileOptions}
- selectedOption={selectedProfile}
- onChange={handleDropdownChange}
- disabled={isLoading || !!mainRunningApp}
- />
- </Field>
- </PanelSectionRow>
-
+ <>
+ <style>
+ {`
+ .LSFG_ProfilesCollapseButton_Container > div > div > div > button {
+ height: 10px !important;
+ }
+ .LSFG_ProfilesCollapseButton_Container > div > div > div > div > button {
+ height: 10px !important;
+ }
+ `}
+ </style>
+
<PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={handleRenameProfile}
- disabled={isLoading || selectedProfile === "decky-lsfg-vk" || !!mainRunningApp}
+ <div
+ style={{
+ fontSize: "14px",
+ fontWeight: "bold",
+ marginTop: "16px",
+ marginBottom: "8px",
+ borderBottom: "1px solid rgba(255, 255, 255, 0.2)",
+ paddingBottom: "4px",
+ color: "white"
+ }}
>
- Rename
- </ButtonItem>
+ Select Profile
+ </div>
</PanelSectionRow>
-
+
<PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={handleDeleteProfile}
- disabled={isLoading || selectedProfile === "decky-lsfg-vk" || !!mainRunningApp}
- >
- Delete
- </ButtonItem>
+ <div className="LSFG_ProfilesCollapseButton_Container">
+ <ButtonItem
+ layout="below"
+ bottomSeparator={profilesCollapsed ? "standard" : "none"}
+ onClick={() => setProfilesCollapsed(!profilesCollapsed)}
+ >
+ {profilesCollapsed ? (
+ <RiArrowDownSFill
+ style={{ transform: "translate(0, -13px)", fontSize: "1.5em" }}
+ />
+ ) : (
+ <RiArrowUpSFill
+ style={{ transform: "translate(0, -12px)", fontSize: "1.5em" }}
+ />
+ )}
+ </ButtonItem>
+ </div>
</PanelSectionRow>
- </PanelSection>
+
+ {!profilesCollapsed && (
+ <>
+ {/* Display currently running game info */}
+ {mainRunningApp && (
+ <PanelSectionRow>
+ <div style={{
+ padding: "8px 12px",
+ backgroundColor: "rgba(0, 255, 0, 0.1)",
+ borderRadius: "4px",
+ border: "1px solid rgba(0, 255, 0, 0.3)",
+ fontSize: "13px"
+ }}>
+ <strong>{mainRunningApp.display_name}</strong> running. Close game to change profile.
+ </div>
+ </PanelSectionRow>
+ )}
+
+ <PanelSectionRow>
+ <Field
+ label=""
+ childrenLayout="below"
+ childrenContainerWidth="max"
+ >
+ <Dropdown
+ rgOptions={profileOptions}
+ selectedOption={selectedProfile}
+ onChange={handleDropdownChange}
+ disabled={isLoading || !!mainRunningApp}
+ />
+ </Field>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <ButtonItem
+ layout="below"
+ onClick={handleRenameProfile}
+ disabled={isLoading || selectedProfile === "decky-lsfg-vk" || !!mainRunningApp}
+ >
+ Rename
+ </ButtonItem>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <ButtonItem
+ layout="below"
+ onClick={handleDeleteProfile}
+ disabled={isLoading || selectedProfile === "decky-lsfg-vk" || !!mainRunningApp}
+ >
+ Delete
+ </ButtonItem>
+ </PanelSectionRow>
+ </>
+ )}
+ </>
);
}