summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxXJSONDeruloXx <danielhimebauch@gmail.com>2025-10-20 12:50:03 -0400
committerxXJSONDeruloXx <danielhimebauch@gmail.com>2025-10-20 12:50:03 -0400
commitc3f510a2944cf8436c8bccedcb77db9bd6a7f2c3 (patch)
treea603425e25da40307b49397d20ba5bed19d7955a
parentbd2fdea07cde9aaac77047aa4a7d406c39e5a56e (diff)
downloaddecky-lsfg-vk-c3f510a2944cf8436c8bccedcb77db9bd6a7f2c3.tar.gz
decky-lsfg-vk-c3f510a2944cf8436c8bccedcb77db9bd6a7f2c3.zip
chore: remove unused elements previously commented out, for store release prep
-rw-r--r--README.md10
-rw-r--r--assets/logo.pngbin21108 -> 0 bytes
-rw-r--r--assets/qam-screenshot.jpegbin60314 -> 0 bytes
-rw-r--r--decky.pyi184
-rw-r--r--defaults/defaults.txt13
-rw-r--r--src/api/lsfgApi.ts20
-rw-r--r--src/components/ClipboardButton.tsx22
-rw-r--r--src/components/ClipboardDisplay.tsx139
-rw-r--r--src/components/Content.tsx16
-rw-r--r--src/components/PluginUpdateChecker.tsx176
-rw-r--r--src/components/WikiButton.tsx22
-rw-r--r--src/components/index.ts11
12 files changed, 17 insertions, 596 deletions
diff --git a/README.md b/README.md
index 609f77c..de75050 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,10 @@
> **Note:**
> This is an **unofficial community plugin**. It is independently developed and **not officially supported** by the creators of Lossless Scaling or lsfg-vk. For support, please use the [decky-lsfg-vk Discord Channel](https://discord.gg/TwvHdVucC3).
+
+<p align="center">
+ <img src="assets/decky-lossless-logo.png" alt="decky-lsfg-vk Logo" width="200"/>
+</p>
<p align="center">
<a href="https://ko-fi.com/B0B71HZTAX" target="_blank" rel="noopener noreferrer">
<img src="https://ko-fi.com/img/githubbutton_sm.svg" alt="Support on Ko-fi"/>
@@ -10,12 +14,6 @@
</p>
-
-<p align="center">
- <img src="assets/decky-lossless-logo.png" alt="decky-lsfg-vk Logo" width="200"/>
-</p>
-
-
## What is this?
A Decky plugin that streamlines the installation of **lsfg-vk** ([Lossless Scaling Frame Generation Vulkan layer](https://github.com/PancakeTAS/lsfg-vk)) on Steam Deck, allowing you to use the Lossless Scaling frame generation features on Linux with a controller friendly UI in SteamOS, Bazzite, or any other Linux platform compatible with Decky Loader.
diff --git a/assets/logo.png b/assets/logo.png
deleted file mode 100644
index 48c4851..0000000
--- a/assets/logo.png
+++ /dev/null
Binary files differ
diff --git a/assets/qam-screenshot.jpeg b/assets/qam-screenshot.jpeg
deleted file mode 100644
index 8df12b2..0000000
--- a/assets/qam-screenshot.jpeg
+++ /dev/null
Binary files differ
diff --git a/decky.pyi b/decky.pyi
deleted file mode 100644
index a72c74c..0000000
--- a/decky.pyi
+++ /dev/null
@@ -1,184 +0,0 @@
-"""
-This module exposes various constants and helpers useful for decky plugins.
-
-* Plugin's settings and configurations should be stored under `DECKY_PLUGIN_SETTINGS_DIR`.
-* Plugin's runtime data should be stored under `DECKY_PLUGIN_RUNTIME_DIR`.
-* Plugin's persistent log files should be stored under `DECKY_PLUGIN_LOG_DIR`.
-
-Avoid writing outside of `DECKY_HOME`, storing under the suggested paths is strongly recommended.
-
-Some basic migration helpers are available: `migrate_any`, `migrate_settings`, `migrate_runtime`, `migrate_logs`.
-
-A logging facility `logger` is available which writes to the recommended location.
-"""
-
-__version__ = '1.0.0'
-
-import logging
-
-from typing import Any
-
-"""
-Constants
-"""
-
-HOME: str
-"""
-The home directory of the effective user running the process.
-Environment variable: `HOME`.
-If `root` was specified in the plugin's flags it will be `/root` otherwise the user whose home decky resides in.
-e.g.: `/home/deck`
-"""
-
-USER: str
-"""
-The effective username running the process.
-Environment variable: `USER`.
-It would be `root` if `root` was specified in the plugin's flags otherwise the user whose home decky resides in.
-e.g.: `deck`
-"""
-
-DECKY_VERSION: str
-"""
-The version of the decky loader.
-Environment variable: `DECKY_VERSION`.
-e.g.: `v2.5.0-pre1`
-"""
-
-DECKY_USER: str
-"""
-The user whose home decky resides in.
-Environment variable: `DECKY_USER`.
-e.g.: `deck`
-"""
-
-DECKY_USER_HOME: str
-"""
-The home of the user where decky resides in.
-Environment variable: `DECKY_USER_HOME`.
-e.g.: `/home/deck`
-"""
-
-DECKY_HOME: str
-"""
-The root of the decky folder.
-Environment variable: `DECKY_HOME`.
-e.g.: `/home/deck/homebrew`
-"""
-
-DECKY_PLUGIN_SETTINGS_DIR: str
-"""
-The recommended path in which to store configuration files (created automatically).
-Environment variable: `DECKY_PLUGIN_SETTINGS_DIR`.
-e.g.: `/home/deck/homebrew/settings/decky-plugin-template`
-"""
-
-DECKY_PLUGIN_RUNTIME_DIR: str
-"""
-The recommended path in which to store runtime data (created automatically).
-Environment variable: `DECKY_PLUGIN_RUNTIME_DIR`.
-e.g.: `/home/deck/homebrew/data/decky-plugin-template`
-"""
-
-DECKY_PLUGIN_LOG_DIR: str
-"""
-The recommended path in which to store persistent logs (created automatically).
-Environment variable: `DECKY_PLUGIN_LOG_DIR`.
-e.g.: `/home/deck/homebrew/logs/decky-plugin-template`
-"""
-
-DECKY_PLUGIN_DIR: str
-"""
-The root of the plugin's directory.
-Environment variable: `DECKY_PLUGIN_DIR`.
-e.g.: `/home/deck/homebrew/plugins/decky-plugin-template`
-"""
-
-DECKY_PLUGIN_NAME: str
-"""
-The name of the plugin as specified in the 'plugin.json'.
-Environment variable: `DECKY_PLUGIN_NAME`.
-e.g.: `Example Plugin`
-"""
-
-DECKY_PLUGIN_VERSION: str
-"""
-The version of the plugin as specified in the 'package.json'.
-Environment variable: `DECKY_PLUGIN_VERSION`.
-e.g.: `0.0.1`
-"""
-
-DECKY_PLUGIN_AUTHOR: str
-"""
-The author of the plugin as specified in the 'plugin.json'.
-Environment variable: `DECKY_PLUGIN_AUTHOR`.
-e.g.: `John Doe`
-"""
-
-DECKY_PLUGIN_LOG: str
-"""
-The path to the plugin's main logfile.
-Environment variable: `DECKY_PLUGIN_LOG`.
-e.g.: `/home/deck/homebrew/logs/decky-plugin-template/plugin.log`
-"""
-
-"""
-Migration helpers
-"""
-
-
-def migrate_any(target_dir: str, *files_or_directories: str) -> dict[str, str]:
- """
- Migrate files and directories to a new location and remove old locations.
- Specified files will be migrated to `target_dir`.
- Specified directories will have their contents recursively migrated to `target_dir`.
-
- Returns the mapping of old -> new location.
- """
-
-
-def migrate_settings(*files_or_directories: str) -> dict[str, str]:
- """
- Migrate files and directories relating to plugin settings to the recommended location and remove old locations.
- Specified files will be migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
- Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_SETTINGS_DIR`.
-
- Returns the mapping of old -> new location.
- """
-
-
-def migrate_runtime(*files_or_directories: str) -> dict[str, str]:
- """
- Migrate files and directories relating to plugin runtime data to the recommended location and remove old locations
- Specified files will be migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
- Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_RUNTIME_DIR`.
-
- Returns the mapping of old -> new location.
- """
-
-
-def migrate_logs(*files_or_directories: str) -> dict[str, str]:
- """
- Migrate files and directories relating to plugin logs to the recommended location and remove old locations.
- Specified files will be migrated to `DECKY_PLUGIN_LOG_DIR`.
- Specified directories will have their contents recursively migrated to `DECKY_PLUGIN_LOG_DIR`.
-
- Returns the mapping of old -> new location.
- """
-
-
-"""
-Logging
-"""
-
-logger: logging.Logger
-"""The main plugin logger writing to `DECKY_PLUGIN_LOG`."""
-
-"""
-Event handling
-"""
-# TODO better docstring im lazy
-async def emit(event: str, *args: Any) -> None:
- """
- Send an event to the frontend.
- """ \ No newline at end of file
diff --git a/defaults/defaults.txt b/defaults/defaults.txt
deleted file mode 100644
index ebf140b..0000000
--- a/defaults/defaults.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-If you have plain-text json configs, theme templates, or templates for usage for your plugin of any description you should have those files be in here.
-Those files will be pulled into the zip during the build process and included with the upload. Example: CssLoader with it's themes in "default/themes" would have the "themes" folder will be added alongside with the dist folder, main.py, LICENSE and README files in the subfolder of the zip containing the plugin.
-Files can also be put in here such as a config, just keep in mind that they this directory cannot be utilized to put files in arbitrary locations, just within the extracted root folder of the plugin, ex: CssLoader has "defaults/themes/..." setup in it's repo, but when packaged to go to the store, the file structure will be:
-
-- LICENSE
-- README
-- dist
- - index.js
-- main.py
-- package.json
-- plugin.json
-- themes
- - exampletheme.css \ No newline at end of file
diff --git a/src/api/lsfgApi.ts b/src/api/lsfgApi.ts
index dda6c23..82f37c8 100644
--- a/src/api/lsfgApi.ts
+++ b/src/api/lsfgApi.ts
@@ -59,23 +59,6 @@ export interface ConfigSchemaResult {
current_profile?: string;
}
-export interface UpdateCheckResult {
- success: boolean;
- update_available: boolean;
- current_version: string;
- latest_version: string;
- release_notes: string;
- release_date: string;
- download_url: string;
- error?: string;
-}
-
-export interface UpdateDownloadResult {
- success: boolean;
- download_path?: string;
- error?: string;
-}
-
export interface LaunchOptionResult {
launch_option: string;
instructions: string;
@@ -178,9 +161,6 @@ export const updateLsfgConfigFromObject = async (config: ConfigurationData): Pro
};
// Self-updater API functions
-export const checkForPluginUpdate = callable<[], UpdateCheckResult>("check_for_plugin_update");
-export const downloadPluginUpdate = callable<[string], UpdateDownloadResult>("download_plugin_update");
-
// Profile management API functions
export const getProfiles = callable<[], ProfilesResult>("get_profiles");
export const createProfile = callable<[string, string?], ProfileResult>("create_profile");
diff --git a/src/components/ClipboardButton.tsx b/src/components/ClipboardButton.tsx
deleted file mode 100644
index cac1863..0000000
--- a/src/components/ClipboardButton.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { PanelSectionRow, ButtonItem } from "@decky/ui";
-import { FaBook } from "react-icons/fa";
-
-export function ClipboardButton() {
- const handleClipboardClick = () => {
- window.open("https://github.com/xXJSONDeruloXx/decky-lossless-scaling-vk/wiki/Clipboard", "_blank");
- };
-
- return (
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={handleClipboardClick}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaBook />
- <div>Plugin Wiki</div>
- </div>
- </ButtonItem>
- </PanelSectionRow>
- );
-}
diff --git a/src/components/ClipboardDisplay.tsx b/src/components/ClipboardDisplay.tsx
deleted file mode 100644
index 852a50f..0000000
--- a/src/components/ClipboardDisplay.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-import { useState, useEffect } from "react";
-import { PanelSectionRow } from "@decky/ui";
-import { FaClipboard, FaEye } from "react-icons/fa";
-
-export function ClipboardDisplay() {
- const [clipboardContent, setClipboardContent] = useState<string>("");
- const [isReading, setIsReading] = useState(false);
-
- const readClipboard = async () => {
- if (isReading) return; // Prevent concurrent reads
-
- setIsReading(true);
- try {
- console.log("ClipboardDisplay: Attempting to read clipboard...");
-
- if (!navigator.clipboard) {
- console.log("ClipboardDisplay: navigator.clipboard not available");
- setClipboardContent("Clipboard API not available");
- return;
- }
-
- if (!navigator.clipboard.readText) {
- console.log("ClipboardDisplay: navigator.clipboard.readText not available");
- setClipboardContent("Clipboard read not supported");
- return;
- }
-
- console.log("ClipboardDisplay: Calling navigator.clipboard.readText()...");
- const content = await navigator.clipboard.readText();
- console.log("ClipboardDisplay: Successfully read clipboard:", content.length, "characters");
- setClipboardContent(content);
- } catch (error) {
- // This is expected if user hasn't granted clipboard permissions
- // or if we're in a context where reading isn't allowed
- console.log("ClipboardDisplay: Error reading clipboard:", error);
- console.log("ClipboardDisplay: Error name:", (error as Error).name);
- console.log("ClipboardDisplay: Error message:", (error as Error).message);
-
- // More specific error messages based on error type
- if (error instanceof DOMException) {
- switch (error.name) {
- case 'NotAllowedError':
- setClipboardContent("Clipboard access denied - check permissions");
- break;
- case 'NotFoundError':
- setClipboardContent("No clipboard data found");
- break;
- case 'SecurityError':
- setClipboardContent("Clipboard access blocked by security policy");
- break;
- default:
- setClipboardContent(`Clipboard error: ${error.name}`);
- }
- } else {
- setClipboardContent("Unable to read clipboard");
- }
- } finally {
- setIsReading(false);
- }
- };
-
- // Read clipboard on mount and then every 3 seconds
- useEffect(() => {
- readClipboard();
-
- const interval = setInterval(() => {
- readClipboard();
- }, 3000);
-
- return () => clearInterval(interval);
- }, []);
-
- const truncateText = (text: string, maxLength: number = 60) => {
- if (text.length <= maxLength) return text;
- return text.substring(0, maxLength) + "...";
- };
-
- const displayText = truncateText(clipboardContent);
-
- return (
- <PanelSectionRow>
- <div style={{
- padding: "8px",
- backgroundColor: "rgba(255, 255, 255, 0.05)",
- borderRadius: "4px",
- border: "1px solid rgba(255, 255, 255, 0.1)",
- marginBottom: "8px"
- }}>
- <div style={{
- display: "flex",
- alignItems: "center",
- gap: "8px",
- marginBottom: "4px"
- }}>
- <FaClipboard style={{ color: "#888", fontSize: "12px" }} />
- <div style={{
- fontSize: "12px",
- fontWeight: "bold",
- color: "#888",
- textTransform: "uppercase"
- }}>
- Current Clipboard
- </div>
- {isReading && (
- <FaEye style={{
- color: "#888",
- fontSize: "10px",
- animation: "pulse 1s ease-in-out infinite"
- }} />
- )}
- </div>
- <div style={{
- fontSize: "11px",
- color: clipboardContent.includes("error") ||
- clipboardContent.includes("denied") ||
- clipboardContent.includes("not available") ||
- clipboardContent.includes("not supported") ||
- clipboardContent.includes("blocked") ||
- clipboardContent === "Unable to read clipboard"
- ? "#ff6b6b"
- : "#fff",
- fontFamily: "monospace",
- wordBreak: "break-word",
- lineHeight: "1.3",
- minHeight: "14px"
- }}>
- {displayText || "Reading clipboard..."}
- </div>
- </div>
- <style>{`
- @keyframes pulse {
- 0% { opacity: 0.5; }
- 50% { opacity: 1; }
- 100% { opacity: 0.5; }
- }
- `}</style>
- </PanelSectionRow>
- );
-}
diff --git a/src/components/Content.tsx b/src/components/Content.tsx
index fdb8672..08027d3 100644
--- a/src/components/Content.tsx
+++ b/src/components/Content.tsx
@@ -8,12 +8,8 @@ import { InstallationButton } from "./InstallationButton";
import { ConfigurationSection } from "./ConfigurationSection";
import { ProfileManagement } from "./ProfileManagement";
import { UsageInstructions } from "./UsageInstructions";
-// import { WikiButton } from "./WikiButton";
-// import { ClipboardButton } from "./ClipboardButton";
import { SmartClipboardButton } from "./SmartClipboardButton";
import { FgmodClipboardButton } from "./FgmodClipboardButton";
-// import { ClipboardDisplay } from "./ClipboardDisplay";
-// import { PluginUpdateChecker } from "./PluginUpdateChecker";
import { NerdStuffModal } from "./NerdStuffModal";
import FlatpaksModal from "./FlatpaksModal";
import { ConfigurationData } from "../config/configSchema";
@@ -106,7 +102,6 @@ export function Content() {
{/* Clipboard buttons - only show if installed */}
{isInstalled && (
<>
- {/* <ClipboardDisplay /> */}
<SmartClipboardButton />
<FgmodClipboardButton />
</>
@@ -131,15 +126,8 @@ export function Content() {
/>
)}
- {/* Usage instructions - always visible for user guidance */}
- <UsageInstructions config={config} />
-
- {/* Wiki and clipboard buttons - always available for documentation */}
- {/* <WikiButton /> */}
- {/* <ClipboardButton /> */}
-
- {/* Plugin Update Checker */}
- {/* <PluginUpdateChecker /> */}
+ {/* Usage instructions - always visible for user guidance */}
+ <UsageInstructions config={config} />
{/* Show installation components at bottom when fully installed */}
{isInstalled && (
diff --git a/src/components/PluginUpdateChecker.tsx b/src/components/PluginUpdateChecker.tsx
deleted file mode 100644
index 9fa2afb..0000000
--- a/src/components/PluginUpdateChecker.tsx
+++ /dev/null
@@ -1,176 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import {
- ButtonItem,
- PanelSection,
- PanelSectionRow,
- Field,
- Focusable
-} from '@decky/ui';
-import { checkForPluginUpdate, downloadPluginUpdate, UpdateCheckResult, UpdateDownloadResult } from '../api/lsfgApi';
-
-interface PluginUpdateCheckerProps {
- // Add any props if needed
-}
-
-interface UpdateInfo {
- updateAvailable: boolean;
- currentVersion: string;
- latestVersion: string;
- releaseNotes: string;
- releaseDate: string;
- downloadUrl: string;
-}
-
-export const PluginUpdateChecker: React.FC<PluginUpdateCheckerProps> = () => {
- const [checkingUpdate, setCheckingUpdate] = useState(false);
- const [downloadingUpdate, setDownloadingUpdate] = useState(false);
- const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
- const [updateError, setUpdateError] = useState<string | null>(null);
- const [downloadResult, setDownloadResult] = useState<UpdateDownloadResult | null>(null);
-
- // Auto-hide error messages after 5 seconds
- useEffect(() => {
- if (updateError) {
- const timer = setTimeout(() => {
- setUpdateError(null);
- }, 5000);
- return () => clearTimeout(timer);
- }
- return undefined;
- }, [updateError]);
-
- const handleCheckForUpdate = async () => {
- setCheckingUpdate(true);
- setUpdateError(null);
- setUpdateInfo(null);
- setDownloadResult(null); // Clear previous download result
-
- try {
- const result: UpdateCheckResult = await checkForPluginUpdate();
-
- if (result.success) {
- setUpdateInfo({
- updateAvailable: result.update_available,
- currentVersion: result.current_version,
- latestVersion: result.latest_version,
- releaseNotes: result.release_notes,
- releaseDate: result.release_date,
- downloadUrl: result.download_url
- });
-
- // Simple console log instead of toast since showToast may not be available
- if (result.update_available) {
- console.log("Update available!", `Version ${result.latest_version} is now available.`);
- } else {
- console.log("Up to date!", "You have the latest version installed.");
- }
- } else {
- setUpdateError(result.error || "Failed to check for updates");
- }
- } catch (error) {
- setUpdateError(`Error checking for updates: ${error}`);
- } finally {
- setCheckingUpdate(false);
- }
- };
-
- const handleDownloadUpdate = async () => {
- if (!updateInfo?.downloadUrl) return;
-
- setDownloadingUpdate(true);
- setUpdateError(null);
- setDownloadResult(null);
-
- try {
- const result: UpdateDownloadResult = await downloadPluginUpdate(updateInfo.downloadUrl);
-
- if (result.success) {
- setDownloadResult(result);
- console.log("✓ Download complete!", `Plugin downloaded to ${result.download_path}`);
- } else {
- setUpdateError(result.error || "Failed to download update");
- }
- } catch (error) {
- setUpdateError(`Error downloading update: ${error}`);
- } finally {
- setDownloadingUpdate(false);
- }
- };
-
- const getStatusMessage = () => {
- if (!updateInfo) return null;
-
- if (updateInfo.updateAvailable) {
- if (downloadResult?.success) {
- return "✓ v" + updateInfo.latestVersion + " downloaded - ready to install";
- } else {
- return "Update available: v" + updateInfo.latestVersion;
- }
- } else {
- return "Up to date (v" + updateInfo.currentVersion + ")";
- }
- };
-
- return (
- <PanelSection title="PLUGIN UPDATES">
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={handleCheckForUpdate}
- disabled={checkingUpdate}
- description={getStatusMessage()}
- >
- {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 && (
- <>
- <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 "decky-lsfg-vk"
- <br />4. Click "Install from ZIP"
- <br />5. Select the downloaded file
- <br />6. Restart Steam or reload plugins
- </Focusable>
- </Field>
- </PanelSectionRow>
- </>
- )}
-
- {updateError && (
- <PanelSectionRow>
- <Field label="Error:">
- <Focusable>
- {updateError}
- </Focusable>
- </Field>
- </PanelSectionRow>
- )}
- </PanelSection>
- );
-};
diff --git a/src/components/WikiButton.tsx b/src/components/WikiButton.tsx
deleted file mode 100644
index 065fb8e..0000000
--- a/src/components/WikiButton.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { PanelSectionRow, ButtonItem } from "@decky/ui";
-import { FaBook } from "react-icons/fa";
-
-export function WikiButton() {
- const handleWikiClick = () => {
- window.open("https://github.com/PancakeTAS/lsfg-vk/wiki", "_blank");
- };
-
- return (
- <PanelSectionRow>
- <ButtonItem
- layout="below"
- onClick={handleWikiClick}
- >
- <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
- <FaBook />
- <div>LSFG-VK Wiki</div>
- </div>
- </ButtonItem>
- </PanelSectionRow>
- );
-}
diff --git a/src/components/index.ts b/src/components/index.ts
new file mode 100644
index 0000000..95e7451
--- /dev/null
+++ b/src/components/index.ts
@@ -0,0 +1,11 @@
+export { Content } from "./Content";
+export { StatusDisplay } from "./StatusDisplay";
+export { InstallationButton } from "./InstallationButton";
+export { ConfigurationSection } from "./ConfigurationSection";
+export { FpsMultiplierControl } from "./FpsMultiplierControl";
+export { UsageInstructions } from "./UsageInstructions";
+export { SmartClipboardButton } from "./SmartClipboardButton";
+export { FgmodClipboardButton } from "./FgmodClipboardButton";
+export { NerdStuffModal } from "./NerdStuffModal";
+export { default as FlatpaksModal } from "./FlatpaksModal";
+export { ProfileManagement } from "./ProfileManagement";