diff options
| -rw-r--r-- | README.md | 110 | ||||
| -rw-r--r-- | assets/image.png | bin | 0 -> 1232759 bytes | |||
| -rw-r--r-- | assets/lsfg-vk.png | bin | 0 -> 1779289 bytes | |||
| -rw-r--r-- | justfile | 11 | ||||
| -rw-r--r-- | main.py | 180 | ||||
| -rw-r--r-- | package.json | 9 | ||||
| -rw-r--r-- | plugin.json | 10 | ||||
| -rw-r--r-- | py_modules/.keep | 0 | ||||
| -rwxr-xr-x | src/index.tsx | 256 |
9 files changed, 396 insertions, 180 deletions
@@ -1,101 +1,27 @@ -# Decky Plugin Template [](https://deckbrew.xyz/discord) +# Lossless Scaling for Steam Deck +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 app on Linux. -Reference example for using [decky-frontend-lib](https://github.com/SteamDeckHomebrew/decky-frontend-lib) (@decky/ui) in a [decky-loader](https://github.com/SteamDeckHomebrew/decky-loader) plugin. +## What is this? -### **Please also refer to the [wiki](https://wiki.deckbrew.xyz/en/user-guide/home#plugin-development) for important information on plugin development and submissions/updates. currently documentation is split between this README and the wiki which is something we are hoping to rectify in the future.** +This plugin automates the installation of lsfg-vk, a compatibility layer that allows the Windows-only [Lossless Scaling](https://store.steampowered.com/app/993090/Lossless_Scaling/) app to work on Linux systems like Steam Deck. Lossless Scaling provides frame generation and upscaling features for games. -## Developers + -### Dependencies +## How to Use -This template relies on the user having Node.js v16.14+ and `pnpm` (v9) installed on their system. -Please make sure to install pnpm v9 to prevent issues with CI during plugin submission. -`pnpm` can be downloaded from `npm` itself which is recommended. +1. **Install the plugin** through the Decky Plugin Store (TBD) +2. **Purchase and install** [Lossless Scaling](https://store.steampowered.com/app/993090/Lossless_Scaling/) from Steam +3. **Open the plugin** from the Decky menu +4. **Click "Install lsfg-vk"** to automatically set up the compatibility layer +5. **Apply launch commands** to the game you want to use frame generation with. The plugin provides a short guide for these commands in its interface. -#### Linux +## What it does -```bash -sudo npm i -g pnpm@9 -``` +The plugin: +- Extracts the lsfg-vk library to `~/.local/lib/` +- Installs the Vulkan layer configuration to `~/.local/share/vulkan/implicit_layer.d/` +- Provides easy uninstallation by removing these files when no longer needed -If you would like to build plugins that have their own custom backends, Docker is required as it is used by the Decky CLI tool. +## Credits -### Making your own plugin - -1. You can fork this repo or utilize the "Use this template" button on Github. -2. In your local fork/own plugin-repository run these commands: - 1. ``pnpm i`` - 2. ``pnpm run build`` - - These setup pnpm and build the frontend code for testing. -3. Consult the [decky-frontend-lib](https://github.com/SteamDeckHomebrew/decky-frontend-lib) repository for ways to accomplish your tasks. - - Documentation and examples are still rough, - - Decky loader primarily targets Steam Deck hardware so keep this in mind when developing your plugin. -4. If using VSCodium/VSCode, run the `setup` and `build` and `deploy` tasks. If not using VSCodium etc. you can derive your own makefile or just manually utilize the scripts for these commands as you see fit. - -If you use VSCode or it's derivatives (we suggest [VSCodium](https://vscodium.com/)!) just run the `setup` and `build` tasks. It's really that simple. - -#### Other important information - -Everytime you change the frontend code (`index.tsx` etc) you will need to rebuild using the commands from step 2 above or the build task if you're using vscode or a derivative. - -Note: If you are receiving build errors due to an out of date library, you should run this command inside of your repository: - -```bash -pnpm update @decky/ui --latest -``` - -### Backend support - -If you are developing with a backend for a plugin and would like to submit it to the [decky-plugin-database](https://github.com/SteamDeckHomebrew/decky-plugin-database) you will need to have all backend code located in ``backend/src``, with backend being located in the root of your git repository. -When building your plugin, the source code will be built and any finished binary or binaries will be output to ``backend/out`` (which is created during CI.) -If your buildscript, makefile or any other build method does not place the binary files in the ``backend/out`` directory they will not be properly picked up during CI and your plugin will not have the required binaries included for distribution. - -Example: -In our makefile used to demonstrate the CI process of building and distributing a plugin backend, note that the makefile explicitly creates the `out` folder (``backend/out``) and then compiles the binary into that folder. Here's the relevant snippet. - -```make -hello: - mkdir -p ./out - gcc -o ./out/hello ./src/main.c -``` - -The CI does create the `out` folder itself but we recommend creating it yourself if possible during your build process to ensure the build process goes smoothly. - -Note: When locally building your plugin it will be placed into a folder called 'out' this is different from the concept described above. - -The out folder is not sent to the final plugin, but is then put into a ``bin`` folder which is found at the root of the plugin's directory. -More information on the bin folder can be found below in the distribution section below. - -### Distribution - -We recommend following the instructions found in the [decky-plugin-database](https://github.com/SteamDeckHomebrew/decky-plugin-database) on how to get your plugin up on the plugin store. This is the best way to get your plugin in front of users. -You can also choose to do distribution via a zip file containing the needed files, if that zip file is uploaded to a URL it can then be downloaded and installed via decky-loader. - -**NOTE: We do not currently have a method to install from a downloaded zip file in "game-mode" due to lack of a usable file-picking dialog.** - -Layout of a plugin zip ready for distribution: -``` -pluginname-v1.0.0.zip (version number is optional but recommended for users sake) - | - pluginname/ <directory> - | | | - | | bin/ <directory> (optional) - | | | - | | binary (optional) - | | - | dist/ <directory> [required] - | | - | index.js [required] - | - package.json [required] - plugin.json [required] - main.py {required if you are using the python backend of decky-loader: serverAPI} - README.md (optional but recommended) - LICENSE(.md) [required, filename should be roughly similar, suffix not needed] -``` - -Note regarding licenses: Including a license is required for the plugin store if your chosen license requires the license to be included alongside usage of source-code/binaries! - -Standard procedure for licenses is to have your chosen license at the top of the file, and to leave the original license for the plugin-template at the bottom. If this is not the case on submission to the plugin database, you will be asked to fix this discrepancy. - -We cannot and will not distribute your plugin on the Plugin Store if it's license requires it's inclusion but you have not included a license to be re-distributed with your plugin in the root of your git repository. +[PancakeTAS](https://github.com/PancakeTAS/lsfg-vk) for creating the lsfg-vk compatibility layer.
\ No newline at end of file diff --git a/assets/image.png b/assets/image.png Binary files differnew file mode 100644 index 0000000..51680a3 --- /dev/null +++ b/assets/image.png diff --git a/assets/lsfg-vk.png b/assets/lsfg-vk.png Binary files differnew file mode 100644 index 0000000..0395798 --- /dev/null +++ b/assets/lsfg-vk.png diff --git a/justfile b/justfile new file mode 100644 index 0000000..e108534 --- /dev/null +++ b/justfile @@ -0,0 +1,11 @@ +default: + echo "Available recipes: build, test, clean" + +build: + rm -rf node_modules && .vscode/build.sh + +test: + scp "/Users/kurt/Developer/decky-plugin-template/out/lsfg-vk Installer.zip" deck@192.168.0.6:~/Desktop + +clean: + rm -rf node_modules dist
\ No newline at end of file @@ -1,4 +1,8 @@ import os +import tarfile +import shutil +import subprocess +import tempfile # The decky plugin module is located at decky-loader/plugin # For easy intellisense checkout the decky-loader code repo @@ -7,35 +11,183 @@ import decky import asyncio class Plugin: - # A normal method. It can be called from the TypeScript side using @decky/api. - async def add(self, left: int, right: int) -> int: - return left + right + async def install_lsfg_vk(self) -> dict: + """Install lsfg-vk by extracting the tar.gz file to ~/.local""" + try: + # Get the path to the lsfg-vk-latest.tar.gz file in the bin directory + plugin_dir = os.path.dirname(os.path.realpath(__file__)) + tar_path = os.path.join(plugin_dir, "bin", "lsfg-vk-latest.tar.gz") + + # Check if the tar.gz file exists + if not os.path.exists(tar_path): + decky.logger.error(f"lsfg-vk-latest.tar.gz not found at {tar_path}") + return {"success": False, "error": "lsfg-vk-latest.tar.gz file not found"} + + # Get the user's home directory + user_home = os.path.expanduser("~") + local_lib_dir = os.path.join(user_home, ".local", "lib") + local_share_dir = os.path.join(user_home, ".local", "share", "vulkan", "implicit_layer.d") + + # Create directories if they don't exist + os.makedirs(local_lib_dir, exist_ok=True) + os.makedirs(local_share_dir, exist_ok=True) + + # Extract the tar.gz file + with tarfile.open(tar_path, 'r:gz') as tar_ref: + # Use /tmp for temporary extraction since we may not have write permissions in plugin dir + with tempfile.TemporaryDirectory() as temp_dir: + tar_ref.extractall(temp_dir) + + # Look for the extracted files and copy them to the correct locations + for root, dirs, files in os.walk(temp_dir): + for file in files: + src_file = os.path.join(root, file) + if file.endswith('.so'): + # Copy library files to ~/.local/lib + dst_file = os.path.join(local_lib_dir, file) + shutil.copy2(src_file, dst_file) + decky.logger.info(f"Copied {file} to {dst_file}") + elif file.endswith('.json'): + # Copy JSON files to ~/.local/share/vulkan/implicit_layer.d + dst_file = os.path.join(local_share_dir, file) + shutil.copy2(src_file, dst_file) + decky.logger.info(f"Copied {file} to {dst_file}") + + # temp_dir will be automatically cleaned up + + decky.logger.info("lsfg-vk installed successfully") + return {"success": True, "message": "lsfg-vk installed successfully"} + + except Exception as e: + decky.logger.error(f"Error installing lsfg-vk: {str(e)}") + return {"success": False, "error": str(e)} - async def long_running(self): - await asyncio.sleep(15) - # Passing through a bunch of random data, just as an example - await decky.emit("timer_event", "Hello from the backend!", True, 2) + async def check_lsfg_vk_installed(self) -> dict: + """Check if lsfg-vk is already installed""" + try: + user_home = os.path.expanduser("~") + lib_file = os.path.join(user_home, ".local", "lib", "liblsfg-vk.so") + json_file = os.path.join(user_home, ".local", "share", "vulkan", "implicit_layer.d", "VkLayer_LS_frame_generation.json") + + lib_exists = os.path.exists(lib_file) + json_exists = os.path.exists(json_file) + + return { + "installed": lib_exists and json_exists, + "lib_exists": lib_exists, + "json_exists": json_exists, + "lib_path": lib_file, + "json_path": json_file + } + + except Exception as e: + decky.logger.error(f"Error checking lsfg-vk installation: {str(e)}") + return {"installed": False, "error": str(e)} + + async def uninstall_lsfg_vk(self) -> dict: + """Uninstall lsfg-vk by removing the installed files""" + try: + user_home = os.path.expanduser("~") + lib_file = os.path.join(user_home, ".local", "lib", "liblsfg-vk.so") + json_file = os.path.join(user_home, ".local", "share", "vulkan", "implicit_layer.d", "VkLayer_LS_frame_generation.json") + + removed_files = [] + + # Remove library file if it exists + if os.path.exists(lib_file): + os.remove(lib_file) + removed_files.append(lib_file) + decky.logger.info(f"Removed {lib_file}") + + # Remove JSON file if it exists + if os.path.exists(json_file): + os.remove(json_file) + removed_files.append(json_file) + decky.logger.info(f"Removed {json_file}") + + if not removed_files: + return {"success": True, "message": "No lsfg-vk files found to remove"} + + decky.logger.info("lsfg-vk uninstalled successfully") + return { + "success": True, + "message": f"lsfg-vk uninstalled successfully. Removed {len(removed_files)} files.", + "removed_files": removed_files + } + + except Exception as e: + decky.logger.error(f"Error uninstalling lsfg-vk: {str(e)}") + return {"success": False, "error": str(e)} + + async def check_lossless_scaling_dll(self) -> dict: + """Check if Lossless Scaling DLL is available at the expected paths""" + try: + # Check environment variable first + dll_path = os.getenv("LSFG_DLL_PATH") + if dll_path and dll_path.strip(): + dll_path_str = dll_path.strip() + if os.path.exists(dll_path_str): + return { + "detected": True, + "path": dll_path_str, + "source": "LSFG_DLL_PATH environment variable" + } + + # Check XDG_DATA_HOME path + data_dir = os.getenv("XDG_DATA_HOME") + if data_dir and data_dir.strip(): + dll_path_str = os.path.join(data_dir.strip(), "Steam", "steamapps", "common", "Lossless Scaling", "Lossless.dll") + if os.path.exists(dll_path_str): + return { + "detected": True, + "path": dll_path_str, + "source": "XDG_DATA_HOME Steam directory" + } + + # Check HOME/.local/share path + home_dir = os.getenv("HOME") + if home_dir and home_dir.strip(): + dll_path_str = os.path.join(home_dir.strip(), ".local", "share", "Steam", "steamapps", "common", "Lossless Scaling", "Lossless.dll") + if os.path.exists(dll_path_str): + return { + "detected": True, + "path": dll_path_str, + "source": "HOME/.local/share Steam directory" + } + + # DLL not found in any expected location + return { + "detected": False, + "path": None, + "source": None, + "message": "Lossless Scaling DLL not found in expected locations" + } + + except Exception as e: + decky.logger.error(f"Error checking Lossless Scaling DLL: {str(e)}") + return { + "detected": False, + "path": None, + "source": None, + "error": str(e) + } # Asyncio-compatible long-running code, executed in a task when the plugin is loaded async def _main(self): - self.loop = asyncio.get_event_loop() - decky.logger.info("Hello World!") + decky.logger.info("lsfg-vk Installer loaded!") # Function called first during the unload process, utilize this to handle your plugin being stopped, but not # completely removed async def _unload(self): - decky.logger.info("Goodnight World!") + decky.logger.info("lsfg-vk Installer unloading") pass # Function called after `_unload` during uninstall, utilize this to clean up processes and other remnants of your # plugin that may remain on the system async def _uninstall(self): - decky.logger.info("Goodbye World!") + decky.logger.info("lsfg-vk Installer uninstalled") pass - async def start_timer(self): - self.loop.create_task(self.long_running()) - # Migrations that should be performed before entering `_main()`. async def _migration(self): decky.logger.info("Migrating") diff --git a/package.json b/package.json index 9501ba3..6b60965 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,15 @@ "react-icons": "^5.3.0", "tslib": "^2.7.0" }, + "remote_binary_bundling" : true, + "remote_binary": + [ + { + "name": "lsfg-vk-latest.tar.gz", + "url": "https://github.com/xXJSONDeruloXx/lsfg-vk/releases/download/latest/lsfg-vk-latest.tar.gz", + "sha256hash": "913e7bba9f1b58339df0b03cd9a8c4dd8d165912224a869b9ee8dd983ab8f3f7" + } + ], "pnpm": { "peerDependencyRules": { "ignoreMissing": [ diff --git a/plugin.json b/plugin.json index 568bded..1873005 100644 --- a/plugin.json +++ b/plugin.json @@ -1,11 +1,11 @@ { - "name": "Example Plugin", - "author": "John Doe", + "name": "Lossless Scaling", + "author": "Kurt Himebauch", "flags": ["debug", "_root"], "api_version": 1, "publish": { - "tags": ["template", "root"], - "description": "Decky example plugin.", - "image": "https://opengraph.githubassets.com/1/SteamDeckHomebrew/PluginLoader" + "tags": ["installer", "vulkan", "lsfg"], + "description": "Install lsfg-vk Vulkan layer for frame generation.", + "image": "https://github.com/xXJSONDeruloXx/decky-lossless-scaling/blob/main/assets/lsfg-vk.png?raw=true" } } diff --git a/py_modules/.keep b/py_modules/.keep deleted file mode 100644 index e69de29..0000000 --- a/py_modules/.keep +++ /dev/null diff --git a/src/index.tsx b/src/index.tsx index 16cd6cb..1ab6f62 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,114 +2,232 @@ import { ButtonItem, PanelSection, PanelSectionRow, - Navigation, staticClasses } from "@decky/ui"; import { - addEventListener, - removeEventListener, callable, definePlugin, toaster, - // routerHook } from "@decky/api" -import { useState } from "react"; -import { FaShip } from "react-icons/fa"; +import { useState, useEffect } from "react"; +import { FaDownload, FaTrash } from "react-icons/fa"; +import { GiPlasticDuck } from "react-icons/gi"; -// import logo from "../assets/logo.png"; +// Function to install lsfg-vk +const installLsfgVk = callable<[], { success: boolean; error?: string; message?: string }>("install_lsfg_vk"); -// This function calls the python function "add", which takes in two numbers and returns their sum (as a number) -// Note the type annotations: -// the first one: [first: number, second: number] is for the arguments -// the second one: number is for the return value -const add = callable<[first: number, second: number], number>("add"); +// Function to uninstall lsfg-vk +const uninstallLsfgVk = callable<[], { success: boolean; error?: string; message?: string; removed_files?: string[] }>("uninstall_lsfg_vk"); -// This function calls the python function "start_timer", which takes in no arguments and returns nothing. -// It starts a (python) timer which eventually emits the event 'timer_event' -const startTimer = callable<[], void>("start_timer"); +// Function to check if lsfg-vk is installed +const checkLsfgVkInstalled = callable<[], { installed: boolean; lib_exists: boolean; json_exists: boolean; lib_path: string; json_path: string; error?: string }>("check_lsfg_vk_installed"); + +// Function to check if Lossless Scaling DLL is available +const checkLosslessScalingDll = callable<[], { detected: boolean; path?: string; source?: string; message?: string; error?: string }>("check_lossless_scaling_dll"); function Content() { - const [result, setResult] = useState<number | undefined>(); + const [isInstalled, setIsInstalled] = useState<boolean>(false); + const [isInstalling, setIsInstalling] = useState<boolean>(false); + const [isUninstalling, setIsUninstalling] = useState<boolean>(false); + const [installationStatus, setInstallationStatus] = useState<string>(""); + const [dllDetected, setDllDetected] = useState<boolean>(false); + const [dllDetectionStatus, setDllDetectionStatus] = useState<string>(""); + + // Check installation status on component mount + useEffect(() => { + const checkInstallation = async () => { + try { + const status = await checkLsfgVkInstalled(); + setIsInstalled(status.installed); + if (status.installed) { + setInstallationStatus("lsfg-vk is installed"); + } else { + setInstallationStatus("lsfg-vk is not installed"); + } + } catch (error) { + setInstallationStatus("Error checking installation status"); + } + }; + + const checkDllDetection = async () => { + try { + const result = await checkLosslessScalingDll(); + setDllDetected(result.detected); + if (result.detected) { + setDllDetectionStatus(`Lossless Scaling App detected (${result.source})`); + } else { + setDllDetectionStatus(result.message || "Lossless Scaling App not detected"); + } + } catch (error) { + setDllDetectionStatus("Error checking Lossless Scaling App"); + } + }; + + checkInstallation(); + checkDllDetection(); + }, []); + + const handleInstall = async () => { + setIsInstalling(true); + setInstallationStatus("Installing lsfg-vk..."); + + try { + const result = await installLsfgVk(); + if (result.success) { + setIsInstalled(true); + setInstallationStatus("lsfg-vk installed successfully!"); + toaster.toast({ + title: "Installation Complete", + body: "lsfg-vk has been installed successfully" + }); + } else { + setInstallationStatus(`Installation failed: ${result.error}`); + toaster.toast({ + title: "Installation Failed", + body: result.error || "Unknown error occurred" + }); + } + } catch (error) { + setInstallationStatus(`Installation failed: ${error}`); + toaster.toast({ + title: "Installation Failed", + body: `Error: ${error}` + }); + } finally { + setIsInstalling(false); + } + }; - const onClick = async () => { - const result = await add(Math.random(), Math.random()); - setResult(result); + const handleUninstall = async () => { + setIsUninstalling(true); + setInstallationStatus("Uninstalling lsfg-vk..."); + + try { + const result = await uninstallLsfgVk(); + if (result.success) { + setIsInstalled(false); + setInstallationStatus("lsfg-vk uninstalled successfully!"); + toaster.toast({ + title: "Uninstallation Complete", + body: result.message || "lsfg-vk has been uninstalled successfully" + }); + } else { + setInstallationStatus(`Uninstallation failed: ${result.error}`); + toaster.toast({ + title: "Uninstallation Failed", + body: result.error || "Unknown error occurred" + }); + } + } catch (error) { + setInstallationStatus(`Uninstallation failed: ${error}`); + toaster.toast({ + title: "Uninstallation Failed", + body: `Error: ${error}` + }); + } finally { + setIsUninstalling(false); + } }; return ( - <PanelSection title="Panel Section"> + <PanelSection title="lsfg-vk Installation"> <PanelSectionRow> - <ButtonItem - layout="below" - onClick={onClick} - > - {result ?? "Add two numbers via Python"} - </ButtonItem> + <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> + <PanelSectionRow> <ButtonItem layout="below" - onClick={() => startTimer()} + onClick={isInstalled ? handleUninstall : handleInstall} + disabled={isInstalling || isUninstalling} > - {"Start Python timer"} + {isInstalling ? ( + <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> + <div>Installing...</div> + </div> + ) : isUninstalling ? ( + <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> + <div>Uninstalling...</div> + </div> + ) : isInstalled ? ( + <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> + <FaTrash /> + <div>Uninstall lsfg-vk</div> + </div> + ) : ( + <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> + <FaDownload /> + <div>Install lsfg-vk</div> + </div> + )} </ButtonItem> </PanelSectionRow> - - {/* <PanelSectionRow> - <div style={{ display: "flex", justifyContent: "center" }}> - <img src={logo} /> + + <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" }}> + Add to your game's launch options in Steam: + </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=2 %COMMAND% + </div> + <div style={{ fontSize: "11px", opacity: 0.8 }}> + • ENABLE_LSFG=1 - Enables frame generation + <br /> + • LSFG_MULTIPLIER=2-4 - FPS multiplier (start with 2) + <br /> + • LSFG_FLOW_SCALE=1.0 - Flow scale (optional, for performance) + </div> </div> - </PanelSectionRow> */} - - {/*<PanelSectionRow> - <ButtonItem - layout="below" - onClick={() => { - Navigation.Navigate("/decky-plugin-test"); - Navigation.CloseSideMenus(); - }} - > - Router - </ButtonItem> - </PanelSectionRow>*/} + </PanelSectionRow> </PanelSection> ); }; export default definePlugin(() => { - console.log("Template plugin initializing, this is called once on frontend startup") - - // serverApi.routerHook.addRoute("/decky-plugin-test", DeckyPluginRouterTest, { - // exact: true, - // }); - - // Add an event listener to the "timer_event" event from the backend - const listener = addEventListener<[ - test1: string, - test2: boolean, - test3: number - ]>("timer_event", (test1, test2, test3) => { - console.log("Template got timer_event with:", test1, test2, test3) - toaster.toast({ - title: "template got timer_event", - body: `${test1}, ${test2}, ${test3}` - }); - }); + console.log("lsfg-vk Installer plugin initializing") return { // The name shown in various decky menus - name: "Test Plugin", + name: "lsfg-vk Installer", // The element displayed at the top of your plugin's menu - titleView: <div className={staticClasses.Title}>Decky Example Plugin</div>, + titleView: <div className={staticClasses.Title}>lsfg-vk Installer</div>, // The content of your plugin's menu content: <Content />, // The icon displayed in the plugin list - icon: <FaShip />, + icon: <GiPlasticDuck />, // The function triggered when your plugin unloads onDismount() { - console.log("Unloading") - removeEventListener("timer_event", listener); - // serverApi.routerHook.removeRoute("/decky-plugin-test"); + console.log("lsfg-vk Installer unloading") }, }; }); |
