summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2025-07-13 00:04:54 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2025-07-13 00:04:54 -0400
commit77494457e2a4f5c80c3a2f7acb054b12d918d8ad (patch)
treefad4c4dd2ce69a850b56078444427866dedce9fa /src/components
parent6cfcaa6c169cb8c898775eee276ff2497ab8f45c (diff)
downloaddecky-lsfg-vk-77494457e2a4f5c80c3a2f7acb054b12d918d8ad.tar.gz
decky-lsfg-vk-77494457e2a4f5c80c3a2f7acb054b12d918d8ad.zip
restructure for maintainability
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ConfigurationSection.tsx115
-rw-r--r--src/components/Content.tsx108
-rw-r--r--src/components/InstallationButton.tsx64
-rw-r--r--src/components/StatusDisplay.tsx38
-rw-r--r--src/components/UsageInstructions.tsx68
-rw-r--r--src/components/index.ts5
6 files changed, 398 insertions, 0 deletions
diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx
new file mode 100644
index 0000000..707ad2a
--- /dev/null
+++ b/src/components/ConfigurationSection.tsx
@@ -0,0 +1,115 @@
+import { PanelSectionRow, ToggleField, SliderField } from "@decky/ui";
+
+interface LsfgConfig {
+ enableLsfg: boolean;
+ multiplier: number;
+ flowScale: number;
+ hdr: boolean;
+ perfMode: boolean;
+ immediateMode: boolean;
+}
+
+interface ConfigurationSectionProps {
+ config: LsfgConfig;
+ onEnableLsfgChange: (value: boolean) => Promise<void>;
+ onMultiplierChange: (value: number) => Promise<void>;
+ onFlowScaleChange: (value: number) => Promise<void>;
+ onHdrChange: (value: boolean) => Promise<void>;
+ onPerfModeChange: (value: boolean) => Promise<void>;
+ onImmediateModeChange: (value: boolean) => Promise<void>;
+}
+
+export function ConfigurationSection({
+ config,
+ onEnableLsfgChange,
+ onMultiplierChange,
+ onFlowScaleChange,
+ onHdrChange,
+ onPerfModeChange,
+ onImmediateModeChange
+}: ConfigurationSectionProps) {
+ return (
+ <>
+ <PanelSectionRow>
+ <div
+ style={{
+ fontSize: "14px",
+ fontWeight: "bold",
+ marginTop: "16px",
+ marginBottom: "8px",
+ borderBottom: "1px solid rgba(255, 255, 255, 0.2)",
+ paddingBottom: "4px"
+ }}
+ >
+ LSFG Configuration
+ </div>
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <ToggleField
+ label="Enable LSFG"
+ description="Enables the frame generation layer"
+ checked={config.enableLsfg}
+ onChange={onEnableLsfgChange}
+ />
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <SliderField
+ label="FPS Multiplier"
+ description="Traditional FPS multiplier value"
+ value={config.multiplier}
+ min={2}
+ max={4}
+ step={1}
+ notchCount={3}
+ notchLabels={[
+ { notchIndex: 0, label: "2X" },
+ { notchIndex: 1, label: "3X" },
+ { notchIndex: 2, label: "4X" }
+ ]}
+ onChange={onMultiplierChange}
+ />
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <SliderField
+ label={`Flow Scale ${Math.round(config.flowScale * 100)}%`}
+ description="Lowers the generated frame's resolution"
+ value={config.flowScale}
+ min={0.25}
+ max={1.0}
+ step={0.01}
+ onChange={onFlowScaleChange}
+ />
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <ToggleField
+ label="HDR Mode"
+ description="Enable HDR mode (only if Game supports HDR)"
+ checked={config.hdr}
+ onChange={onHdrChange}
+ />
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <ToggleField
+ label="Performance Mode"
+ description="Use lighter model for FG (experimental)"
+ checked={config.perfMode}
+ onChange={onPerfModeChange}
+ />
+ </PanelSectionRow>
+
+ <PanelSectionRow>
+ <ToggleField
+ label="Immediate Mode"
+ description="Disable vsync for reduced input lag"
+ checked={config.immediateMode}
+ onChange={onImmediateModeChange}
+ />
+ </PanelSectionRow>
+ </>
+ );
+}
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
new file mode 100644
index 0000000..cecb142
--- /dev/null
+++ b/src/components/Content.tsx
@@ -0,0 +1,108 @@
+import { useEffect } from "react";
+import { PanelSection } from "@decky/ui";
+import { useInstallationStatus, useDllDetection, useLsfgConfig } from "../hooks/useLsfgHooks";
+import { useInstallationActions } from "../hooks/useInstallationActions";
+import { StatusDisplay } from "./StatusDisplay";
+import { InstallationButton } from "./InstallationButton";
+import { ConfigurationSection } from "./ConfigurationSection";
+import { UsageInstructions } from "./UsageInstructions";
+
+export function Content() {
+ const {
+ isInstalled,
+ installationStatus,
+ setIsInstalled,
+ setInstallationStatus
+ } = useInstallationStatus();
+
+ const { dllDetected, dllDetectionStatus } = useDllDetection();
+
+ const {
+ config,
+ setters,
+ loadLsfgConfig,
+ updateConfig
+ } = useLsfgConfig();
+
+ const { isInstalling, isUninstalling, handleInstall, handleUninstall } = useInstallationActions();
+
+ // Reload config when installation status changes
+ useEffect(() => {
+ if (isInstalled) {
+ loadLsfgConfig();
+ }
+ }, [isInstalled, loadLsfgConfig]);
+
+ // Configuration change handlers
+ const handleEnableLsfgChange = async (value: boolean) => {
+ setters.setEnableLsfg(value);
+ await updateConfig(value, config.multiplier, config.flowScale, config.hdr, config.perfMode, config.immediateMode);
+ };
+
+ const handleMultiplierChange = async (value: number) => {
+ setters.setMultiplier(value);
+ await updateConfig(config.enableLsfg, value, config.flowScale, config.hdr, config.perfMode, config.immediateMode);
+ };
+
+ const handleFlowScaleChange = async (value: number) => {
+ setters.setFlowScale(value);
+ await updateConfig(config.enableLsfg, config.multiplier, value, config.hdr, config.perfMode, config.immediateMode);
+ };
+
+ const handleHdrChange = async (value: boolean) => {
+ setters.setHdr(value);
+ await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, value, config.perfMode, config.immediateMode);
+ };
+
+ const handlePerfModeChange = async (value: boolean) => {
+ setters.setPerfMode(value);
+ await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, value, config.immediateMode);
+ };
+
+ const handleImmediateModeChange = async (value: boolean) => {
+ setters.setImmediateMode(value);
+ await updateConfig(config.enableLsfg, config.multiplier, config.flowScale, config.hdr, config.perfMode, value);
+ };
+
+ const onInstall = () => {
+ handleInstall(setIsInstalled, setInstallationStatus, loadLsfgConfig);
+ };
+
+ const onUninstall = () => {
+ handleUninstall(setIsInstalled, setInstallationStatus);
+ };
+
+ return (
+ <PanelSection>
+ <StatusDisplay
+ dllDetected={dllDetected}
+ dllDetectionStatus={dllDetectionStatus}
+ isInstalled={isInstalled}
+ installationStatus={installationStatus}
+ />
+
+ <InstallationButton
+ isInstalled={isInstalled}
+ isInstalling={isInstalling}
+ isUninstalling={isUninstalling}
+ onInstall={onInstall}
+ onUninstall={onUninstall}
+ />
+
+ {/* Configuration Section - only show if installed */}
+ {isInstalled && (
+ <ConfigurationSection
+ config={config}
+ onEnableLsfgChange={handleEnableLsfgChange}
+ onMultiplierChange={handleMultiplierChange}
+ onFlowScaleChange={handleFlowScaleChange}
+ onHdrChange={handleHdrChange}
+ onPerfModeChange={handlePerfModeChange}
+ onImmediateModeChange={handleImmediateModeChange}
+ />
+ )}
+
+ <UsageInstructions multiplier={config.multiplier} />
+ </PanelSection>
+ );
+}
diff --git a/src/components/InstallationButton.tsx b/src/components/InstallationButton.tsx
new file mode 100644
index 0000000..7892678
--- /dev/null
+++ b/src/components/InstallationButton.tsx
@@ -0,0 +1,64 @@
+import { ButtonItem, PanelSectionRow } from "@decky/ui";
+import { FaDownload, FaTrash } from "react-icons/fa";
+
+interface InstallationButtonProps {
+ isInstalled: boolean;
+ isInstalling: boolean;
+ isUninstalling: boolean;
+ onInstall: () => void;
+ onUninstall: () => void;
+}
+
+export function InstallationButton({
+ isInstalled,
+ isInstalling,
+ isUninstalling,
+ onInstall,
+ onUninstall
+}: InstallationButtonProps) {
+ const renderButtonContent = () => {
+ if (isInstalling) {
+ return (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <div>Installing...</div>
+ </div>
+ );
+ }
+
+ if (isUninstalling) {
+ return (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <div>Uninstalling...</div>
+ </div>
+ );
+ }
+
+ if (isInstalled) {
+ return (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <FaTrash />
+ <div>Uninstall lsfg-vk</div>
+ </div>
+ );
+ }
+
+ return (
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
+ <FaDownload />
+ <div>Install lsfg-vk</div>
+ </div>
+ );
+ };
+
+ return (
+ <PanelSectionRow>
+ <ButtonItem
+ layout="below"
+ onClick={isInstalled ? onUninstall : onInstall}
+ disabled={isInstalling || isUninstalling}
+ >
+ {renderButtonContent()}
+ </ButtonItem>
+ </PanelSectionRow>
+ );
+}
diff --git a/src/components/StatusDisplay.tsx b/src/components/StatusDisplay.tsx
new file mode 100644
index 0000000..8eecd42
--- /dev/null
+++ b/src/components/StatusDisplay.tsx
@@ -0,0 +1,38 @@
+import { PanelSectionRow } from "@decky/ui";
+
+interface StatusDisplayProps {
+ dllDetected: boolean;
+ dllDetectionStatus: string;
+ isInstalled: boolean;
+ installationStatus: string;
+}
+
+export function StatusDisplay({
+ dllDetected,
+ dllDetectionStatus,
+ isInstalled,
+ installationStatus
+}: StatusDisplayProps) {
+ return (
+ <PanelSectionRow>
+ <div style={{ marginBottom: "8px", fontSize: "14px" }}>
+ <div
+ style={{
+ color: dllDetected ? "#4CAF50" : "#F44336",
+ fontWeight: "bold",
+ marginBottom: "4px"
+ }}
+ >
+ {dllDetectionStatus}
+ </div>
+ <div
+ style={{
+ color: isInstalled ? "#4CAF50" : "#FF9800"
+ }}
+ >
+ Status: {installationStatus}
+ </div>
+ </div>
+ </PanelSectionRow>
+ );
+}
diff --git a/src/components/UsageInstructions.tsx b/src/components/UsageInstructions.tsx
new file mode 100644
index 0000000..712d4c1
--- /dev/null
+++ b/src/components/UsageInstructions.tsx
@@ -0,0 +1,68 @@
+import { PanelSectionRow } from "@decky/ui";
+
+interface UsageInstructionsProps {
+ multiplier: number;
+}
+
+export function UsageInstructions({ multiplier }: UsageInstructionsProps) {
+ return (
+ <PanelSectionRow>
+ <div
+ style={{
+ fontSize: "13px",
+ marginTop: "12px",
+ padding: "8px",
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
+ borderRadius: "4px"
+ }}
+ >
+ <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"
+ }}
+ >
+ ENABLE_LSFG=1 LSFG_MULTIPLIER={multiplier} %COMMAND%
+ </div>
+ <div style={{ fontSize: "11px", opacity: 0.8 }}>
+ The lsfg script uses your current configuration settings.
+ <br />
+ • ENABLE_LSFG=1 - Enables frame generation
+ <br />
+ • LSFG_MULTIPLIER=2-4 - FPS multiplier (start with 2)
+ <br />
+ • LSFG_FLOW_SCALE=0.25-1.0 - Flow scale (for performance)
+ <br />
+ • LSFG_HDR=1 - HDR mode (only if using HDR)
+ <br />
+ • MESA_VK_WSI_PRESENT_MODE=immediate - Disable vsync
+ </div>
+ </div>
+ </PanelSectionRow>
+ );
+}
diff --git a/src/components/index.ts b/src/components/index.ts
new file mode 100644
index 0000000..77f11d4
--- /dev/null
+++ b/src/components/index.ts
@@ -0,0 +1,5 @@
+export { Content } from "./Content";
+export { StatusDisplay } from "./StatusDisplay";
+export { InstallationButton } from "./InstallationButton";
+export { ConfigurationSection } from "./ConfigurationSection";
+export { UsageInstructions } from "./UsageInstructions";