Initial commit

This commit is contained in:
JSON Derulo
2025-01-21 18:52:51 -05:00
committed by GitHub
commit c1ebbc4b02
24 changed files with 2703 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
*.swp
pids
logs
results
tmp
# Coverage reports
coverage
# API keys and secrets
.env
# Dependency directory
node_modules
bower_components
.pnpm-store
# Editors
.idea
*.iml
# OS metadata
.DS_Store
Thumbs.db
# Ignore built ts files
dist/
__pycache__/
/.yalc
yalc.lock
.vscode/settings.json
# Ignore output folder
backend/out
# Make sure to ignore any instance of the loader's decky_plugin.py
decky_plugin.py
# Ignore decky CLI for building plugins
out
out/*
cli/
cli/*
cli/decky
Vendored Executable
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
CLI_LOCATION="$(pwd)/cli"
echo "Building plugin in $(pwd)"
printf "Please input sudo password to proceed.\n"
# read -s sudopass
# printf "\n"
echo $sudopass | sudo -E $CLI_LOCATION/decky plugin build $(pwd)
Vendored Executable
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
# printf "${SCRIPT_DIR}\n"
# printf "$(dirname $0)\n"
if ! [[ -e "${SCRIPT_DIR}/settings.json" ]]; then
printf '.vscode/settings.json does not exist. Creating it with default settings. Exiting afterwards. Run your task again.\n\n'
cp "${SCRIPT_DIR}/defsettings.json" "${SCRIPT_DIR}/settings.json"
exit 1
else
printf '.vscode/settings.json does exist. Congrats.\n'
printf 'Make sure to change settings.json to match your deck.\n'
fi
+12
View File
@@ -0,0 +1,12 @@
{
"deckip" : "steamdeck.local",
"deckport" : "22",
"deckuser" : "deck",
"deckpass" : "ssap",
"deckkey" : "-i ${env:HOME}/.ssh/id_rsa",
"deckdir" : "/home/deck",
"pluginname": "Example Plugin",
"python.analysis.extraPaths": [
"./py_modules"
]
}
Vendored Executable
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
PNPM_INSTALLED="$(which pnpm)"
DOCKER_INSTALLED="$(which docker)"
CLI_INSTALLED="$(pwd)/cli/decky"
# echo "$PNPM_INSTALLED"
# echo "$DOCKER_INSTALLED"
# echo "$CLI_INSTALLED"
echo "If you are using alpine linux, do not expect any support."
if [[ "$PNPM_INSTALLED" =~ "which" ]]; then
echo "pnpm is not currently installed, you can install it via your distro's package managment system or via a script that will attempt to do a manual install based on your system. If you wish to proceed with installing via the script then answer "no" (capitals do not matter) and proceed with the rest of the script. Otherwise, just hit enter to proceed and use the script."
read run_pnpm_script
if [[ "$run_pnpm_script" =~ "n" ]]; then
echo "You have chose to install pnpm via npm or your distros package manager. Please make sure to do so before attempting to build your plugin."
else
CURL_INSTALLED="$(which curl)"
WGET_INSTALLED="$(which wget)"
if [[ "$CURL_INSTALLED" =~ "which" ]]; then
printf "curl not found, attempting with wget.\n"
if [[ "$WGET_INSTALLED" =~ "which" ]]; then
printf "wget not found, please install wget or curl.\n"
printf "Could not install pnpm as curl and wget were not found.\n"
else
wget -qO- https://get.pnpm.io/install.sh | sh -
fi
else
curl -fsSL https://get.pnpm.io/install.sh | sh -
fi
fi
fi
if [[ "$DOCKER_INSTALLED" =~ "which" ]]; then
echo "Docker is not currently installed, in order build plugins with a backend you will need to have Docker installed. Please install Docker via the preferred method for your distribution."
fi
if ! test -f "$CLI_INSTALLED"; then
echo "The Decky CLI tool (binary file is just called "decky") is used to build your plugin as a zip file which you can then install on your Steam Deck to perform testing. We highly recommend you install it. Hitting enter now will run the script to install Decky CLI and extract it to a folder called cli in the current plugin directory. You can also type 'no' and hit enter to skip this but keep in mind you will not have a usable plugin without building it."
read run_cli_script
if [[ "$run_cli_script" =~ "n" ]]; then
echo "You have chosen to not install the Decky CLI tool to build your plugins. Please install this tool to build and test your plugin before submitting it to the Plugin Database."
else
SYSTEM_ARCH="$(uname -a)"
mkdir "$(pwd)"/cli
if [[ "$SYSTEM_ARCH" =~ "x86_64" ]]; then
if [[ "$SYSTEM_ARCH" =~ "Linux" ]]; then
curl -L -o "$(pwd)"/cli/decky "https://github.com/SteamDeckHomebrew/cli/releases/latest/download/decky-linux-x86_64"
fi
if [[ "$SYSTEM_ARCH" =~ "Darwin" ]]; then
curl -L -o "$(pwd)"/cli/decky "https://github.com/SteamDeckHomebrew/cli/releases/latest/download/decky-macOS-x86_64"
fi
else
echo "System Arch not found! The only supported systems are Linux x86_64 and Apple x86_64/ARM64, not $SYSTEM_ARCH"
fi
if [[ "$SYSTEM_ARCH" =~ "arm64" ]]; then
curl -L -o "$(pwd)"/cli/decky "https://github.com/SteamDeckHomebrew/cli/releases/latest/download/decky-macOS-aarch64"
fi
chmod +x "$(pwd)"/cli/decky
echo "Decky CLI tool is now installed and you can build plugins into easy zip files using the "Build Zip" Task in vscodium."
fi
fi
+142
View File
@@ -0,0 +1,142 @@
{
"version": "2.0.0",
"tasks": [
//PRELIMINARY SETUP TASKS
//Dependency setup task
{
"label": "depsetup",
"type": "shell",
"group": "none",
"detail": "Install depedencies for basic setup",
"command": "${workspaceFolder}/.vscode/setup.sh",
// // placeholder for windows scripts, not currently planned
// "windows": {
// "command": "call -c ${workspaceFolder}\\.vscode\\setup.bat",
// },
"problemMatcher": []
},
//pnpm setup task to grab all needed modules
{
"label": "pnpmsetup",
"type": "shell",
"group": "none",
"detail": "Setup pnpm",
"command": "which pnpm && pnpm i",
"problemMatcher": []
},
//Preliminary "All-in-one" setup task
{
"label": "setup",
"detail": "Set up depedencies, pnpm and update Decky Frontend Library.",
"dependsOrder": "sequence",
"dependsOn": [
"depsetup",
"pnpmsetup",
"updatefrontendlib"
],
"problemMatcher": []
},
//Preliminary Deploy Config Setup
{
"label": "settingscheck",
"type": "shell",
"group": "none",
"detail": "Check that settings.json has been created",
"command": "${workspaceFolder}/.vscode/config.sh",
// // placeholder for windows scripts, not currently planned
// "windows": {
// "command": "call ${workspaceFolder}\\.vscode\\config.bat",
// },
"problemMatcher": []
},
//BUILD TASKS
{
"label": "cli-build",
"group": "build",
"detail": "Build plugin with CLI",
"command": "${workspaceFolder}/.vscode/build.sh",
// // placeholder for windows logic, not currently planned
// "windows": {
// "command": "call ${workspaceFolder}\\.vscode\\build.bat",
// },
"problemMatcher": []
},
//"All-in-one" build task
{
"label": "build",
"group": "build",
"detail": "Build decky-plugin-template",
"dependsOrder": "sequence",
"dependsOn": [
"setup",
"settingscheck",
"cli-build",
],
"problemMatcher": []
},
//DEPLOY TASKS
//Copies the zip file of the built plugin to the plugins folder
{
"label": "copyzip",
"detail": "Deploy plugin zip to deck",
"type": "shell",
"group": "none",
"dependsOn": [
"chmodplugins"
],
"command": "rsync -azp --chmod=D0755,F0755 --rsh='ssh -p ${config:deckport} ${config:deckkey}' out/ ${config:deckuser}@${config:deckip}:${config:deckdir}/homebrew/plugins",
"problemMatcher": []
},
//
{
"label": "extractzip",
"detail": "",
"type": "shell",
"group": "none",
"command": "echo '${config:deckdir}/homebrew/plugins/${config:pluginname}.zip' && ssh ${config:deckuser}@${config:deckip} -p ${config:deckport} ${config:deckkey} 'echo ${config:deckpass} | sudo -S mkdir 755 -p \"$(echo \"${config:deckdir}/homebrew/plugins/${config:pluginname}\" | sed \"s| |-|g\")\" && echo ${config:deckpass} | sudo -S chown ${config:deckuser}:${config:deckuser} \"$(echo \"${config:deckdir}/homebrew/plugins/${config:pluginname}\" | sed \"s| |-|g\")\" && echo ${config:deckpass} | sudo -S bsdtar -xzpf \"${config:deckdir}/homebrew/plugins/${config:pluginname}.zip\" -C \"$(echo \"${config:deckdir}/homebrew/plugins/${config:pluginname}\" | sed \"s| |-|g\")\" --strip-components=1 --fflags '",
"problemMatcher": []
},
//"All-in-one" deploy task
{
"label": "deploy",
"dependsOrder": "sequence",
"group": "none",
"dependsOn": [
"copyzip",
"extractzip"
],
"problemMatcher": []
},
//"All-in-on" build & deploy task
{
"label": "builddeploy",
"detail": "Builds plugin and deploys to deck",
"dependsOrder": "sequence",
"group": "none",
"dependsOn": [
"build",
"deploy"
],
"problemMatcher": []
},
//GENERAL TASKS
//Update Decky Frontend Library, aka DFL
{
"label": "updatefrontendlib",
"type": "shell",
"group": "build",
"detail": "Update @decky/ui aka DFL",
"command": "pnpm update @decky/ui --latest",
"problemMatcher": []
},
//Used chmod plugins folder to allow copy-over of files
{
"label": "chmodplugins",
"detail": "chmods plugins folder to prevent perms issues",
"type": "shell",
"group": "none",
"command": "ssh ${config:deckuser}@${config:deckip} -p ${config:deckport} ${config:deckkey} 'echo '${config:deckpass}' | sudo -S chmod -R ug+rw ${config:deckdir}/homebrew/plugins/'",
"problemMatcher": []
},
]
}
+31
View File
@@ -0,0 +1,31 @@
BSD 3-Clause License
Copyright (c) 2024, Hypothetical Plugin Developer
Original Copyright (c) 2022-2024, Steam Deck Homebrew
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+101
View File
@@ -0,0 +1,101 @@
# Decky Plugin Template [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://deckbrew.xyz/discord)
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.
### **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.**
## Developers
### Dependencies
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.
#### Linux
```bash
sudo npm i -g pnpm@9
```
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.
### 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.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+9
View File
@@ -0,0 +1,9 @@
# we support images for building with a vanilla SteamOS base,
# or versions with ootb support for rust or go
# developers can also customize these images via this Dockerfile
#FROM ghcr.io/steamdeckhomebrew/holo-toolchain-rust:latest
#FROM ghcr.io/steamdeckhomebrew/holo-toolchain-go:latest
FROM ghcr.io/steamdeckhomebrew/holo-base:latest
# entrypoint.sh should always be located in the backend folder
ENTRYPOINT [ "/backend/entrypoint.sh" ]
+14
View File
@@ -0,0 +1,14 @@
# This is the default target, which will be built when
# you invoke make
.PHONY: all
all: hello
# This rule tells make how to build hello from hello.cpp
hello:
mkdir -p ./out
gcc -o ./out/hello ./src/main.c
# This rule tells make to delete hello and hello.o
.PHONY: clean
clean:
rm -f hello
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
set -e
echo "Container's IP address: `awk 'END{print $1}' /etc/hosts`"
cd /backend
make
+5
View File
@@ -0,0 +1,5 @@
#include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
+184
View File
@@ -0,0 +1,184 @@
"""
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.
"""
+13
View File
@@ -0,0 +1,13 @@
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
+57
View File
@@ -0,0 +1,57 @@
import os
# The decky plugin module is located at decky-loader/plugin
# For easy intellisense checkout the decky-loader code repo
# and add the `decky-loader/plugin/imports` path to `python.analysis.extraPaths` in `.vscode/settings.json`
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 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)
# 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!")
# 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!")
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!")
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")
# Here's a migration example for logs:
# - `~/.config/decky-template/template.log` will be migrated to `decky.decky_LOG_DIR/template.log`
decky.migrate_logs(os.path.join(decky.DECKY_USER_HOME,
".config", "decky-template", "template.log"))
# Here's a migration example for settings:
# - `~/homebrew/settings/template.json` is migrated to `decky.decky_SETTINGS_DIR/template.json`
# - `~/.config/decky-template/` all files and directories under this root are migrated to `decky.decky_SETTINGS_DIR/`
decky.migrate_settings(
os.path.join(decky.DECKY_HOME, "settings", "template.json"),
os.path.join(decky.DECKY_USER_HOME, ".config", "decky-template"))
# Here's a migration example for runtime data:
# - `~/homebrew/template/` all files and directories under this root are migrated to `decky.decky_RUNTIME_DIR/`
# - `~/.local/share/decky-template/` all files and directories under this root are migrated to `decky.decky_RUNTIME_DIR/`
decky.migrate_runtime(
os.path.join(decky.DECKY_HOME, "template"),
os.path.join(decky.DECKY_USER_HOME, ".local", "share", "decky-template"))
+50
View File
@@ -0,0 +1,50 @@
{
"name": "decky-plugin-template",
"version": "0.0.1",
"description": "A template to quickly create decky plugins from scratch, based on TypeScript and webpack",
"type": "module",
"scripts": {
"build": "rollup -c",
"watch": "rollup -c -w",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/SteamDeckHomebrew/decky-plugin-template.git"
},
"keywords": [
"decky",
"plugin",
"plugin-template",
"steam-deck",
"deck"
],
"author": "You <you@you.tld>",
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/SteamDeckHomebrew/decky-plugin-template/issues"
},
"homepage": "https://github.com/SteamDeckHomebrew/decky-plugin-template#readme",
"devDependencies": {
"@decky/rollup": "^1.0.1",
"@decky/ui": "^4.7.2",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"@types/webpack": "^5.28.5",
"rollup": "^4.22.5",
"typescript": "^5.6.2"
},
"dependencies": {
"@decky/api": "^1.1.2",
"react-icons": "^5.3.0",
"tslib": "^2.7.0"
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"react",
"react-dom"
]
}
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"name": "Example Plugin",
"author": "John Doe",
"flags": ["debug", "_root"],
"api_version": 1,
"publish": {
"tags": ["template", "root"],
"description": "Decky example plugin.",
"image": "https://opengraph.githubassets.com/1/SteamDeckHomebrew/PluginLoader"
}
}
+1763
View File
File diff suppressed because it is too large Load Diff
View File
+5
View File
@@ -0,0 +1,5 @@
import deckyPlugin from "@decky/rollup";
export default deckyPlugin({
// Add your extra Rollup options here
})
Executable
+115
View File
@@ -0,0 +1,115 @@
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 logo from "../assets/logo.png";
// 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");
// 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 Content() {
const [result, setResult] = useState<number | undefined>();
const onClick = async () => {
const result = await add(Math.random(), Math.random());
setResult(result);
};
return (
<PanelSection title="Panel Section">
<PanelSectionRow>
<ButtonItem
layout="below"
onClick={onClick}
>
{result ?? "Add two numbers via Python"}
</ButtonItem>
</PanelSectionRow>
<PanelSectionRow>
<ButtonItem
layout="below"
onClick={() => startTimer()}
>
{"Start Python timer"}
</ButtonItem>
</PanelSectionRow>
{/* <PanelSectionRow>
<div style={{ display: "flex", justifyContent: "center" }}>
<img src={logo} />
</div>
</PanelSectionRow> */}
{/*<PanelSectionRow>
<ButtonItem
layout="below"
onClick={() => {
Navigation.Navigate("/decky-plugin-test");
Navigation.CloseSideMenus();
}}
>
Router
</ButtonItem>
</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}`
});
});
return {
// The name shown in various decky menus
name: "Test Plugin",
// The element displayed at the top of your plugin's menu
titleView: <div className={staticClasses.Title}>Decky Example Plugin</div>,
// The content of your plugin's menu
content: <Content />,
// The icon displayed in the plugin list
icon: <FaShip />,
// The function triggered when your plugin unloads
onDismount() {
console.log("Unloading")
removeEventListener("timer_event", listener);
// serverApi.routerHook.removeRoute("/decky-plugin-test");
},
};
});
+14
View File
@@ -0,0 +1,14 @@
declare module "*.svg" {
const content: string;
export default content;
}
declare module "*.png" {
const content: string;
export default content;
}
declare module "*.jpg" {
const content: string;
export default content;
}
+22
View File
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"outDir": "dist",
"module": "ESNext",
"target": "ES2020",
"jsx": "react",
"jsxFactory": "window.SP_REACT.createElement",
"jsxFragmentFactory": "window.SP_REACT.Fragment",
"declaration": false,
"moduleResolution": "node",
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strict": true,
"allowSyntheticDefaultImports": true
},
"include": ["src"],
"exclude": ["node_modules"]
}