mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-13 04:05:04 +03:00
Implement React-based plugin store (#81)
Co-authored-by: TrainDoctor <11465594+TrainDoctor@users.noreply.github.com> Co-authored-by: WerWolv <werwolv98@gmail.com>
This commit is contained in:
+6
-2
@@ -154,5 +154,9 @@ cython_debug/
|
|||||||
# static files are built
|
# static files are built
|
||||||
backend/static
|
backend/static
|
||||||
|
|
||||||
# pnpm lockfile
|
# ignore settings.json
|
||||||
frontend/pnpm-lock.yaml
|
# prevents leaking login details
|
||||||
|
.vscode/settings.json
|
||||||
|
|
||||||
|
# plugins folder for local launches
|
||||||
|
plugins/*
|
||||||
+12
@@ -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
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"deckip" : "0.0.0.0",
|
||||||
|
"deckport" : "22",
|
||||||
|
"deckpass" : "ssap",
|
||||||
|
"deckkey" : "-i ${env:HOME}/.ssh/id_rsa",
|
||||||
|
"deckdir" : "/home/deck"
|
||||||
|
}
|
||||||
Vendored
+3
-3
@@ -2,16 +2,16 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "Debug",
|
"name": "Debug (Local)",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/backend/main.py",
|
"program": "${workspaceFolder}/backend/main.py",
|
||||||
"cwd": "${workspaceFolder}/backend",
|
"cwd": "${workspaceFolder}/backend",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"env": {
|
"env": {
|
||||||
"PLUGIN_PATH": "/home/deck/homebrew/plugins"
|
"PLUGIN_PATH": "${workspaceFolder}/plugins"
|
||||||
},
|
},
|
||||||
"preLaunchTask": "Build frontend"
|
"preLaunchTask": "localrun"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+124
-5
@@ -1,15 +1,134 @@
|
|||||||
{
|
{
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
|
// OTHER
|
||||||
{
|
{
|
||||||
"label": "Stop Service",
|
"label": "checkforsettings",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command":"systemctl --user stop plugin_loader",
|
"group": "none",
|
||||||
|
"detail": "Check that settings.json has been created",
|
||||||
|
"command": "bash -c ${workspaceFolder}/.vscode/config.sh",
|
||||||
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Build frontend",
|
"label": "localrun",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command":"cd ${workspaceFolder}/frontend; npm run build",
|
"group": "none",
|
||||||
}
|
"dependsOn" : ["buildall"],
|
||||||
|
"detail": "Check for local runs, create a plugins folder",
|
||||||
|
"command": "mkdir -p plugins",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "dependencies",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "none",
|
||||||
|
"detail": "Check for local runs, create a plugins folder",
|
||||||
|
"command": "rsync -azp --rsh='ssh -p ${config:deckport} ${config:deckkey}' requirements.txt deck@${config:deckip}:${config:deckdir}/homebrew/dev/pluginloader/requirements.txt && ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'python -m ensurepip && python -m pip install --upgrade pip && python -m pip install --upgrade setuptools && python -m pip install -r ${config:deckdir}/homebrew/dev/pluginloader/requirements.txt'",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
// BUILD
|
||||||
|
{
|
||||||
|
"label": "pnpmsetup",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "build",
|
||||||
|
"detail": "Setup pnpm",
|
||||||
|
"command": "cd frontend && pnpm i",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "buildfrontend",
|
||||||
|
"type": "npm",
|
||||||
|
"group": "build",
|
||||||
|
"detail": "rollup -c",
|
||||||
|
"script": "build",
|
||||||
|
"path": "frontend",
|
||||||
|
"problemMatcher": [],
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "buildall",
|
||||||
|
"group": "build",
|
||||||
|
"detail": "Deploy pluginloader to deck",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"dependsOn": [
|
||||||
|
"pnpmsetup",
|
||||||
|
"buildfrontend"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
// DEPLOY
|
||||||
|
{
|
||||||
|
"label": "createfolders",
|
||||||
|
"detail": "Create plugins folder in expected directory",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "none",
|
||||||
|
"dependsOn": [
|
||||||
|
"checkforsettings"
|
||||||
|
],
|
||||||
|
"command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'mkdir -p ${config:deckdir}/homebrew/dev/pluginloader && mkdir -p ${config:deckdir}/homebrew/dev/plugins'",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "deploy",
|
||||||
|
"detail": "Deploy dev PluginLoader to deck",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "none",
|
||||||
|
"command": "rsync -azp --delete --rsh='ssh -p ${config:deckport} ${config:deckkey}' --exclude='.git/' --exclude='.github/' --exclude='.vscode/' --exclude='frontend/' --exclude='dist/' --exclude='contrib/' --exclude='*.log' --exclude='requirements.txt' --exclude='backend/__pycache__/' --exclude='.gitignore' . deck@${config:deckip}:${config:deckdir}/homebrew/dev/pluginloader",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "deployall",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"group": "none",
|
||||||
|
"dependsOn": [
|
||||||
|
"createfolders",
|
||||||
|
"dependencies",
|
||||||
|
"deploy"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
// RUN
|
||||||
|
{
|
||||||
|
"label": "runpydeck",
|
||||||
|
"detail": "Run indev PluginLoader on Deck",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "none",
|
||||||
|
"dependsOn" : ["checkforsettings"],
|
||||||
|
"command": "ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'export PLUGIN_PATH=${config:deckdir}/homebrew/dev/plugins; export CHOWN_PLUGIN_PATH=0; echo '${config:deckpass}' | sudo -SE python3 ${config:deckdir}/homebrew/dev/pluginloader/backend/main.py'",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "runpylocal",
|
||||||
|
"detail": "Run PluginLoader from python locally",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "none",
|
||||||
|
"command": "export PLUGIN_PATH=${workspaceFolder}/plugins; export CHOWN_PLUGIN_PATH=0; sudo -E python3 ${workspaceFolder}/backend/main.py",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
// ALL-IN-ONES
|
||||||
|
{
|
||||||
|
"label": "updateremote",
|
||||||
|
"detail": "Build and deploy",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"group": "none",
|
||||||
|
"dependsOn": [
|
||||||
|
"buildall",
|
||||||
|
"deployall",
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "allinone",
|
||||||
|
"detail": "Build, deploy and run",
|
||||||
|
"dependsOrder": "sequence",
|
||||||
|
"group": "none",
|
||||||
|
"dependsOn": [
|
||||||
|
"buildall",
|
||||||
|
"deployall",
|
||||||
|
"runpydeck"
|
||||||
|
],
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-18
@@ -4,6 +4,8 @@
|
|||||||
## This script defaults to port 22 unless otherwise specified, and cannot run without a sudo password or LAN IP.
|
## This script defaults to port 22 unless otherwise specified, and cannot run without a sudo password or LAN IP.
|
||||||
## You will need to specify the path to the ssh key if using key connection exclusively.
|
## You will need to specify the path to the ssh key if using key connection exclusively.
|
||||||
|
|
||||||
|
## TODO: document latest changes to wiki
|
||||||
|
|
||||||
## Pre-parse arugments for ease of use
|
## Pre-parse arugments for ease of use
|
||||||
CLONEFOLDER=${1:-""}
|
CLONEFOLDER=${1:-""}
|
||||||
INSTALLFOLDER=${2:-""}
|
INSTALLFOLDER=${2:-""}
|
||||||
@@ -11,9 +13,13 @@ DECKIP=${3:-""}
|
|||||||
SSHPORT=${4:-""}
|
SSHPORT=${4:-""}
|
||||||
PASSWORD=${5:-""}
|
PASSWORD=${5:-""}
|
||||||
SSHKEYLOC=${6:-""}
|
SSHKEYLOC=${6:-""}
|
||||||
|
LOADERBRANCH=${7:-""}
|
||||||
|
LIBRARYBRANCH=${8:-""}
|
||||||
|
TEMPLATEBRANCH=${9:-""}
|
||||||
|
LATEST=${10:-""}
|
||||||
|
|
||||||
## gather options into an array
|
## gather options into an array
|
||||||
OPTIONSARRAY=("$CLONEFOLDER" $INSTALLFOLDER "$DECKIP" "$SSHPORT" "$PASSWORD" "$SSHKEYLOC")
|
OPTIONSARRAY=("$CLONEFOLDER" $INSTALLFOLDER "$DECKIP" "$SSHPORT" "$PASSWORD" "$SSHKEYLOC" "$LOADERBRANCH" "$LIBRARYBRANCH" "$TEMPLATEBRANCH" "$LATEST")
|
||||||
|
|
||||||
## iterate through options array to check their presence
|
## iterate through options array to check their presence
|
||||||
count=0
|
count=0
|
||||||
@@ -28,19 +34,21 @@ setfolder() {
|
|||||||
local DEFAULT="git"
|
local DEFAULT="git"
|
||||||
elif [[ "$2" == "install" ]]; then
|
elif [[ "$2" == "install" ]]; then
|
||||||
local ACTION="install"
|
local ACTION="install"
|
||||||
local DEFAULT="loaderdev"
|
local DEFAULT="dev"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf "Enter the directory in /home/user to ${ACTION} to.\n"
|
|
||||||
printf "Example: if your home directory is /home/user you would type: ${DEFAULT}\n"
|
|
||||||
printf "The ${ACTION} directory would be: ${HOME}/${DEFAULT}\n"
|
|
||||||
if [[ "$ACTION" == "clone" ]]; then
|
if [[ "$ACTION" == "clone" ]]; then
|
||||||
|
printf "Enter the directory in /home/user/ to ${ACTION} to.\n"
|
||||||
|
printf "The ${ACTION} directory would be: ${HOME}/${DEFAULT}\n"
|
||||||
read -p "Enter your ${ACTION} directory: " CLONEFOLDER
|
read -p "Enter your ${ACTION} directory: " CLONEFOLDER
|
||||||
if ! [[ "$CLONEFOLDER" =~ ^[[:alnum:]]+$ ]]; then
|
if ! [[ "$CLONEFOLDER" =~ ^[[:alnum:]]+$ ]]; then
|
||||||
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
|
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
|
||||||
CLONEFOLDER="${DEFAULT}"
|
CLONEFOLDER="${DEFAULT}"
|
||||||
fi
|
fi
|
||||||
elif [[ "$ACTION" == "install" ]]; then
|
elif [[ "$ACTION" == "install" ]]; then
|
||||||
|
printf "Enter the directory in /home/deck/homebrew to ${ACTION} pluginloader to.\n"
|
||||||
|
printf "The ${ACTION} directory would be: /home/deck/homebrew/${DEFAULT}/pluginloader\n"
|
||||||
|
printf "It is highly recommended that you use the default folder path seen above, just press enter at the next prompt.\n"
|
||||||
read -p "Enter your ${ACTION} directory: " INSTALLFOLDER
|
read -p "Enter your ${ACTION} directory: " INSTALLFOLDER
|
||||||
if ! [[ "$INSTALLFOLDER" =~ ^[[:alnum:]]+$ ]]; then
|
if ! [[ "$INSTALLFOLDER" =~ ^[[:alnum:]]+$ ]]; then
|
||||||
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
|
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
|
||||||
@@ -106,16 +114,46 @@ clonefromto() {
|
|||||||
# printf "repo=$1\n"
|
# printf "repo=$1\n"
|
||||||
# printf "outdir=$2\n"
|
# printf "outdir=$2\n"
|
||||||
# printf "branch=$3\n"
|
# printf "branch=$3\n"
|
||||||
if [[ -z $3 ]]; then
|
printf "Repository: $1\n"
|
||||||
BRANCH=""
|
git clone $1 $2 &> '/dev/null'
|
||||||
else
|
|
||||||
BRANCH="-b $3"
|
|
||||||
fi
|
|
||||||
git clone $1 $2 $BRANCH &> '/dev/null'
|
|
||||||
CODE=$?
|
CODE=$?
|
||||||
|
# printf "CODE=${CODE}"
|
||||||
if [[ $CODE -eq 128 ]]; then
|
if [[ $CODE -eq 128 ]]; then
|
||||||
cd $2
|
cd $2
|
||||||
git fetch &> '/dev/null'
|
git fetch --all &> '/dev/null'
|
||||||
|
fi
|
||||||
|
if [[ -z $3 ]]; then
|
||||||
|
printf "Enter the desired branch for repository "$1" :\n"
|
||||||
|
local OUT="$(git branch -r | sed '/\/HEAD/d')"
|
||||||
|
# $OUT="$($OUT > )"
|
||||||
|
printf "$OUT\nbranch: "
|
||||||
|
read BRANCH
|
||||||
|
else
|
||||||
|
printf "on branch: $3\n"
|
||||||
|
BRANCH="$3"
|
||||||
|
fi
|
||||||
|
if ! [[ -z ${BRANCH} ]]; then
|
||||||
|
git checkout $BRANCH &> '/dev/null'
|
||||||
|
fi
|
||||||
|
if [[ ${LATEST} == "true" ]]; then
|
||||||
|
git pull --all
|
||||||
|
elif [[ ${LATEST} == "true" ]]; then
|
||||||
|
printf "Assuming user not pulling latest commits.\n"
|
||||||
|
else
|
||||||
|
printf "Pull latest commits? (y/N): "
|
||||||
|
read PULL
|
||||||
|
case ${PULL:0:1} in
|
||||||
|
y|Y )
|
||||||
|
printf "Pulling latest commits.\n"
|
||||||
|
git pull --all
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
printf "Not pulling latest commits.\n"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if ! [[ "$PULL" =~ ^[[:alnum:]]+$ ]]; then
|
||||||
|
printf "Assuming user not pulling latest commits.\n"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +196,7 @@ if [[ "$INSTALLFOLDER" == "" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
CLONEDIR="$HOME/$CLONEFOLDER"
|
CLONEDIR="$HOME/$CLONEFOLDER"
|
||||||
INSTALLDIR="/home/deck/$INSTALLFOLDER"
|
INSTALLDIR="/home/deck/hombrew/$INSTALLFOLDER"
|
||||||
|
|
||||||
## Input ip address, port, password and sshkey
|
## Input ip address, port, password and sshkey
|
||||||
|
|
||||||
@@ -217,11 +255,11 @@ mkdir -p ${CLONEDIR} &> '/dev/null'
|
|||||||
# rm -r ${CLONEDIR}/pluginlibrary
|
# rm -r ${CLONEDIR}/pluginlibrary
|
||||||
# rm -r ${CLONEDIR}/plugintemplate
|
# rm -r ${CLONEDIR}/plugintemplate
|
||||||
|
|
||||||
clonefromto "https://github.com/SteamDeckHomebrew/PluginLoader" ${CLONEDIR}/pluginloader react-frontend-plugins
|
clonefromto "https://github.com/SteamDeckHomebrew/PluginLoader" ${CLONEDIR}/pluginloader "$LOADERBRANCH"
|
||||||
|
|
||||||
clonefromto "https://github.com/SteamDeckHomebrew/decky-frontend-lib" ${CLONEDIR}/pluginlibrary
|
clonefromto "https://github.com/SteamDeckHomebrew/decky-frontend-lib" ${CLONEDIR}/pluginlibrary "$LIBRARYBRANCH"
|
||||||
|
|
||||||
clonefromto "https://github.com/SteamDeckHomebrew/decky-plugin-template" ${CLONEDIR}/plugintemplate
|
clonefromto "https://github.com/SteamDeckHomebrew/decky-plugin-template" ${CLONEDIR}/plugintemplate "$TEMPLATEBRANCH"
|
||||||
|
|
||||||
## Transpile and bundle typescript
|
## Transpile and bundle typescript
|
||||||
|
|
||||||
@@ -271,7 +309,7 @@ printf "Run these commands to deploy your local changes to the deck:\n"
|
|||||||
printf "'rsync -avzp --mkpath --rsh=""\"ssh -p ${SSHPORT} ${IDENINVOC}\""" --exclude='.git/' --exclude='node_modules' --exclude='package-lock.json' --delete ${CLONEDIR}/pluginname deck@${DECKIP}:${INSTALLDIR}/plugins'\n"
|
printf "'rsync -avzp --mkpath --rsh=""\"ssh -p ${SSHPORT} ${IDENINVOC}\""" --exclude='.git/' --exclude='node_modules' --exclude='package-lock.json' --delete ${CLONEDIR}/pluginname deck@${DECKIP}:${INSTALLDIR}/plugins'\n"
|
||||||
printf "'rsync -avzp --mkpath --rsh=""\"ssh -p ${SSHPORT} ${IDENINVOC}\""" --exclude='.git/' --exclude='node_modules' --exclude='package-lock.json' --exclude=='frontend' --exclude='*dist*' --exclude='*contrib*' --delete ${CLONEDIR}/pluginloader/* deck@${DECKIP}:${INSTALLDIR}/pluginloader/'\n"
|
printf "'rsync -avzp --mkpath --rsh=""\"ssh -p ${SSHPORT} ${IDENINVOC}\""" --exclude='.git/' --exclude='node_modules' --exclude='package-lock.json' --exclude=='frontend' --exclude='*dist*' --exclude='*contrib*' --delete ${CLONEDIR}/pluginloader/* deck@${DECKIP}:${INSTALLDIR}/pluginloader/'\n"
|
||||||
|
|
||||||
printf "Run in console or in a script this command to run your development version:\n'ssh deck@${DECKIP} -p 22 ${IDENINVOC} 'export PLUGIN_PATH=${INSTALLDIR}/plugins; export CHOWN_PLUGIN_PATH=0; echo 'steam' | sudo -SE python3 ${INSTALLDIR}/pluginloader/backend/main.py'\n"
|
printf "Run in console or in a script this command to run your development version:\n'ssh deck@${DECKIP} -p ${SSHPORT} ${IDENINVOC} 'export PLUGIN_PATH=${INSTALLDIR}/plugins; export CHOWN_PLUGIN_PATH=0; echo 'steam' | sudo -SE python3 ${INSTALLDIR}/pluginloader/backend/main.py'\n"
|
||||||
|
|
||||||
## Disable Releases versions if they exist
|
## Disable Releases versions if they exist
|
||||||
|
|
||||||
|
|||||||
Generated
-3881
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^21.1.0",
|
"@rollup/plugin-commonjs": "^21.1.0",
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^13.2.1",
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
"@rollup/plugin-replace": "^4.0.0",
|
"@rollup/plugin-replace": "^4.0.0",
|
||||||
"@rollup/plugin-typescript": "^8.3.2",
|
"@rollup/plugin-typescript": "^8.3.2",
|
||||||
"@types/react": "16.14.0",
|
"@types/react": "16.14.0",
|
||||||
@@ -21,13 +21,14 @@
|
|||||||
"@types/webpack": "^5.28.0",
|
"@types/webpack": "^5.28.0",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.1",
|
||||||
"import-sort-style-module": "^6.0.0",
|
"import-sort-style-module": "^6.0.0",
|
||||||
|
"inquirer": "^8.2.4",
|
||||||
"prettier": "^2.6.2",
|
"prettier": "^2.6.2",
|
||||||
"prettier-plugin-import-sort": "^0.0.7",
|
"prettier-plugin-import-sort": "^0.0.7",
|
||||||
"react": "16.14.0",
|
"react": "16.14.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"rollup": "^2.70.2",
|
"rollup": "^2.75.6",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.7.2"
|
"typescript": "^4.7.3"
|
||||||
},
|
},
|
||||||
"importSort": {
|
"importSort": {
|
||||||
".js, .jsx, .ts, .tsx": {
|
".js, .jsx, .ts, .tsx": {
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"decky-frontend-lib": "^0.0.6",
|
"decky-frontend-lib": "^0.10.2",
|
||||||
"react-icons": "^4.3.1"
|
"react-icons": "^4.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1724
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ const PluginView: VFC = () => {
|
|||||||
|
|
||||||
const onStoreClick = () => {
|
const onStoreClick = () => {
|
||||||
Router.CloseSideMenus();
|
Router.CloseSideMenus();
|
||||||
Router.NavigateToExternalWeb('http://127.0.0.1:1337/browser/redirect');
|
Router.Navigate('/decky/store');
|
||||||
};
|
};
|
||||||
|
|
||||||
if (activePlugin) {
|
if (activePlugin) {
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
import {
|
||||||
|
DialogButton,
|
||||||
|
Dropdown,
|
||||||
|
Focusable,
|
||||||
|
Router,
|
||||||
|
SingleDropdownOption,
|
||||||
|
SuspensefulImage,
|
||||||
|
staticClasses,
|
||||||
|
} from 'decky-frontend-lib';
|
||||||
|
import { FC, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { StorePlugin } from './Store';
|
||||||
|
|
||||||
|
interface PluginCardProps {
|
||||||
|
plugin: StorePlugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classNames = (...classes: string[]) => {
|
||||||
|
return classes.join(' ');
|
||||||
|
};
|
||||||
|
|
||||||
|
async function requestPluginInstall(plugin: StorePlugin, selectedVer: string) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('artifact', plugin.artifact);
|
||||||
|
formData.append('version', selectedVer);
|
||||||
|
formData.append('hash', plugin.versions[selectedVer]);
|
||||||
|
await fetch('http://localhost:1337/browser/install_plugin', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
|
||||||
|
const [selectedOption, setSelectedOption] = useState<number>(0);
|
||||||
|
const buttonRef = useRef<HTMLDivElement>(null);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: '30px',
|
||||||
|
paddingTop: '10px',
|
||||||
|
paddingBottom: '10px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* TODO: abstract this messy focus hackiness into a custom component in lib */}
|
||||||
|
<Focusable
|
||||||
|
// className="Panel Focusable"
|
||||||
|
ref={containerRef}
|
||||||
|
onActivate={(e: CustomEvent) => {
|
||||||
|
buttonRef.current!.focus();
|
||||||
|
}}
|
||||||
|
onCancel={(e: CustomEvent) => {
|
||||||
|
containerRef.current!.querySelectorAll('* :focus').length === 0
|
||||||
|
? Router.NavigateBackOrOpenMenu()
|
||||||
|
: containerRef.current!.focus();
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
background: '#ACB2C924',
|
||||||
|
height: 'unset',
|
||||||
|
marginBottom: 'unset',
|
||||||
|
// boxShadow: var(--gpShadow-Medium);
|
||||||
|
scrollSnapAlign: 'start',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<a
|
||||||
|
style={{ fontSize: '18pt', padding: '10px' }}
|
||||||
|
className={classNames(staticClasses.Text)}
|
||||||
|
onClick={() => Router.NavigateToExternalWeb('https://github.com/' + plugin.artifact)}
|
||||||
|
>
|
||||||
|
<span style={{ color: 'grey' }}>{plugin.artifact.split('/')[0]}/</span>
|
||||||
|
{plugin.artifact.split('/')[1]}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SuspensefulImage
|
||||||
|
suspenseWidth="256px"
|
||||||
|
style={{
|
||||||
|
width: 'auto',
|
||||||
|
height: '160px',
|
||||||
|
}}
|
||||||
|
src={`https://cdn.tzatzikiweeb.moe/file/steam-deck-homebrew/artifact_images/${plugin.artifact.replace(
|
||||||
|
'/',
|
||||||
|
'_',
|
||||||
|
)}.png`}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className={classNames(staticClasses.PanelSectionRow)}>
|
||||||
|
<span>Author: {plugin.author}</span>
|
||||||
|
</p>
|
||||||
|
<p className={classNames(staticClasses.PanelSectionRow)}>
|
||||||
|
<span>Tags:</span>
|
||||||
|
{plugin.tags.map((tag: string) => (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
padding: '5px',
|
||||||
|
marginRight: '10px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
background: tag == 'root' ? '#842029' : '#ACB2C947',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tag == 'root' ? 'Requires root' : tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
alignSelf: 'flex-end',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Focusable
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: '1',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogButton
|
||||||
|
ref={buttonRef}
|
||||||
|
onClick={() => requestPluginInstall(plugin, Object.keys(plugin.versions)[selectedOption])}
|
||||||
|
>
|
||||||
|
Install
|
||||||
|
</DialogButton>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: '0.2',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
rgOptions={
|
||||||
|
Object.keys(plugin.versions).map((v, k) => ({
|
||||||
|
data: k,
|
||||||
|
label: v,
|
||||||
|
})) as SingleDropdownOption[]
|
||||||
|
}
|
||||||
|
strDefaultLabel={'Select a version'}
|
||||||
|
selectedOption={selectedOption}
|
||||||
|
onChange={({ data }) => setSelectedOption(data)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Focusable>
|
||||||
|
</div>
|
||||||
|
</Focusable>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PluginCard;
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { SteamSpinner } from 'decky-frontend-lib';
|
||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import PluginCard from './PluginCard';
|
||||||
|
|
||||||
|
export interface StorePlugin {
|
||||||
|
artifact: string;
|
||||||
|
versions: {
|
||||||
|
[version: string]: string;
|
||||||
|
};
|
||||||
|
author: string;
|
||||||
|
description: string;
|
||||||
|
tags: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const StorePage: FC<{}> = () => {
|
||||||
|
const [data, setData] = useState<StorePlugin[] | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const res = await fetch('https://beta.deckbrew.xyz/get_plugins', { method: 'GET' }).then((r) => r.json());
|
||||||
|
console.log(res);
|
||||||
|
setData(res);
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: '40px',
|
||||||
|
height: 'calc( 100% - 40px )',
|
||||||
|
overflowY: 'scroll',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'nowrap',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{data === null ? (
|
||||||
|
<div style={{ height: '100%' }}>
|
||||||
|
<SteamSpinner />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
data.map((plugin: StorePlugin) => <PluginCard plugin={plugin} />)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StorePage;
|
||||||
@@ -9,6 +9,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.DeckyPluginLoader?.dismountAll();
|
window.DeckyPluginLoader?.dismountAll();
|
||||||
|
window.DeckyPluginLoader?.deinit();
|
||||||
|
|
||||||
window.DeckyPluginLoader = new PluginLoader();
|
window.DeckyPluginLoader = new PluginLoader();
|
||||||
window.importDeckyPlugin = function (name: string) {
|
window.importDeckyPlugin = function (name: string) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { FaPlug } from 'react-icons/fa';
|
|||||||
import { DeckyState, DeckyStateContextProvider } from './components/DeckyState';
|
import { DeckyState, DeckyStateContextProvider } from './components/DeckyState';
|
||||||
import LegacyPlugin from './components/LegacyPlugin';
|
import LegacyPlugin from './components/LegacyPlugin';
|
||||||
import PluginView from './components/PluginView';
|
import PluginView from './components/PluginView';
|
||||||
|
import StorePage from './components/store/Store';
|
||||||
import TitleView from './components/TitleView';
|
import TitleView from './components/TitleView';
|
||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
import { Plugin } from './plugin';
|
import { Plugin } from './plugin';
|
||||||
@@ -43,6 +44,8 @@ class PluginLoader extends Logger {
|
|||||||
),
|
),
|
||||||
icon: <FaPlug />,
|
icon: <FaPlug />,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.routerHook.addRoute('/decky/store', () => <StorePage />);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addPluginInstallPrompt(artifact: string, version: string, request_id: string) {
|
public addPluginInstallPrompt(artifact: string, version: string, request_id: string) {
|
||||||
@@ -71,6 +74,10 @@ class PluginLoader extends Logger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public deinit() {
|
||||||
|
this.routerHook.removeRoute('/decky/store');
|
||||||
|
}
|
||||||
|
|
||||||
public async importPlugin(name: string) {
|
public async importPlugin(name: string) {
|
||||||
try {
|
try {
|
||||||
if (this.reloadLock) {
|
if (this.reloadLock) {
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ class RouterHook extends Logger {
|
|||||||
this.routerState.addRoute(path, component, props);
|
this.routerState.addRoute(path, component, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeRoute(path: string) {
|
||||||
|
this.routerState.removeRoute(path);
|
||||||
|
}
|
||||||
|
|
||||||
deinit() {
|
deinit() {
|
||||||
unpatch(this.gamepadWrapper, 'render');
|
unpatch(this.gamepadWrapper, 'render');
|
||||||
this.router && unpatch(this.router, 'type');
|
this.router && unpatch(this.router, 'type');
|
||||||
|
|||||||
Reference in New Issue
Block a user