diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/ConfigurationSection.tsx | 162 | ||||
| -rw-r--r-- | src/components/Content.tsx | 16 | ||||
| -rw-r--r-- | 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 ( <> <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> + </> + )} + </> ); } |
