Compare commits

..

3 Commits

Author SHA1 Message Date
TrainDoctor 3a927084b0 FIlled out functionality of handler function, now for JS to call route 2022-12-10 14:25:21 -08:00
TrainDoctor 5e07c8f028 Fix a typo 2022-12-10 14:25:20 -08:00
TrainDoctor 91d1bc8a78 Improvements to install of remote binaries, and start backend reloading 2022-12-10 14:25:20 -08:00
23 changed files with 107 additions and 330 deletions
+36
View File
@@ -0,0 +1,36 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Description**
[A clear and concise description of what the bug is.]
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
[A clear and concise description of what you expected to happen.]
**Screenshots**
[If applicable, add screenshots to help explain your problem.]
**Version information**
- SteamOS Version: ``[Run ``uname -a`` and place the output here. Leave the single quotations outside.]``
- Selected Update Channel: [Stable, Beta or Preview.]
**Logs**
[Please reboot your deck (if possible) when attempting to recreate the issue, then run
``cd ~ && journalctl -b0 -u plugin_loader.service > backendlog.txt``. This will save the log file to ``~`` aka ``/home/deck``. Please upload the file here in place of this textblock.]
**Additional context**
Have you modified the read-only filesystem at any point?
[Yes or No.]
-73
View File
@@ -1,73 +0,0 @@
name: Bug report
description: File a bug/issue
title: "[BUG] <title>"
labels: [bug]
body:
- type: checkboxes
id: low-effort-checks
attributes:
label: Please confirm
description: Issues without all checks may be ignored/closed.
options:
- label: I have searched existing issues
- label: This issue is not a duplicate of an existing one
- label: I have checked the [common issues section in the readme file](https://github.com/SteamDeckHomebrew/decky-loader#-common-issues)
- type: textarea
attributes:
label: Bug Report Description
description: A clear and concise description of what the bug is and if possible, the steps you used to get to the bug. If appropriate, include screenshots or videos.
placeholder: |
When I try to use ...
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Expected Behaviour
description: A brief description of the expected behavior.
placeholder: It should be ...
validations:
required: true
- type: input
attributes:
label: SteamOS version
# description: Can be found with `uname -a`
# placeholder: "Linux steamdeck 5.13.0-valve36-1-neptune #1 SMP PREEMPT Mon, 19 Dec 2022 23:39:41 +0000 x86_64 GNU/Linux"
placeholder: "SteamOS 3.4.3 Stable"
validations:
required: true
- type: dropdown
attributes:
label: Selected Update Channel
description: Which branch of Decky are you on?
multiple: false
options:
- Stable
- Prerelease
validations:
required: true
- type: input
attributes:
label: Have you modified the read-only filesystem at any point?
description: Describe how here, if you haven't done anything you can leave this blank
placeholder: Yes, I've installed neofetch via pacman.
validations:
required: false
- type: textarea
attributes:
label: Logs
description: Please reboot your deck (if possible) when attempting to recreate the issue, then run ``cd ~ && journalctl -b0 -u plugin_loader.service > deckylog.txt``. This will save the log file to ``~`` aka ``/home/deck``. Please upload the file here
placeholder: deckylog.txt
validations:
required: false
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Steam Deck Homebrew Discord Server
url: https://discord.gg/ZU74G2NJzk
about: Please ask and answer questions here.
+2 -2
View File
@@ -47,10 +47,10 @@ jobs:
with:
node-version: 18
- name: Set up Python 3.10.2 🐍
- name: Set up Python 3.10.9 🐍
uses: actions/setup-python@v4
with:
python-version: "3.10.2"
python-version: "3.10.9"
- name: Install Python dependencies ⬇️
run: |
+1 -1
View File
@@ -98,7 +98,7 @@ Please consult [the wiki page regarding development](https://deckbrew.xyz/en/loa
1. Use the VS Code tasks or `deck.sh` script to deploy your changes to your Steam Deck to test them.
1. You will be testing your changes with the Python script version. You will need to build, deploy, and reload each time.
⚠️ If you are recieving build errors due to an out of date library, you should run this command inside of your repository.
⚠️ 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-frontend-lib --latest
+9 -2
View File
@@ -55,11 +55,14 @@ class PluginBrowser:
pluginBinPath = path.join(pluginBasePath, 'bin')
if access(packageJsonPath, R_OK):
with open(packageJsonPath, "r", encoding="utf-8") as f:
with open(packageJsonPath, 'r') as f:
packageJson = json.load(f)
if "remote_binary" in packageJson and len(packageJson["remote_binary"]) > 0:
# create bin directory if needed.
rc=call(["chmod", "-R", "777", pluginBasePath])
if rc != 0:
logger.error(f"chown of plugin w/ remote binary exited with a non-zero exit code, chmod: {rc})")
if access(pluginBasePath, W_OK):
if not path.exists(pluginBinPath):
@@ -67,6 +70,8 @@ class PluginBrowser:
if not access(pluginBinPath, W_OK):
rc=call(["chmod", "-R", "777", pluginBinPath])
if rc != 0:
logger.error(f"chown of plugin w/ remote binary exited with a non-zero exit code, chmod: {rc})")
rv = True
for remoteBinary in packageJson["remote_binary"]:
@@ -80,6 +85,8 @@ class PluginBrowser:
code_chown = call(["chown", "-R", get_user()+":"+get_user_group(), self.plugin_path])
rc=call(["chmod", "-R", "555", pluginBasePath])
if code_chown or rc != 0:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {rc})")
else:
rv = True
logger.debug(f"No Remote Binaries to Download")
@@ -93,7 +100,7 @@ class PluginBrowser:
def find_plugin_folder(self, name):
for folder in listdir(self.plugin_path):
try:
with open(path.join(self.plugin_path, folder, 'plugin.json'), "r", encoding="utf-8") as f:
with open(path.join(self.plugin_path, folder, 'plugin.json'), 'r') as f:
plugin = json.load(f)
if plugin['name'] == name:
+1 -1
View File
@@ -396,7 +396,7 @@ async def get_tab_lambda(test) -> Tab:
async def get_gamepadui_tab() -> Tab:
tabs = await get_tabs()
tab = next((i for i in tabs if ("https://steamloopback.host/routes/" in i.url and (i.title == "Steam Shared Context presented by Valve™" or i.title == "Steam" or i.title == "SP"))), None)
tab = next((i for i in tabs if ("https://steamloopback.host/routes/" in i.url and (i.title == "Steam" or i.title == "SP"))), None)
if not tab:
raise ValueError(f"GamepadUI Tab not found")
return tab
+3 -3
View File
@@ -118,7 +118,7 @@ class Loader:
def handle_frontend_bundle(self, request):
plugin = self.plugins[request.match_info["plugin_name"]]
with open(path.join(self.plugin_path, plugin.plugin_directory, "dist/index.js"), "r", encoding="utf-8") as bundle:
with open(path.join(self.plugin_path, plugin.plugin_directory, "dist/index.js"), 'r') as bundle:
return web.Response(text=bundle.read(), content_type="application/javascript")
def import_plugin(self, file, plugin_directory, refresh=False, batch=False):
@@ -186,7 +186,7 @@ class Loader:
"""
async def load_plugin_main_view(self, request):
plugin = self.plugins[request.match_info["name"]]
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), "r", encoding="utf-8") as template:
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), 'r') as template:
template_data = template.read()
ret = f"""
<script src="/legacy/library.js"></script>
@@ -202,7 +202,7 @@ class Loader:
self.logger.info(path)
ret = ""
file_path = path.join(self.plugin_path, plugin.plugin_directory, route_path)
with open(file_path, "r", encoding="utf-8") as resource_data:
with open(file_path, 'r') as resource_data:
ret = resource_data.read()
return web.Response(text=ret)
+14 -2
View File
@@ -7,7 +7,7 @@ if hasattr(sys, '_MEIPASS'):
from asyncio import new_event_loop, set_event_loop, sleep
from json import dumps, loads
from logging import DEBUG, INFO, basicConfig, getLogger
from os import getenv, chmod, path
from os import getenv, chmod, listdir, path
from traceback import format_exc
import aiohttp_cors
@@ -91,6 +91,7 @@ class PluginManager:
chown_plugin_dir()
self.loop.create_task(self.loader_reinjector())
self.loop.create_task(self.load_plugins())
self.loop.create_task(self.reload_plugin_backends())
self.web_app.on_startup.append(startup)
@@ -116,6 +117,17 @@ class PluginManager:
self.plugin_loader.import_plugins()
# await inject_to_tab("SP", "window.syncDeckyPlugins();")
async def reload_plugin_backends(self, name):
await self.wait_for_server()
if name in self.plugin_loader.plugins:
self.plugin_loader.plugins[name].stop()
self.plugin_loader.plugins.pop(name, None)
else:
logger.error("Couldn't find plugin %s to reload.", str(name))
directories = [i for i in listdir(self.plugin_loader.plugin_path) if path.isdir(path.join(self.plugin_loader.plugin_path, i)) and path.isfile(path.join(self.plugin_loader.plugin_path, i, "plugin.json"))]
if name in directories:
Loader.import_plugin(path.join(self.plugin_loader.plugin_path, directories[name], "main.py"), directories[name], False, False)
async def loader_reinjector(self):
while True:
tab = None
@@ -176,7 +188,7 @@ class PluginManager:
# logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
# await t.close()
# await sleep(0.5)
await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartShutdown(false), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}", False, False, False)
await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.User.StartRestart(), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.SP_REACT){await new Promise(r => setTimeout(r, 10))};await import('http://localhost:1337/frontend/index.js')}catch(e){console.error(e)};})();}}catch(e){console.error(e)}", False, False, False)
except:
logger.info("Failed to inject JavaScript into tab\n" + format_exc())
pass
+5 -5
View File
@@ -27,9 +27,9 @@ class PluginWrapper:
self.version = None
json = load(open(path.join(plugin_path, plugin_directory, "plugin.json"), "r", encoding="utf-8"))
json = load(open(path.join(plugin_path, plugin_directory, "plugin.json"), "r"))
if path.isfile(path.join(plugin_path, plugin_directory, "package.json")):
package_json = load(open(path.join(plugin_path, plugin_directory, "package.json"), "r", encoding="utf-8"))
package_json = load(open(path.join(plugin_path, plugin_directory, "package.json"), "r"))
self.version = package_json["version"]
@@ -112,7 +112,7 @@ class PluginWrapper:
d["res"] = str(e)
d["success"] = False
finally:
writer.write((dumps(d, ensure_ascii=False)+"\n").encode("utf-8"))
writer.write((dumps(d)+"\n").encode("utf-8"))
await writer.drain()
async def _open_socket_if_not_exists(self):
@@ -140,7 +140,7 @@ class PluginWrapper:
return
async def _(self):
if await self._open_socket_if_not_exists():
self.writer.write((dumps({ "stop": True }, ensure_ascii=False)+"\n").encode("utf-8"))
self.writer.write((dumps({"stop": True})+"\n").encode("utf-8"))
await self.writer.drain()
self.writer.close()
get_event_loop().create_task(_(self))
@@ -151,7 +151,7 @@ class PluginWrapper:
async with self.method_call_lock:
if await self._open_socket_if_not_exists():
self.writer.write(
(dumps({ "method": method_name, "args": kwargs }, ensure_ascii=False) + "\n").encode("utf-8"))
(dumps({"method": method_name, "args": kwargs})+"\n").encode("utf-8"))
await self.writer.drain()
line = bytearray()
while True:
+4 -4
View File
@@ -35,22 +35,22 @@ class SettingsManager:
self.settings = {}
try:
open(self.path, "x", encoding="utf-8")
open(self.path, "x")
except FileExistsError as e:
self.read()
pass
def read(self):
try:
with open(self.path, "r", encoding="utf-8") as file:
with open(self.path, "r") as file:
self.settings = load(file)
except Exception as e:
print(e)
pass
def commit(self):
with open(self.path, "w+", encoding="utf-8") as file:
dump(self.settings, file, indent=4, ensure_ascii=False)
with open(self.path, "w+") as file:
dump(self.settings, file, indent=4)
def getSetting(self, key, default):
return self.settings.get(key, default)
+4 -4
View File
@@ -32,7 +32,7 @@ class Updater:
self.allRemoteVers = None
try:
logger.info(getcwd())
with open(path.join(getcwd(), ".loader.version"), "r", encoding="utf-8") as version_file:
with open(path.join(getcwd(), ".loader.version"), 'r') as version_file:
self.localVer = version_file.readline().replace("\n", "")
except:
self.localVer = False
@@ -159,10 +159,10 @@ class Updater:
out.write(data)
except Exception as e:
logger.error(f"Error at %s", exc_info=e)
with open(path.join(getcwd(), "plugin_loader.service"), "r", encoding="utf-8") as service_file:
with open(path.join(getcwd(), "plugin_loader.service"), 'r') as service_file:
service_data = service_file.read()
service_data = service_data.replace("${HOMEBREW_FOLDER}", "/home/"+helpers.get_user()+"/homebrew")
with open(path.join(getcwd(), "plugin_loader.service"), "w", encoding="utf-8") as service_file:
with open(path.join(getcwd(), "plugin_loader.service"), 'w') as service_file:
service_file.write(service_data)
logger.debug("Saved service file")
@@ -191,7 +191,7 @@ class Updater:
self.context.loop.create_task(tab.evaluate_js(f"window.DeckyUpdater.updateProgress({new_progress})", False, False, False))
progress = new_progress
with open(path.join(getcwd(), ".loader.version"), "w", encoding="utf-8") as out:
with open(path.join(getcwd(), ".loader.version"), "w") as out:
out.write(version)
call(['chmod', '+x', path.join(getcwd(), "PluginLoader")])
-8
View File
@@ -1,8 +0,0 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=Install Decky
Exec=sh -c 'curl -O --output-dir /tmp/ https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/user_install_script.sh 2> /dev/null && bash /tmp/user_install_script.sh'
Icon=steamdeck-gaming-return
Terminal=true
Type=Application
StartupNotify=false
-1
View File
@@ -11,7 +11,6 @@ HOMEBREW_FOLDER="${USER_DIR}/homebrew"
rm -rf "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
touch "${USER_DIR}/.steam/steam/.cef-enable-remote-debugging"
# Download latest release and install it
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))")
-1
View File
@@ -11,7 +11,6 @@ HOMEBREW_FOLDER="${USER_DIR}/homebrew"
rm -rf "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
touch "${USER_DIR}/.steam/steam/.cef-enable-remote-debugging"
# Download latest release and install it
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "false"))")
-164
View File
@@ -1,164 +0,0 @@
#!/bin/sh
# if a password was set by decky, this will run when the program closes
temp_pass_cleanup() {
echo $PASS | sudo -S -k passwd -d deck
}
# if the script is not root yet, get the password and rerun as root
if (( $EUID != 0 )); then
PASS_STATUS=$(passwd -S deck 2> /dev/null)
if [ "$PASS_STATUS" = "" ]; then
echo "Deck user not found. Continuing anyway, as it probably just means user is on a non-steamos system."
fi
if [ "${PASS_STATUS:5:2}" = "NP" ]; then # if no password is set
if ( zenity --title="Decky Installer" --width=300 --height=200 --question --text="You appear to have not set an admin password.\nDecky can still install by temporarily setting your password to 'Decky!' and continuing, then removing it when the installer finishes\nAre you okay with that?" ); then
yes "Decky!" | passwd deck
trap temp_pass_cleanup EXIT # make sure password is removed when application closes
PASS="Decky!"
else exit 1; fi
else
# get password
FINISHED="false"
while [ "$FINISHED" != "true" ]; do
PASS=$(zenity --title="Decky Installer" --width=300 --height=100 --entry --hide-text --text="Enter your sudo/admin password")
if [[ $? -eq 1 ]] || [[ $? -eq 5 ]]; then
exit 1
fi
if ( echo "$PASS" | sudo -S -k true ); then
FINISHED="true"
else
zenity --title="Decky Installer" --width=150 --height=40 --info --text "Incorrect Password"
fi
done
fi
if ! [ $USER = "deck" ]; then
zenity --title="Decky Installer" --width=300 --height=100 --warning --text "You appear to not be on a deck.\nDecky should still mostly work, but you may not get full functionality."
fi
# get user dir before rerunning as root, otherwise it'll just be 'home/root'
USER_DIR="$(getent passwd $USER | cut -d: -f6)"
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
echo "$PASS" | sudo -S -k sh "$0" "$USER_DIR" "$HOMEBREW_FOLDER" # rerun script as root
exit 1
fi
# all code below should be run as root
USER_DIR=$1
HOMEBREW_FOLDER=$2
# if decky is already installed, then also add an 'uninstall' prompt
if [[ -f "${USER_DIR}/homebrew/services/PluginLoader" ]] ; then
BRANCH=$(zenity --title="Decky Installer" --width=360 --height=170 --list --radiolist --text "Select Option:" --hide-header --column "Buttons" --column "Choice" --column "Info" TRUE "release" "(Recommended option)" FALSE "prerelease" "(May be unstable)" FALSE "uninstall decky loader" "")
else
BRANCH=$(zenity --title="Decky Installer" --width=300 --height=100 --list --radiolist --text "Select Branch:" --hide-header --column "Buttons" --column "Choice" --column "Info" TRUE "release" "(Recommended option)" FALSE "prerelease" "(May be unstable)" )
fi
if [[ $? -eq 1 ]] || [[ $? -eq 5 ]]; then
exit 1
fi
# uninstall if uninstall option was selected
if [ "$BRANCH" == "uninstall decky loader" ] ; then
(
echo "30" ; echo "# Disabling and removing services" ;
sudo systemctl disable --now plugin_loader.service > /dev/null
sudo rm -f "${USER_DIR}/.config/systemd/user/plugin_loader.service"
sudo rm -f "/etc/systemd/system/plugin_loader.service"
echo "60" ; echo "# Removing Temporary Files" ;
rm -rf "/tmp/plugin_loader"
rm -rf "/tmp/user_install_script.sh"
echo "90" ; echo "# Cleaning services folder" ;
sudo rm "${HOMEBREW_FOLDER}/services/PluginLoader"
echo "100" ; echo "# Uninstall finished, installer can now be closed";
) |
zenity --progress \
--title="Decky Installer" \
--width=300 --height=100 \
--text="Uninstalling..." \
--percentage=0 \
--no-cancel
exit 1
fi
# otherwise install decky loader
(
echo "15" ; echo "# Creating file structure" ;
rm -rf "${HOMEBREW_FOLDER}/services"
sudo mkdir -p "${HOMEBREW_FOLDER}/services"
sudo mkdir -p "${HOMEBREW_FOLDER}/plugins"
touch "${USER_DIR}/.steam/steam/.cef-enable-remote-debugging"
echo "30" ; echo "# Finding latest $BRANCH";
if [ $BRANCH = 'prerelease' ] ; then
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))")
else
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "false"))")
fi
read VERSION DOWNLOADURL < <(echo $(jq -r '.tag_name, .assets[].browser_download_url' <<< ${RELEASE}))
echo "45" ; echo "# Installing version $VERSION" ;
curl -L $DOWNLOADURL -o ${HOMEBREW_FOLDER}/services/PluginLoader 2>&1 | stdbuf -oL tr '\r' '\n' | sed -u 's/^ *\([0-9][0-9]*\).*\( [0-9].*$\)/\1\n#Download Speed\:\2/' | zenity --progress --title "Downloading Decky" --text="Download Speed: 0" --width=300 --height=100 --auto-close --no-cancel 2>/dev/null
chmod +x ${HOMEBREW_FOLDER}/services/PluginLoader
echo $VERSION > ${HOMEBREW_FOLDER}/services/.loader.version
echo "70" ; echo "# Kiling plugin_loader if it exists" ;
systemctl --user stop plugin_loader 2> /dev/null
systemctl --user disable plugin_loader 2> /dev/null
systemctl stop plugin_loader 2> /dev/null
systemctl disable plugin_loader 2> /dev/null
echo "85" ; echo "# Setting up systemd" ;
curl -L https://raw.githubusercontent.com/SteamDeckHomebrew/decky-loader/main/dist/plugin_loader-${BRANCH}.service --output ${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service
cat > "${HOMEBREW_FOLDER}/services/plugin_loader-backup.service" <<- EOM
[Unit]
Description=SteamDeck Plugin Loader
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
Restart=always
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=${HOMEBREW_FOLDER}/services
KillSignal=SIGKILL
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
Environment=LOG_LEVEL=INFO
[Install]
WantedBy=multi-user.target
EOM
# if .service file doesn't exist for whatever reason, use backup file instead
if [[ -f "${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service" ]]; then
printf "Grabbed latest ${BRANCH} service.\n"
sed -i -e "s|\${HOMEBREW_FOLDER}|${HOMEBREW_FOLDER}|" "${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service"
cp -f "${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service" "/etc/systemd/system/plugin_loader.service"
else
printf "Could not curl latest ${BRANCH} systemd service, using built-in service as a backup!\n"
rm -f "/etc/systemd/system/plugin_loader.service"
cp "${HOMEBREW_FOLDER}/services/plugin_loader-backup.service" "/etc/systemd/system/plugin_loader.service"
fi
mkdir -p ${HOMEBREW_FOLDER}/services/.systemd
cp ${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service ${HOMEBREW_FOLDER}/services/.systemd/plugin_loader-${BRANCH}.service
cp ${HOMEBREW_FOLDER}/services/plugin_loader-backup.service ${HOMEBREW_FOLDER}/services/.systemd/plugin_loader-backup.service
rm ${HOMEBREW_FOLDER}/services/plugin_loader-backup.service ${HOMEBREW_FOLDER}/services/plugin_loader-${BRANCH}.service
systemctl daemon-reload
systemctl start plugin_loader
systemctl enable plugin_loader
echo "100" ; echo "# Install finished, installer can now be closed";
) |
zenity --progress \
--title="Decky Installer" \
--width=300 --height=100 \
--text="Installing..." \
--percentage=0 \
--no-cancel # not actually sure how to make the cancel work properly, so it's just not there unless someone else can figure it out
if [ "$?" = -1 ] ; then
zenity --title="Decky Installer" --width=150 --height=70 --error --text="Download interrupted."
fi
+1 -1
View File
@@ -41,7 +41,7 @@
}
},
"dependencies": {
"decky-frontend-lib": "^3.18.4",
"decky-frontend-lib": "^3.7.14",
"react-file-icon": "^1.2.0",
"react-icons": "^4.4.0",
"react-markdown": "^8.0.3",
+4 -4
View File
@@ -10,7 +10,7 @@ specifiers:
'@types/react-file-icon': ^1.0.1
'@types/react-router': 5.1.18
'@types/webpack': ^5.28.0
decky-frontend-lib: ^3.18.4
decky-frontend-lib: ^3.7.14
husky: ^8.0.1
import-sort-style-module: ^6.0.0
inquirer: ^8.2.4
@@ -30,7 +30,7 @@ specifiers:
typescript: ^4.7.4
dependencies:
decky-frontend-lib: 3.18.4
decky-frontend-lib: 3.7.14
react-file-icon: 1.2.0_wcqkhtmu7mswc6yz4uyexck3ty
react-icons: 4.4.0_react@16.14.0
react-markdown: 8.0.3_vshvapmxg47tngu7tvrsqpq55u
@@ -944,8 +944,8 @@ packages:
dependencies:
ms: 2.1.2
/decky-frontend-lib/3.18.4:
resolution: {integrity: sha512-i3TAe3RJtT1TK0rJgW9Ek5jxMWZRCYLDvqHDylGVieUvuyI7c8X+cogz30pP4cqeGOaA1d/MxBEbhlpD3JhVvg==}
/decky-frontend-lib/3.7.14:
resolution: {integrity: sha512-ShAoP3VqiwkJYukDBHsOF9fk7wYw0VaKpHw6j9WdzLxwZwBcg0J7QBNIFYP3nfA0UgEwSJVEg/22kONiplipmA==}
dev: false
/decode-named-character-reference/1.0.2:
@@ -1,4 +1,4 @@
import { ConfirmModal, Navigation, QuickAccessTab, Spinner, staticClasses } from 'decky-frontend-lib';
import { ConfirmModal, QuickAccessTab, Router, Spinner, staticClasses } from 'decky-frontend-lib';
import { FC, useState } from 'react';
interface PluginInstallModalProps {
@@ -20,7 +20,7 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
onOK={async () => {
setLoading(true);
await onOK();
setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
+3 -3
View File
@@ -2,8 +2,8 @@ import {
DialogButton,
Dropdown,
Focusable,
Navigation,
QuickAccessTab,
Router,
SingleDropdownOption,
SuspensefulImage,
joinClassNames,
@@ -38,8 +38,8 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
}}
onCancel={(_: CustomEvent) => {
if (containerRef.current!.querySelectorAll('* :focus').length === 0) {
Navigation.NavigateBack();
setTimeout(() => Navigation.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
Router.NavigateBackOrOpenMenu();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 1000);
} else {
containerRef.current!.focus();
}
+8 -17
View File
@@ -1,5 +1,4 @@
import {
Navigation,
ReactRouter,
Router,
fakeRenderComponent,
@@ -27,20 +26,13 @@ const logger = new Logger('DeveloperMode');
let removeSettingsObserver: () => void = () => {};
export async function setShowValveInternal(show: boolean) {
let settingsMod: any;
while (!settingsMod) {
settingsMod = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop]?.settings?.bIsValveEmail !== 'undefined') return m[prop];
}
});
if (!settingsMod) {
logger.debug('[ValveInternal] waiting for settingsMod');
await sleep(1000);
export function setShowValveInternal(show: boolean) {
const settingsMod = findModuleChild((m) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
if (typeof m[prop]?.settings?.bIsValveEmail !== 'undefined') return m[prop];
}
}
});
if (show) {
removeSettingsObserver = settingsMod[
@@ -82,14 +74,13 @@ export async function startup() {
window.DFL = {
findModuleChild,
findModule,
Navigation,
Router,
ReactRouter,
ReactUtils: {
fakeRenderComponent,
findInReactTree,
findInTree,
},
Router,
ReactRouter,
classes: {
scrollClasses,
staticClasses,
+2
View File
@@ -23,6 +23,7 @@ class TabsHook extends Logger {
tabs: Tab[] = [];
private qAMRoot?: any;
private qamPatch?: Patch;
private unsubscribeSecurity?: () => void;
constructor() {
super('TabsHook');
@@ -113,6 +114,7 @@ class TabsHook extends Logger {
deinit() {
this.qamPatch?.unpatch();
this.qAMRoot.return.alternate.type = this.qAMRoot.return.type;
this.unsubscribeSecurity?.();
}
add(tab: Tab) {
+8 -27
View File
@@ -1,4 +1,4 @@
import { Module, Patch, ToastData, afterPatch, findInReactTree, findModuleChild, sleep } from 'decky-frontend-lib';
import { Patch, ToastData, afterPatch, findInReactTree, sleep } from 'decky-frontend-lib';
import { ReactNode } from 'react';
import Toast from './components/Toast';
@@ -7,7 +7,6 @@ import Logger from './logger';
declare global {
interface Window {
__TOASTER_INSTANCE: any;
settingsStore: any;
NotificationStore: any;
}
}
@@ -17,7 +16,7 @@ class Toaster extends Logger {
// private toasterState: DeckyToasterState = new DeckyToasterState();
private node: any;
private rNode: any;
private audioModule: any;
private settingsModule: any;
private finishStartup?: () => void;
private ready: Promise<void> = new Promise((res) => (this.finishStartup = res));
private toasterPatch?: Patch;
@@ -128,17 +127,6 @@ class Toaster extends Logger {
this.rNode.stateNode.forceUpdate();
delete this.rNode.stateNode.shouldComponentUpdate;
this.audioModule = findModuleChild((m: Module) => {
if (typeof m !== 'object') return undefined;
for (let prop in m) {
try {
if (m[prop].PlayNavSound && m[prop].RegisterCallbackOnPlaySound) return m[prop];
} catch {
return undefined;
}
}
});
this.log('Initialized');
this.finishStartup?.();
}
@@ -147,31 +135,24 @@ class Toaster extends Logger {
// toast.duration = toast.duration || 5e3;
// this.toasterState.addToast(toast);
await this.ready;
const settings = this.settingsModule?.settings;
let toastData = {
nNotificationID: window.NotificationStore.m_nNextTestNotificationID++,
rtCreated: Date.now(),
eType: toast.eType || 11,
eType: 15,
nToastDurationMS: toast.duration || (toast.duration = 5e3),
data: toast,
decky: true,
};
// @ts-ignore
toastData.data.appid = () => 0;
if (toast.sound === undefined) toast.sound = 6;
if (toast.playSound === undefined) toast.playSound = true;
if (toast.showToast === undefined) toast.showToast = true;
if (
(window.settingsStore.settings.bDisableAllToasts && !toast.critical) ||
(window.settingsStore.settings.bDisableToastsInGame &&
!toast.critical &&
window.NotificationStore.BIsUserInGame())
(settings?.bDisableAllToasts && !toast.critical) ||
(settings?.bDisableToastsInGame && !toast.critical && window.NotificationStore.BIsUserInGame())
)
return;
if (toast.playSound) this.audioModule?.PlayNavSound(toast.sound);
if (toast.showToast) {
window.NotificationStore.m_rgNotificationToasts.push(toastData);
window.NotificationStore.DispatchNextToast();
}
window.NotificationStore.m_rgNotificationToasts.push(toastData);
window.NotificationStore.DispatchNextToast();
}
deinit() {