Compare commits

..

40 Commits

Author SHA1 Message Date
TrainDoctor 2518d1a0b3 Update README.md 2022-10-08 18:10:03 -07:00
TrainDoctor 010e6a22ab don't test on live folks -_- 2022-10-08 17:25:34 -07:00
TrainDoctor 134b896e01 Append "-pre" before bumping pre-release version 2022-10-08 17:17:40 -07:00
TrainDoctor 047813b965 Merge branch 'update-ci' w/ needed updates 2022-10-08 17:08:36 -07:00
AAGaming dbcb549ae2 use hasattr instead of in 2022-10-08 19:04:09 -04:00
AAGaming d689614c78 possibly fix zip permissions 2022-10-08 17:57:51 -04:00
Devon Schneider ec907627b8 Use a conditional while loop for when the scroll root is not found. (#212)
* Changed back to a while loop for grabbing the scroll root\nAs recursive function exits before it hits 30 iterations in testing.

* Changed so if recursive limit is hit, it returns null instead of attempting to continue.

* Added log messages regarding recursion limits, as well as fixed recursion tracking.

* Removed errant return remaining from scroll root check.
2022-10-08 06:35:21 -07:00
AAGaming a3809222f9 disable file watcher until after 10 seconds 2022-10-07 22:38:20 -04:00
Party Wumpus 86dc706892 README: password typing clarification (#208)
While the video does *show* the password not appearing, he doesn't mention it.
This should reduce the occasional user confusion of passwords not appearing.
2022-10-07 09:35:59 -07:00
Eddie Dover 0e409a9f96 Add plugin description to Store PluginCard (#196)
* Add plugin description to Store PluginCard

The description for plugins is available on the web but not in the store, this attempts to fix the issue.

Unfortunately, my current env is completely Windows based so I cannot test this change locally, and setting up an Arch env in WSL2 is proving to be more time consuming than such a simple PR deserves.

* Removed prefix from description

* Apply suggestions from code review

Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>

* Inline style fix.

Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>

* Prettier formatting fix

* Apply suggestions from code review

Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>

Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>
2022-10-05 14:24:02 -07:00
Devon Schneider d58001c323 Fixed an issue regarding the scroll root path (#201)
* Changed so searching for the scroll root node will look through siblings of children nodes.

* Added a missing await

Seem to have missed an await from when I was converting my proof-of-concept to an async function.

* Minor stylistic change

* Changed where program retries to find the scroll root.
2022-10-04 11:54:54 -07:00
TrainDoctor d727ba72f3 Fix outdated line about manual install 2022-10-02 10:56:06 -07:00
TrainDoctor fa028fa525 Update build.yml 2022-10-02 08:19:41 -07:00
TrainDoctor c947548064 Update build.yml 2022-10-02 08:11:00 -07:00
AAGaming 19d5527bdf ACTUALLY FIX the friends focus workaround 2022-10-02 10:29:33 -04:00
AAGaming ef51b96f08 fix styling on links in markdown 2022-10-02 10:14:51 -04:00
AAGaming 617916e8e5 actually fix focus issue on startup 2022-10-01 23:10:25 -04:00
TrainDoctor 6c4a4d0a44 Update install_release.sh 2022-10-01 19:04:05 -07:00
TrainDoctor bedcb0fb71 Update README.md 2022-10-01 18:58:59 -07:00
AAGaming 2461f52ca7 change console.log to styled log 2022-10-01 21:24:10 -04:00
AAGaming 3c00eb8cf4 fix friends menu focusing itself 2022-10-01 21:22:30 -04:00
TrainDoctor 21e1d8504a Update README.md 2022-10-01 16:17:40 -07:00
Party Wumpus ba93c4add2 Change 'PluginLoader' to 'Decky Loader' in readme (#190)
* (readme) Make store link go to beta.deckbrew + Change wording for clarification

* further change

* change line 26 to say plugin menu

* Change all 'PluginLoader' to 'Decky Loader' in readme

Seems like a reasonable change as Decky was rebranded a while ago
2022-10-01 14:41:09 -07:00
TrainDoctor 61fea41c8a Update package.json 2022-09-30 21:40:51 -07:00
TrainDoctor e40d3e4db5 Removal of legacy link 2022-09-30 21:31:19 -07:00
Lukas Senionis bbad6bf2be fix(pluginview): align icon and plugin name (#185) 2022-09-26 21:51:39 -07:00
Party Wumpus 4e04455163 (readme) Make store link go to beta.deckbrew + Change wording (#184)
* (readme) Make store link go to beta.deckbrew + Change wording for clarification

* further change

* change line 26 to say plugin menu
2022-09-25 10:04:50 -07:00
Dan Burkhardt 314292b042 updated installation steps to reflect confirmation and system reboot step (#180)
Co-authored-by: TrainDoctor <traindoctor@protonmail.com>
2022-09-21 15:05:18 -07:00
Aamir Tahir a264f36966 Add note about opening terminal to README (#176)
Quick note about how to open the terminal using the pre-installed Konsole app for users that are either new to linux or Steam Deck.
2022-09-21 14:56:45 -07:00
AAGaming 60c8c5db42 check for plugin updates after installing a plugin 2022-09-19 21:13:06 -04:00
Trent Callan 852c52c59a Grab Plugin path from find_plugin_folder plugin name is not always folder anme (#178) 2022-09-18 22:13:45 -07:00
Trent Callan 3136ad72ed Download Remote Binaries during Store Install (#177)
* Download Remote Binaries during store install.
Fix Manual Install Modal Type

* Use Unix Line endings. (Thanks WSL)

* Fix Merge Commit with main branch.
2022-09-18 17:20:19 -07:00
TrainDoctor a3659ba425 Oops I need that 2022-09-04 21:14:38 -07:00
TrainDoctor d1887870f5 return default value for out to "" 2022-09-04 21:03:45 -07:00
TrainDoctor 1892403044 better equivalency checking 2022-09-04 20:56:38 -07:00
TrainDoctor f5a1837227 OUT 2022-09-04 20:51:16 -07:00
TrainDoctor 97f95705f8 Fix pre-release, none 2022-09-04 20:39:57 -07:00
TrainDoctor 7c99af9a9a Add more logging and change release bump logic 2022-09-04 20:25:44 -07:00
TrainDoctor b35bd056d5 Work on bump logic 2022-09-04 20:11:06 -07:00
TrainDoctor d2da85460d Update CI to accommodate custom version bumps 2022-09-04 20:04:11 -07:00
18 changed files with 280 additions and 55 deletions
+61 -10
View File
@@ -15,6 +15,15 @@ on:
- none
- prerelease
- release
bump:
type: choice
description: Semver to bump
default: 'none'
options:
- none
- patch
- minor
- major
permissions:
contents: write
@@ -25,6 +34,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Print input
run : |
echo "release: ${{ github.event.inputs.release }}\n"
echo "bump: ${{ github.event.inputs.bump }}\n"
- name: Checkout 🧰
uses: actions/checkout@v3
@@ -106,11 +120,26 @@ jobs:
if [[ "$VERSION" =~ "-pre" ]]; then
printf "is prerelease, bumping to release\n"
OUT=$(semver bump release "$VERSION")
printf "OUT: ${OUT}\n"
printf "OUT: ${OUT}\n"\
printf "bumping by selected type.\n"
if [[ "${{github.event.inputs.bump}}" != "none" ]]; then
OUT=$(semver bump ${{github.event.inputs.bump}} "$OUT")
printf "OUT: ${OUT}\n"
else
printf "no type selected, defaulting to patch.\n"
OUT=$(semver bump patch "$OUT")
printf "OUT: ${OUT}\n"
fi
elif [[ ! "$VERSION" =~ "-pre" ]]; then
printf "previous tag is a release, bumping by a patch\n"
OUT=$(semver bump minor "$VERSION")
printf "OUT: ${OUT}\n"
printf "previous tag is a release, bumping by selected type.\n"
if [[ "${{github.event.inputs.bump}}" != "none" ]]; then
OUT=$(semver bump ${{github.event.inputs.bump}} "$VERSION")
printf "OUT: ${OUT}\n"
else
printf "previous tag is a release, but no bump selected. Defaulting to a patch bump.\n"
OUT=$(semver bump patch "$VERSION")
printf "OUT: ${OUT}\n"
fi
fi
echo "vOUT: v$OUT"
echo ::set-output name=tag_name::v$OUT
@@ -138,7 +167,7 @@ jobs:
needs: build
runs-on: ubuntu-latest
steps:
steps:
- name: Checkout 🧰
uses: actions/checkout@v3
@@ -170,16 +199,38 @@ jobs:
echo "VERS: $VERSION"
OUT=""
if [[ ! "$VERSION" =~ "-pre" ]]; then
printf "is release, bumping minor version and prerel\n"
OUT=$(semver bump minor "$VERSION")
printf "pre-release from release, bumping by selected type and prerel\n"
if [[ ! ${{ github.event.inputs.bump }} == "none" ]]; then
OUT=$(semver bump ${{github.event.inputs.bump}} "$VERSION")
printf "OUT: ${OUT}\n"
else
printf "type not selected, defaulting to patch\n"
OUT=$(semver bump patch "$VERSION")
printf "OUT: ${OUT}\n"
fi
OUT="$OUT-pre"
OUT=$(semver bump prerel "$OUT")
printf "OUT: ${OUT}\n"
elif [[ "$VERSION" =~ "-pre" ]]; then
printf "is a prerelease, bumping prerel\n"
OUT=$(semver bump prerel "$VERSION")
printf "OUT: ${OUT}\n"
printf "pre-release to pre-release, bumping by selected type and or prerel version\n"
if [[ ! ${{ github.event.inputs.bump }} == "none" ]]; then
OUT=$(semver bump ${{github.event.inputs.bump}} "$VERSION")
printf "OUT: ${OUT}\n"
OUT="$OUT-pre"
printf "OUT: ${OUT}\n"
printf "bumping prerel\n"
OUT=$(semver bump prerel "$OUT")
printf "OUT: ${OUT}\n"
else
printf "type not selected, defaulting to new pre-release only\n"
printf "bumping prerel\n"
OUT="$VERSION-pre"
printf "OUT: ${OUT}\n"
OUT=$(semver bump prerel "$OUT")
printf "OUT: ${OUT}\n"
fi
fi
printf "vOUT: v${OUT}\n"
echo ::set-output name=tag_name::v$OUT
- name: Push tag 📤
+15 -13
View File
@@ -1,29 +1,31 @@
# Plugin Loader [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/ZU74G2NJzk)
# Decky Loader [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/ZU74G2NJzk)
![Decky](https://media.discordapp.net/attachments/966017112244125756/1012466063893610506/main.jpg)
Keep an eye on the [Wiki](https://deckbrew.xyz) for more information about Plugin Loader, documentation + tools for plugin development and more.
Keep an eye on the [Wiki](https://deckbrew.xyz) for more information about Decky Loader, documentation + tools for plugin development and more.
## Installation
1. Go into the Steam Deck Settings
2. Under System -> System Settings toggle `Enable Developer Mode`
3. Scroll the sidebar all the way down and click on `Developer`
4. Under Miscellaneous, enable `CEF Remote Debugging`
5. Click on the `STEAM` button and select `Power` -> `Switch to Desktop`
6. Make sure you have a password set with the "passwd" command in terminal to install it ([YouTube Guide](https://www.youtube.com/watch?v=1vOMYGj22rQ)).
7. Open a terminal and paste the following command into it:
- For the latest pre-release:
5. Confirm dialog and wait for system reboot
6. Click on the `STEAM` button and select `Power` -> `Switch to Desktop`
7. Make sure you have a password set with the "passwd" command in terminal to install it ([YouTube Guide](https://www.youtube.com/watch?v=1vOMYGj22rQ)).
- It will look like the password isn't typing properly. That's normal, it's a security feature (Similar to `***` when typing passwords online)
8. Open a terminal ("Konsole" is the pre-installed terminal application) and paste the following command into it:
- For the latest release (recommended for all users):
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/main/dist/install_release.sh | sh`
- For the latest pre-release (testing releases, unlikely to be fully stable):
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/main/dist/install_prerelease.sh | sh`
- For testers/plugin developers:
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/main/dist/install_prerelease.sh | sh`
- [Wiki Link](https://deckbrew.xyz/en/loader-dev/development)
- For the legacy version (unsupported):
- `curl -L https://github.com/SteamDeckHomebrew/decky-loader/raw/legacy/dist/install_release.sh | sh`
7. Done! Reboot back into Gaming mode and enjoy your plugins!
9. Done! Reboot back into Gaming mode and enjoy your plugins!
### Install/Uninstall Plugins
- Using the shopping bag button in the top right corner, you can go to the offical ["Plugin Store"](https://plugins.deckbrew.xyz/)
- Simply copy the plugin's folder into `~/homebrew/plugins`
- Using the shopping bag button in the top right corner of the plugin menu, you can go to the offical Plugin Store ([Web Preview](https://beta.deckbrew.xyz/)).
- Install from URL in the settings menu.
- Use the settings menu to uninstall plugins, this will not remove any files made in different directories by plugins.
### Uninstall
@@ -41,8 +43,8 @@ Keep an eye on the [Wiki](https://deckbrew.xyz) for more information about Plugi
- There is no complete plugin development documentation yet. However a good starting point is the [Plugin Template](https://github.com/SteamDeckHomebrew/decky-plugin-template) repository.
## [Contribution](https://deckbrew.xyz/en/loader-dev/development)
- Please consult the [Wiki](https://deckbrew.xyz/en/loader-dev/development) for installing development versions of PluginLoader.
- This is also useful for Plugin Developers looking to target new but unreleased versions of PluginLoader.
- Please consult the [Wiki](https://deckbrew.xyz/en/loader-dev/development) for installing development versions of Decky Loader.
- This is also useful for Plugin Developers looking to target new but unreleased versions of Decky Loader.
- [Here's how to get the Steam Deck UI on your enviroment of choice.](https://youtu.be/1IAbZte8e7E?t=112)
- (The video shows Windows usage but unless you're using Arch WSL/cygwin this script is unsupported on Windows.)
+2 -1
View File
@@ -1,5 +1,6 @@
{
"inputs": {
"release": "prerelease"
"release": "prerelease",
"bump": "none"
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
{
"inputs": {
"release": "release"
"release": "release",
"bump": "none"
}
}
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
type=$1
# bump=$2
oldartifactsdir="old"
+55 -9
View File
@@ -8,14 +8,14 @@ from concurrent.futures import ProcessPoolExecutor
from hashlib import sha256
from io import BytesIO
from logging import getLogger
from os import path, rename, listdir
from os import R_OK, W_OK, path, rename, listdir, access, mkdir
from shutil import rmtree
from subprocess import call
from time import time
from zipfile import ZipFile
# Local modules
from helpers import get_ssl_context, get_user, get_user_group
from helpers import get_ssl_context, get_user, get_user_group, download_remote_binary_to_path
from injector import get_tab, inject_to_tab
logger = getLogger("Browser")
@@ -47,6 +47,48 @@ class PluginBrowser:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})")
return False
return True
async def _download_remote_binaries_for_plugin_with_name(self, pluginBasePath):
rv = False
try:
packageJsonPath = path.join(pluginBasePath, 'package.json')
pluginBinPath = path.join(pluginBasePath, 'bin')
if access(packageJsonPath, R_OK):
with open(packageJsonPath, 'r') as f:
packageJson = json.load(f)
if len(packageJson["remote_binary"]) > 0:
# create bin directory if needed.
rc=call(["chmod", "-R", "777", pluginBasePath])
if access(pluginBasePath, W_OK):
if not path.exists(pluginBinPath):
mkdir(pluginBinPath)
if not access(pluginBinPath, W_OK):
rc=call(["chmod", "-R", "777", pluginBinPath])
rv = True
for remoteBinary in packageJson["remote_binary"]:
# Required Fields. If any Remote Binary is missing these fail the install.
binName = remoteBinary["name"]
binURL = remoteBinary["url"]
binHash = remoteBinary["sha256hash"]
if not await download_remote_binary_to_path(binURL, binHash, path.join(pluginBinPath, binName)):
rv = False
raise Exception(f"Error Downloading Remote Binary {binName}@{binURL} with hash {binHash} to {path.join(pluginBinPath, binName)}")
code_chown = call(["chown", "-R", get_user()+":"+get_user_group(), self.plugin_path])
rc=call(["chmod", "-R", "555", pluginBasePath])
else:
rv = True
logger.debug(f"No Remote Binaries to Download")
except Exception as e:
rv = False
logger.debug(str(e))
return rv
def find_plugin_folder(self, name):
for folder in listdir(self.plugin_path):
@@ -100,14 +142,18 @@ class PluginBrowser:
logger.debug("Unzipping...")
ret = self._unzip_to_plugin_dir(res_zip, name, hash)
if ret:
logger.info(f"Installed {name} (Version: {version})")
plugin_dir = self.find_plugin_folder(name)
if name in self.loader.plugins:
self.loader.plugins[name].stop()
self.loader.plugins.pop(name, None)
await sleep(1)
self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_dir)
# await inject_to_tab("SP", "window.syncDeckyPlugins()")
ret = await self._download_remote_binaries_for_plugin_with_name(plugin_dir)
if ret:
logger.info(f"Installed {name} (Version: {version})")
if name in self.loader.plugins:
self.loader.plugins[name].stop()
self.loader.plugins.pop(name, None)
await sleep(1)
self.loader.import_plugin(path.join(plugin_dir, "main.py"), plugin_dir)
# await inject_to_tab("SP", "window.syncDeckyPlugins()")
else:
logger.fatal(f"Failed Downloading Remote Binaries")
else:
self.log.fatal(f"SHA-256 Mismatch!!!! {name} (Version: {version})")
if self.loader.watcher:
+28
View File
@@ -2,11 +2,15 @@ import re
import ssl
import subprocess
import uuid
import os
from subprocess import check_output
from time import sleep
from hashlib import sha256
from io import BytesIO
import certifi
from aiohttp.web import Response, middleware
from aiohttp import ClientSession
REMOTE_DEBUGGER_UNIT = "steam-web-debug-portforward.service"
@@ -83,6 +87,30 @@ def get_homebrew_path(home_path = None) -> str:
return str(home_path+"/homebrew")
# return str(home_path+"/homebrew")
# Download Remote Binaries to local Plugin
async def download_remote_binary_to_path(url, binHash, path) -> bool:
rv = False
try:
if os.access(os.path.dirname(path), os.W_OK):
async with ClientSession() as client:
res = await client.get(url, ssl=get_ssl_context())
if res.status == 200:
data = BytesIO(await res.read())
remoteHash = sha256(data.getbuffer()).hexdigest()
if binHash == remoteHash:
data.seek(0)
with open(path, 'wb') as f:
f.write(data.getbuffer())
rv = True
else:
raise Exception(f"Fatal Error: Hash Mismatch for remote binary {path}@{url}")
else:
rv = False
except:
rv = False
return rv
async def is_systemd_unit_active(unit_name: str) -> bool:
res = subprocess.run(["systemctl", "is-active", unit_name], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return res.returncode == 0
+9 -1
View File
@@ -25,7 +25,7 @@ class FileChangeHandler(RegexMatchingEventHandler):
self.logger = getLogger("file-watcher")
self.plugin_path = plugin_path
self.queue = queue
self.disabled = False
self.disabled = True
def maybe_reload(self, src_path):
if self.disabled:
@@ -70,6 +70,7 @@ class Loader:
self.logger.info(f"plugin_path: {self.plugin_path}")
self.plugins = {}
self.watcher = None
self.live_reload = live_reload
if live_reload:
self.reload_queue = Queue()
@@ -78,6 +79,7 @@ class Loader:
self.observer.schedule(self.watcher, self.plugin_path, recursive=True)
self.observer.start()
self.loop.create_task(self.handle_reloads())
self.loop.create_task(self.enable_reload_wait())
server_instance.add_routes([
web.get("/frontend/{path:.*}", self.handle_frontend_assets),
@@ -92,6 +94,12 @@ class Loader:
web.get("/steam_resource/{path:.+}", self.get_steam_resource)
])
async def enable_reload_wait(self):
if self.live_reload:
await sleep(10)
self.logger.info("Hot reload enabled")
self.watcher.disabled = False
async def handle_frontend_assets(self, request):
file = path.join(path.dirname(__file__), "static", request.match_info["path"])
+6 -2
View File
@@ -1,9 +1,13 @@
# Change PyInstaller files permissions
import sys
from subprocess import call
if hasattr(sys, '_MEIPASS'):
call(['chmod', '-R', '755', sys._MEIPASS])
# Full imports
from asyncio import get_event_loop, sleep
from json import dumps, loads
from logging import DEBUG, INFO, basicConfig, getLogger
from os import getenv, path
from subprocess import call
from os import getenv, chmod
from traceback import format_exc
import aiohttp_cors
+7 -2
View File
@@ -13,8 +13,13 @@ sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest release and install it
curl -L https://github.com/SteamDeckHomebrew/PluginLoader/releases/latest/download/PluginLoader --output "${HOMEBREW_FOLDER}/services/PluginLoader"
chmod +x "${HOMEBREW_FOLDER}/services/PluginLoader"
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "false"))")
read VERSION DOWNLOADURL < <(echo $(jq -r '.tag_name, .assets[].browser_download_url' <<< ${RELEASE}))
printf "Installing version %s...\n" "${VERSION}"
curl -L $DOWNLOADURL --output ${HOMEBREW_FOLDER}/services/PluginLoader
chmod +x ${HOMEBREW_FOLDER}/services/PluginLoader
echo $VERSION > ${HOMEBREW_FOLDER}/services/.loader.version
systemctl --user stop plugin_loader 2> /dev/null
systemctl --user disable plugin_loader 2> /dev/null
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "decky_frontend",
"version": "0.0.1",
"version": "2.1.1",
"private": true,
"license": "GPLV2",
"scripts": {
+1
View File
@@ -24,6 +24,7 @@ const Markdown: FunctionComponent<MarkdownProps> = (props) => {
aRef?.current?.click();
props.onDismiss?.();
}}
style={{ display: 'inline' }}
>
<a ref={aRef} {...nodeProps.node.properties}>
{nodeProps.children}
+2 -2
View File
@@ -32,8 +32,8 @@ const PluginView: VFC = () => {
.map(({ name, icon }) => (
<PanelSectionRow key={name}>
<ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<div>{icon}</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
{icon}
<div>{name}</div>
<NotificationBadge show={updates?.has(name)} style={{ top: '-5px', right: '-5px' }} />
</div>
@@ -21,6 +21,7 @@ const PluginInstallModal: FC<PluginInstallModalProps> = ({ artifact, version, ha
setLoading(true);
await onOK();
setTimeout(() => Router.OpenQuickAccessMenu(QuickAccessTab.Decky), 250);
setTimeout(() => window.DeckyPluginLoader.checkPluginUpdates(), 1000);
}}
onCancel={async () => {
await onCancel();
+16 -2
View File
@@ -113,8 +113,22 @@ const PluginCard: FC<PluginCardProps> = ({ plugin }) => {
}}
className="deckyStoreCardInfo"
>
<p className={joinClassNames(staticClasses.PanelSectionRow)}>
<span>Author: {plugin.author}</span>
<p
className={joinClassNames(staticClasses.PanelSectionRow)}
style={{ marginTop: '0px', marginLeft: '16px' }}
>
<span style={{ paddingLeft: '0px' }}>Author: {plugin.author}</span>
</p>
<p
className={joinClassNames(staticClasses.PanelSectionRow)}
style={{
marginLeft: '16px',
marginTop: '0px',
marginBottom: '0px',
marginRight: '16px',
}}
>
<span style={{ paddingLeft: '0px' }}>{plugin.description}</span>
</p>
<p
className={joinClassNames('deckyStoreCardTagsContainer', staticClasses.PanelSectionRow)}
+48 -1
View File
@@ -1,4 +1,16 @@
import { ConfirmModal, ModalRoot, QuickAccessTab, Router, showModal, sleep, staticClasses } from 'decky-frontend-lib';
import {
ConfirmModal,
ModalRoot,
Patch,
QuickAccessTab,
Router,
callOriginal,
findModuleChild,
replacePatch,
showModal,
sleep,
staticClasses,
} from 'decky-frontend-lib';
import { lazy } from 'react';
import { FaPlug } from 'react-icons/fa';
@@ -39,6 +51,8 @@ class PluginLoader extends Logger {
// stores a list of plugin names which requested to be reloaded
private pluginReloadQueue: { name: string; version?: string }[] = [];
private focusWorkaroundPatch?: Patch;
constructor() {
super(PluginLoader.name);
this.log('Initialized');
@@ -83,6 +97,38 @@ class PluginLoader extends Logger {
initFilepickerPatches();
this.updateVersion();
const self = this;
try {
// TODO remove all of this once Valve fixes the bug
const focusManager = findModuleChild((m) => {
if (typeof m !== 'object') return false;
for (let prop in m) {
if (m[prop]?.prototype?.TakeFocus) return m[prop];
}
return false;
});
this.focusWorkaroundPatch = replacePatch(focusManager.prototype, 'TakeFocus', function () {
// @ts-ignore
const classList = this.m_node?.m_element.classList;
if (
// @ts-ignore
(this.m_node?.m_element && classList.contains(staticClasses.TabGroupPanel)) ||
classList.contains('FriendsListTab') ||
classList.contains('FriendsTabList') ||
classList.contains('FriendsListAndChatsSteamDeck')
) {
self.debug('Intercepted friends re-focus');
return true;
}
return callOriginal;
});
} catch (e) {
this.error('Friends focus patch failed', e);
}
}
public async updateVersion() {
@@ -167,6 +213,7 @@ class PluginLoader extends Logger {
this.routerHook.removeRoute('/decky/store');
this.routerHook.removeRoute('/decky/settings');
deinitFilepickerPatches();
this.focusWorkaroundPatch?.unpatch();
}
public unloadPlugin(name: string) {
+24 -10
View File
@@ -47,18 +47,31 @@ class TabsHook extends Logger {
const self = this;
const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current;
let scrollRoot: any;
let currentNode = tree;
async function findScrollRoot(currentNode: any, iters: number): Promise<any> {
if (iters >= 30) {
self.error(
'Scroll root was not found before hitting the recursion limit, a developer will need to increase the limit.',
);
return null;
}
currentNode = currentNode?.child;
if (currentNode?.type?.prototype?.RemoveSmartScrollContainer) {
self.log(`Scroll root was found in ${iters} recursion cycles`);
return currentNode;
}
if (!currentNode) return null;
if (currentNode.sibling) {
let node = await findScrollRoot(currentNode.sibling, iters + 1);
if (node !== null) return node;
}
return await findScrollRoot(currentNode, iters + 1);
}
(async () => {
let iters = 0;
scrollRoot = await findScrollRoot(tree, 0);
while (!scrollRoot) {
iters++;
currentNode = currentNode?.child;
if (iters >= 30 || !currentNode) {
iters = 0;
currentNode = tree;
await sleep(5000);
}
if (currentNode?.type?.prototype?.RemoveSmartScrollContainer) scrollRoot = currentNode;
this.log('Failed to find scroll root node, reattempting in 5 seconds');
await sleep(5000);
scrollRoot = await findScrollRoot(tree, 0);
}
let newQA: any;
let newQATabRenderer: any;
@@ -101,6 +114,7 @@ class TabsHook extends Logger {
});
this.cNode = scrollRoot;
this.cNode.stateNode.forceUpdate();
this.log('Finished initial injection');
})();
}
+1
View File
@@ -27,6 +27,7 @@ class Toaster extends Logger {
async init() {
let instance: any;
while (true) {
instance = findInReactTree(
(document.getElementById('root') as any)._reactRootContainer._internalRoot.current,