import { FC, useState, useEffect } from 'react'; import { ModalRoot, DialogBody, DialogHeader, DialogControlsSection, DialogControlsSectionHeader, ButtonItem, PanelSectionRow, Field, Toggle, Spinner, Focusable, showModal, ConfirmModal } from '@decky/ui'; import { FaCheck, FaTimes, FaDownload, FaTrash, FaCog } from 'react-icons/fa'; import flatpakTargetImage from '../../assets/flatpak-target.png'; import { checkFlatpakExtensionStatus, installFlatpakExtension, uninstallFlatpakExtension, getFlatpakApps, setFlatpakAppOverride, removeFlatpakAppOverride, FlatpakExtensionStatus, FlatpakApp, FlatpakAppInfo } from '../api/lsfgApi'; interface FlatpaksModalProps { closeModal?: () => void; } export const FlatpaksModal: FC = ({ closeModal }) => { const [extensionStatus, setExtensionStatus] = useState(null); const [flatpakApps, setFlatpakApps] = useState(null); const [loading, setLoading] = useState(true); const [operationInProgress, setOperationInProgress] = useState(null); const loadData = async () => { setLoading(true); try { const [statusResult, appsResult] = await Promise.all([ checkFlatpakExtensionStatus(), getFlatpakApps() ]); setExtensionStatus(statusResult); setFlatpakApps(appsResult); } catch (error) { console.error('Error loading Flatpak data:', error); } finally { setLoading(false); } }; useEffect(() => { loadData(); }, []); const handleExtensionOperation = async (operation: 'install' | 'uninstall', version: string) => { const operationId = `${operation}-${version}`; setOperationInProgress(operationId); try { const result = operation === 'install' ? await installFlatpakExtension(version) : await uninstallFlatpakExtension(version); if (result.success) { // Reload status after operation const newStatus = await checkFlatpakExtensionStatus(); setExtensionStatus(newStatus); } } catch (error) { console.error(`Error ${operation}ing extension:`, error); } finally { setOperationInProgress(null); } }; const handleAppOverrideToggle = async (app: FlatpakApp) => { const hasOverrides = app.has_filesystem_override && app.has_env_override; const operationId = `app-${app.app_id}`; setOperationInProgress(operationId); try { const result = hasOverrides ? await removeFlatpakAppOverride(app.app_id) : await setFlatpakAppOverride(app.app_id); if (result.success) { // Reload apps data after operation const newApps = await getFlatpakApps(); setFlatpakApps(newApps); } } catch (error) { console.error('Error toggling app override:', error); } finally { setOperationInProgress(null); } }; const confirmOperation = (operation: () => void, title: string, description: string) => { showModal( {}} /> ); }; if (loading) { return ( Flatpak Extensions
); } return ( Flatpak Extensions {/* Extension Status Section */} Runtime Extension Installer {extensionStatus && extensionStatus.success ? ( <> {/* 23.08 Runtime */} : } > { const operation = extensionStatus.installed_23_08 ? 'uninstall' : 'install'; const action = () => handleExtensionOperation(operation, '23.08'); if (operation === 'uninstall') { confirmOperation( action, 'Uninstall Runtime Extension', 'Are you sure you want to uninstall the 23.08 runtime extension?' ); } else { action(); } }} disabled={operationInProgress === 'install-23.08' || operationInProgress === 'uninstall-23.08'} > {operationInProgress === 'install-23.08' || operationInProgress === 'uninstall-23.08' ? ( ) : extensionStatus.installed_23_08 ? ( <> Uninstall ) : ( <> Install )} {/* 24.08 Runtime */} : } > { const operation = extensionStatus.installed_24_08 ? 'uninstall' : 'install'; const action = () => handleExtensionOperation(operation, '24.08'); if (operation === 'uninstall') { confirmOperation( action, 'Uninstall Runtime Extension', 'Are you sure you want to uninstall the 24.08 runtime extension?' ); } else { action(); } }} disabled={operationInProgress === 'install-24.08' || operationInProgress === 'uninstall-24.08'} > {operationInProgress === 'install-24.08' || operationInProgress === 'uninstall-24.08' ? ( ) : extensionStatus.installed_24_08 ? ( <> Uninstall ) : ( <> Install )} {/* 25.08 Runtime */} : } > { const operation = extensionStatus.installed_25_08 ? 'uninstall' : 'install'; const action = () => handleExtensionOperation(operation, '25.08'); if (operation === 'uninstall') { confirmOperation( action, 'Uninstall Runtime Extension', 'Are you sure you want to uninstall the 25.08 runtime extension?' ); } else { action(); } }} disabled={operationInProgress === 'install-25.08' || operationInProgress === 'uninstall-25.08'} > {operationInProgress === 'install-25.08' || operationInProgress === 'uninstall-25.08' ? ( ) : extensionStatus.installed_25_08 ? ( <> Uninstall ) : ( <> Install )} ) : ( } /> )} {/* Flatpak Apps Section */} Flatpak Applications {flatpakApps && flatpakApps.success ? ( flatpakApps.apps.length > 0 ? ( flatpakApps.apps.map((app) => { const hasOverrides = app.has_filesystem_override && app.has_env_override; const partialOverrides = app.has_filesystem_override || app.has_env_override; let statusColor = 'red'; let statusText = 'No overrides'; if (hasOverrides) { statusColor = 'green'; statusText = 'Configured'; } else if (partialOverrides) { statusColor = 'orange'; statusText = 'Partial'; } return ( } > handleAppOverrideToggle(app)} disabled={operationInProgress === `app-${app.app_id}`} /> ); }) ) : ( ) ) : ( } /> )} {/* Steam Configuration Instructions */} Steam Configuration
Configure Steam Flatpak Shortcuts
In Steam, open your flatpak game and click the cog wheel."
IMPORTANT: Set this in TARGET (NOT LAUNCH OPTIONS)
Try first:
~/lsfg
If that doesn't work, try full path:
/home/(username)/lsfg
Final result should look like:
~/lsfg "usr/bin/flatpak"
{/* Visual example image */}
Steam Properties Target Field Example
{/* Close Button */} Close
); };