summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorKurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>2025-07-18 16:01:47 -0400
committerGitHub <noreply@github.com>2025-07-18 16:01:47 -0400
commit1d296606babfb0ceb02068e852582ade7adc4d98 (patch)
treeb29330427adf1b27487a9d2a4b59942a71f585e3 /src/components
parentfa328306e3393a787d7c4f5855ecc23177eaa480 (diff)
parent3b60286fc360704eb6faeb72edbec602c624bd51 (diff)
downloaddecky-lsfg-vk-1d296606babfb0ceb02068e852582ade7adc4d98.tar.gz
decky-lsfg-vk-1d296606babfb0ceb02068e852582ade7adc4d98.zip
Merge pull request #34 from xXJSONDeruloXx/conf-per-game
implement conf file and real time changes
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ConfigurationSection.tsx103
-rw-r--r--src/components/Content.tsx2
-rw-r--r--src/components/LaunchOptionInfo.tsx25
-rw-r--r--src/components/PluginUpdateChecker.tsx121
-rw-r--r--src/components/UsageInstructions.tsx151
-rw-r--r--src/components/index.ts3
6 files changed, 223 insertions, 182 deletions
diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx
index 2545217..dc8da89 100644
--- a/src/components/ConfigurationSection.tsx
+++ b/src/components/ConfigurationSection.tsx
@@ -1,9 +1,9 @@
-import { PanelSectionRow, ToggleField, SliderField } from "@decky/ui";
+import { PanelSectionRow, ToggleField, SliderField, DropdownItem } from "@decky/ui";
import { ConfigurationData } from "../config/configSchema";
interface ConfigurationSectionProps {
config: ConfigurationData;
- onConfigChange: (fieldName: keyof ConfigurationData, value: boolean | number) => Promise<void>;
+ onConfigChange: (fieldName: keyof ConfigurationData, value: boolean | number | string) => Promise<void>;
}
export function ConfigurationSection({
@@ -27,28 +27,38 @@ export function ConfigurationSection({
</div>
</PanelSectionRow>
- <PanelSectionRow>
+ {/* <PanelSectionRow>
<ToggleField
label="Enable LSFG"
- description="Enables the frame generation layer"
- checked={config.enable_lsfg}
- onChange={(value) => onConfigChange('enable_lsfg', value)}
+ description="Enables lsfg globally (apply before launching games)"
+ checked={config.enable}
+ onChange={(value) => onConfigChange('enable', value)}
/>
- </PanelSectionRow>
+ </PanelSectionRow> */}
+
+ {/* <PanelSectionRow>
+ <TextField
+ label="Lossless.dll Path"
+ description="specify where Lossless.dll is stored"
+ value={config.dll}
+ onChange={(e) => onConfigChange('dll', e.target.value)}
+ />
+ </PanelSectionRow> */}
<PanelSectionRow>
<SliderField
label="FPS Multiplier"
description="Traditional FPS multiplier value"
value={config.multiplier}
- min={2}
+ min={1}
max={4}
step={1}
- notchCount={3}
+ notchCount={4}
notchLabels={[
- { notchIndex: 0, label: "2X" },
- { notchIndex: 1, label: "3X" },
- { notchIndex: 2, label: "4X" }
+ { notchIndex: 0, label: "OFF" },
+ { notchIndex: 1, label: "2X" },
+ { notchIndex: 2, label: "3X" },
+ { notchIndex: 3, label: "4X" }
]}
onChange={(value) => onConfigChange('multiplier', value)}
/>
@@ -57,7 +67,7 @@ export function ConfigurationSection({
<PanelSectionRow>
<SliderField
label={`Flow Scale ${Math.round(config.flow_scale * 100)}%`}
- description="Lowers the internal motion estimation resolution"
+ description="Lowers internal motion estimation resolution, improving performance slightly"
value={config.flow_scale}
min={0.25}
max={1.0}
@@ -68,51 +78,66 @@ export function ConfigurationSection({
<PanelSectionRow>
<ToggleField
- label="HDR Mode"
- description="Enable HDR mode (only if Game supports HDR)"
- checked={config.hdr}
- onChange={(value) => onConfigChange('hdr', value)}
+ 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="Performance Mode"
- description="Use lighter model for FG"
- checked={config.perf_mode}
- onChange={(value) => onConfigChange('perf_mode', value)}
+ label="HDR Mode"
+ description="Enables HDR mode (only for games that support HDR)"
+ checked={config.hdr_mode}
+ onChange={(value) => onConfigChange('hdr_mode', value)}
/>
</PanelSectionRow>
<PanelSectionRow>
- <ToggleField
- label="Immediate Mode"
- description="Reduce input lag (Experimental, will cause issues in many games)"
- checked={config.immediate_mode}
- onChange={(value) => onConfigChange('immediate_mode', value)}
+ <div
+ style={{
+ fontSize: "14px",
+ fontWeight: "bold",
+ marginTop: "16px",
+ marginBottom: "8px",
+ borderBottom: "1px solid rgba(255, 255, 255, 0.2)",
+ paddingBottom: "4px",
+ color: "white"
+ }}
+ >
+ Experimental Features
+ </div>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <DropdownItem
+ label="Override Vulkan present mode"
+ description="Select a specific Vulkan presentation mode for better performance or compatibility (may cause crashes)"
+ menuLabel="Select presentation mode"
+ selectedOption={config.experimental_present_mode}
+ onChange={(value) => onConfigChange('experimental_present_mode', value.data)}
+ rgOptions={[
+ { data: "", label: "Default" },
+ { data: "fifo", label: "FIFO" },
+ { data: "vsync", label: "VSync" },
+ { data: "mailbox", label: "Mailbox" },
+ { data: "immediate", label: "Immediate" }
+ ]}
/>
</PanelSectionRow>
<PanelSectionRow>
<SliderField
- label={`Game Frame Cap ${config.frame_cap === 0 ? "(Disabled)" : `(${config.frame_cap} FPS)`}`}
- description="Limit base game FPS (0 = disabled)"
- value={config.frame_cap}
+ label={`FPS Limit${config.experimental_fps_limit > 0 ? ` (${config.experimental_fps_limit} FPS)` : ' (Off)'}`}
+ description="Base framerate cap for DirectX games, before frame multiplier (requires game re-launch)"
+ value={config.experimental_fps_limit}
min={0}
max={60}
step={1}
- onChange={(value) => onConfigChange('frame_cap', value)}
+ onChange={(value) => onConfigChange('experimental_fps_limit', value)}
/>
</PanelSectionRow>
-
- {/* <PanelSectionRow>
- <ToggleField
- label="Disable vkbasalt"
- description="Some plugins add vkbasalt layer, which can break lsfg. Toggling on fixes this"
- checked={config.disable_vkbasalt}
- onChange={(value) => onConfigChange('disable_vkbasalt', value)}
- />
- </PanelSectionRow> */}
</>
);
}
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
index ea3f3c1..613e722 100644
--- a/src/components/Content.tsx
+++ b/src/components/Content.tsx
@@ -37,7 +37,7 @@ export function Content() {
}, [isInstalled, loadLsfgConfig]);
// Generic configuration change handler
- const handleConfigChange = async (fieldName: keyof ConfigurationData, value: boolean | number) => {
+ const handleConfigChange = async (fieldName: keyof ConfigurationData, value: boolean | number | string) => {
await updateField(fieldName, value);
};
diff --git a/src/components/LaunchOptionInfo.tsx b/src/components/LaunchOptionInfo.tsx
new file mode 100644
index 0000000..298c45a
--- /dev/null
+++ b/src/components/LaunchOptionInfo.tsx
@@ -0,0 +1,25 @@
+import { PanelSectionRow, Field } from "@decky/ui";
+
+export function LaunchOptionInfo() {
+ return (
+ <PanelSectionRow>
+ <Field
+ bottomSeparator="none"
+ label="Setup Instructions"
+ description={
+ <>
+ <div>For each game where you want to use lsfg-vk:</div>
+ <div style={{ marginTop: "8px" }}>
+ 1. Right-click the game in Steam → Properties<br/>
+ 2. Add this to Launch Options: <code>LSFG_PROCESS=decky-lsfg-vk %command%</code><br/>
+ 3. Or use the "Copy Launch Option" button above
+ </div>
+ <div style={{ marginTop: "8px", fontStyle: "italic" }}>
+ This temporary solution allows hot-reloading while keeping you on the latest lsfg-vk version.
+ </div>
+ </>
+ }
+ />
+ </PanelSectionRow>
+ );
+}
diff --git a/src/components/PluginUpdateChecker.tsx b/src/components/PluginUpdateChecker.tsx
index 0028a79..d427c18 100644
--- a/src/components/PluginUpdateChecker.tsx
+++ b/src/components/PluginUpdateChecker.tsx
@@ -1,7 +1,10 @@
import React, { useState, useEffect } from 'react';
import {
ButtonItem,
- PanelSection
+ PanelSection,
+ PanelSectionRow,
+ Field,
+ Focusable
} from '@decky/ui';
import { checkForPluginUpdate, downloadPluginUpdate, UpdateCheckResult, UpdateDownloadResult } from '../api/lsfgApi';
@@ -99,88 +102,74 @@ export const PluginUpdateChecker: React.FC<PluginUpdateCheckerProps> = () => {
if (updateInfo.updateAvailable) {
if (downloadResult?.success) {
- return (
- <div style={{ color: 'lightgreen', marginTop: '5px' }}>
- ✓ v{updateInfo.latestVersion} downloaded - ready to install
- </div>
- );
+ return "✓ v" + updateInfo.latestVersion + " downloaded - ready to install";
} else {
- return (
- <div style={{ color: 'orange', marginTop: '5px' }}>
- Update available: v{updateInfo.latestVersion}
- </div>
- );
+ return "Update available: v" + updateInfo.latestVersion;
}
} else {
- return (
- <div style={{ color: 'lightgreen', marginTop: '5px' }}>
- Up to date (v{updateInfo.currentVersion})
- </div>
- );
+ return "Up to date (v" + updateInfo.currentVersion + ")";
}
};
return (
- <PanelSection title="Plugin Updates">
- <ButtonItem
- layout="below"
- onClick={handleCheckForUpdate}
- disabled={checkingUpdate}
- description={getStatusMessage()}
- >
- {checkingUpdate ? 'Checking for updates...' : 'Check for Updates'}
- </ButtonItem>
-
- {updateInfo && updateInfo.updateAvailable && !downloadResult?.success && (
+ <PanelSection title="PLUGIN UPDATES">
+ <PanelSectionRow>
<ButtonItem
layout="below"
- onClick={handleDownloadUpdate}
- disabled={downloadingUpdate}
- description={`Download version ${updateInfo.latestVersion}`}
+ onClick={handleCheckForUpdate}
+ disabled={checkingUpdate}
+ description={getStatusMessage()}
>
- {downloadingUpdate ? 'Downloading...' : 'Download Update'}
+ {checkingUpdate ? 'Checking for updates...' : 'Check for Updates'}
</ButtonItem>
+ </PanelSectionRow>
+
+ {updateInfo && updateInfo.updateAvailable && !downloadResult?.success && (
+ <PanelSectionRow>
+ <ButtonItem
+ layout="below"
+ onClick={handleDownloadUpdate}
+ disabled={downloadingUpdate}
+ description={`Download version ${updateInfo.latestVersion}`}
+ >
+ {downloadingUpdate ? 'Downloading...' : 'Download Update'}
+ </ButtonItem>
+ </PanelSectionRow>
)}
{downloadResult?.success && (
- <div style={{
- marginTop: '10px',
- padding: '10px',
- backgroundColor: 'rgba(0, 255, 0, 0.1)',
- borderRadius: '4px',
- border: '1px solid rgba(0, 255, 0, 0.3)'
- }}>
- <div style={{ color: 'lightgreen', fontWeight: 'bold', marginBottom: '5px' }}>
- ✓ Download Complete!
- </div>
- <div style={{ fontSize: '12px', marginBottom: '10px' }}>
- File saved to: {downloadResult.download_path}
- </div>
- <div style={{ fontSize: '12px' }}>
- <strong>Installation Instructions:</strong>
- <ol style={{ paddingLeft: '20px', marginTop: '5px' }}>
- <li>Go to Decky Loader settings</li>
- <li>Click "Developer" tab</li>
- <li>Click "Uninstall" next to "Lossless Scaling"</li>
- <li>Click "Install from ZIP"</li>
- <li>Select the downloaded file</li>
- <li>Restart Steam or reload plugins</li>
- </ol>
- </div>
- </div>
+ <>
+ <PanelSectionRow>
+ <Field label="Download Complete!">
+ <Focusable>
+ File saved to: {downloadResult.download_path}
+ </Focusable>
+ </Field>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <Field label="Installation Instructions:">
+ <Focusable>
+ 1. Go to Decky Loader settings
+ <br />2. Click "Developer" tab
+ <br />3. Click "Uninstall" next to "Lossless Scaling"
+ <br />4. Click "Install from ZIP"
+ <br />5. Select the downloaded file
+ <br />6. Restart Steam or reload plugins
+ </Focusable>
+ </Field>
+ </PanelSectionRow>
+ </>
)}
{updateError && (
- <div style={{
- color: 'red',
- marginTop: '10px',
- padding: '8px',
- backgroundColor: 'rgba(255, 0, 0, 0.1)',
- borderRadius: '4px',
- fontSize: '12px'
- }}>
- {updateError}
- </div>
+ <PanelSectionRow>
+ <Field label="Error:">
+ <Focusable>
+ {updateError}
+ </Focusable>
+ </Field>
+ </PanelSectionRow>
)}
</PanelSection>
);
diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx
index 727a0ab..8ac94d8 100644
--- a/src/components/UsageInstructions.tsx
+++ b/src/components/UsageInstructions.tsx
@@ -6,88 +6,89 @@ interface UsageInstructionsProps {
}
export function UsageInstructions({ config }: UsageInstructionsProps) {
- // Build manual environment variables string based on current config
- const buildManualEnvVars = (): string => {
- const envVars: string[] = [];
-
- if (config.enable_lsfg) {
- envVars.push("ENABLE_LSFG=1");
- }
-
- // Always include multiplier and flow_scale if LSFG is enabled, as they have defaults
- if (config.enable_lsfg) {
- envVars.push(`LSFG_MULTIPLIER=${config.multiplier}`);
- envVars.push(`LSFG_FLOW_SCALE=${config.flow_scale}`);
- }
-
- if (config.hdr) {
- envVars.push("LSFG_HDR=1");
- }
-
- if (config.perf_mode) {
- envVars.push("LSFG_PERF_MODE=1");
- }
-
- if (config.immediate_mode) {
- envVars.push("MESA_VK_WSI_PRESENT_MODE=immediate");
- }
-
- if (config.disable_vkbasalt) {
- envVars.push("DISABLE_VKBASALT=1");
- }
-
- if (config.frame_cap > 0) {
- envVars.push(`DXVK_FRAME_RATE=${config.frame_cap}`);
- }
-
- return envVars.length > 0 ? `${envVars.join(" ")} %command%` : "%command%";
- };
-
return (
<>
<PanelSectionRow>
<div
style={{
- fontSize: "13px",
- marginTop: "12px",
- padding: "8px",
- backgroundColor: "rgba(255, 255, 255, 0.05)",
- borderRadius: "4px"
+ fontSize: "14px",
+ fontWeight: "bold",
+ marginTop: "16px",
+ marginBottom: "8px",
+ borderBottom: "1px solid rgba(255, 255, 255, 0.2)",
+ paddingBottom: "4px"
+ }}
+ >
+ Usage Instructions
+ </div>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <div
+ style={{
+ fontSize: "12px",
+ lineHeight: "1.4",
+ opacity: "0.8",
+ whiteSpace: "pre-wrap"
+ }}
+ >
+ {config.enable
+ ? "Add the launch option below (or use \"Launch Option Clipboard\") to Steam games to activate frame generation."
+ : "LSFG is disabled. Enable it above and add the launch option to activate frame generation."
+ }
+ </div>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <div
+ style={{
+ fontSize: "12px",
+ lineHeight: "1.4",
+ opacity: "0.8",
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
+ padding: "8px",
+ borderRadius: "4px",
+ fontFamily: "monospace",
+ marginTop: "8px",
+ marginBottom: "8px",
+ textAlign: "center"
+ }}
+ >
+ <strong>~/lsfg %command%</strong>
+ </div>
+ </PanelSectionRow>
+
+ {/* <PanelSectionRow>
+ <div
+ style={{
+ fontSize: "12px",
+ lineHeight: "1.4",
+ opacity: "0.8",
+ whiteSpace: "pre-wrap"
+ }}
+ >
+ {`Current Configuration:
+• Enable: ${config.enable ? "Yes" : "No"}
+• DLL Path: ${config.dll}
+• Multiplier: ${config.multiplier}x
+• Flow Scale: ${Math.round(config.flow_scale * 100)}%
+• Performance Mode: ${config.performance_mode ? "Yes" : "No"}
+• HDR Mode: ${config.hdr_mode ? "Yes" : "No"}
+• Present Mode: ${config.experimental_present_mode || "Default (FIFO)"}
+• FPS Limit: ${config.experimental_fps_limit > 0 ? `${config.experimental_fps_limit} FPS` : "Off"}`}
+ </div>
+ </PanelSectionRow> */}
+
+ <PanelSectionRow>
+ <div
+ style={{
+ fontSize: "11px",
+ lineHeight: "1.3",
+ opacity: "0.6",
+ marginTop: "8px"
}}
>
- <div style={{ fontWeight: "bold", marginBottom: "6px" }}>
- Usage Instructions:
- </div>
- <div style={{ marginBottom: "4px" }}>
- Option 1: Use the lsfg script (recommended):
- </div>
- <div
- style={{
- fontFamily: "monospace",
- backgroundColor: "rgba(0, 0, 0, 0.3)",
- padding: "4px",
- borderRadius: "2px",
- fontSize: "12px",
- marginBottom: "6px"
- }}
- >
- ~/lsfg %command%
- </div>
- <div style={{ marginBottom: "4px" }}>
- Option 2: Manual environment variables:
- </div>
- <div
- style={{
- fontFamily: "monospace",
- backgroundColor: "rgba(0, 0, 0, 0.3)",
- padding: "4px",
- borderRadius: "2px",
- fontSize: "12px",
- marginBottom: "6px"
- }}
- >
- {buildManualEnvVars()}
- </div>
+The configuration is stored in ~/.config/lsfg-vk/conf.toml and hot-reloads while games are running.
</div>
</PanelSectionRow>
</>
diff --git a/src/components/index.ts b/src/components/index.ts
index d26159d..ed0b803 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -2,7 +2,8 @@ export { Content } from "./Content";
export { StatusDisplay } from "./StatusDisplay";
export { InstallationButton } from "./InstallationButton";
export { ConfigurationSection } from "./ConfigurationSection";
-export { UsageInstructions } from "./UsageInstructions";
+// export { UsageInstructions } from "./UsageInstructions";
export { WikiButton } from "./WikiButton";
export { ClipboardButton } from "./ClipboardButton";
+export { LaunchOptionInfo } from "./LaunchOptionInfo";
export { PluginUpdateChecker } from "./PluginUpdateChecker";