Compare commits

...

11 Commits

Author SHA1 Message Date
Party Wumpus 4c95484ccb move check for if plugin was installed to the start of the function (#715) 2024-10-13 12:22:02 -07:00
Mitja Skuver 8d2b252e6d Fixed plugin manual zip installation getting stuck indefinitely (#706) 2024-10-13 12:20:08 -07:00
AAGaming dbd7488d8f lint 2024-10-11 15:05:07 -04:00
AAGaming cff3ca504d bump @decky/ui to fix issues on beta 2024-10-11 15:00:54 -04:00
AAGaming 456ccf479a fix dropdownmultiselect on beta 2024-10-11 14:58:37 -04:00
AAGaming 4f05a001fb fix another toString 2024-10-11 14:58:05 -04:00
AAGaming 43aa0497f4 prevent future issues where toString may not be a function (what) 2024-10-11 14:47:20 -04:00
AAGaming 1781c19c11 Fix broken checkboxes on Beta Steam client (#710) 2024-10-04 13:26:00 -04:00
Lukas Senionis 1ef3cb8307 fix(http_request): remove conflicting CORS headers (#708) 2024-10-04 13:15:58 -04:00
WerWolvTranslationBot 5bc4dc684d Translations update from Weblate (#707)
Co-authored-by: ayssia <nynaevealmearah@gmail.com>
2024-10-03 17:28:00 -04:00
AAGaming 4cff530b52 Fix missing components on Oct 2 2024 Steam Beta (#709) 2024-10-03 17:08:35 -04:00
11 changed files with 74 additions and 35 deletions
+22 -9
View File
@@ -157,8 +157,16 @@ class PluginBrowser:
# Will be set later in code
res_zip = None
# Check if plugin is installed
# Check if plugin was already installed before this
isInstalled = False
try:
pluginFolderPath = self.find_plugin_folder(name)
if pluginFolderPath:
isInstalled = True
except:
logger.error(f"Failed to determine if {name} is already installed, continuing anyway.")
# Preserve plugin order before removing plugin (uninstall alters the order and removes the plugin from the list)
current_plugin_order = self.settings.getSetting("pluginOrder")[:]
if self.loader.watcher:
@@ -213,14 +221,19 @@ class PluginBrowser:
return
else:
name = sub(r"/.+$", "", plugin_json_list[0])
try:
pluginFolderPath = self.find_plugin_folder(name)
if pluginFolderPath:
isInstalled = True
except:
logger.error(f"Failed to determine if {name} is already installed, continuing anyway.")
plugin_json_file = plugin_json_list[0]
name = sub(r"/.+$", "", plugin_json_file)
try:
with plugin_zip.open(plugin_json_file) as f:
plugin_json_data = json.loads(f.read().decode('utf-8'))
plugin_name_from_plugin_json = plugin_json_data.get('name')
if plugin_name_from_plugin_json and plugin_name_from_plugin_json.strip():
logger.info(f"Extracted plugin name from {plugin_json_file}: {plugin_name_from_plugin_json}")
name = plugin_name_from_plugin_json
else:
logger.warning(f"Nonexistent or invalid 'name' key value in {plugin_json_file}. Falling back to extracting from path.")
except Exception as e:
logger.error(f"Failed to read or parse {plugin_json_file}: {str(e)}. Falling back to extracting from path.")
# Check to make sure we got the file
if res_zip is None:
+14 -1
View File
@@ -191,6 +191,15 @@
"testing_title": "测试"
},
"Store": {
"download_progress_info": {
"download_zip": "正在下载插件",
"increment_count": "正在计入下载次数",
"installing_plugin": "正在安装插件",
"open_zip": "正在打开 ZIP 文件",
"parse_zip": "正在解析 ZIP 文件",
"start": "正在初始化",
"uninstalling_previous": "正在卸载之前的版本"
},
"store_contrib": {
"desc": "如果你想要提交你的插件到 Decky 插件商店,请访问 GitHub 上的 SteamDeckHomebrew/decky-plugin-template 存储库。有关开发和分发插件的信息,请查看 README 文件。",
"label": "贡献"
@@ -239,7 +248,11 @@
}
},
"Testing": {
"download": "下载"
"download": "下载",
"error": "安装 PR 时出错",
"header": "以下版本的 Decky Loader 是根据开放的第三方 Pull Request 构建的。Decky Loader 团队尚未验证这些版本的功能或安全性,且它们可能已经过期。",
"loading": "正在加载尚未合并的 Pull Request ...",
"start_download_toast": "正在下载 PR #{{id}}"
},
"TitleView": {
"decky_store_desc": "打开 Decky 商店",
+18 -4
View File
@@ -9,7 +9,7 @@ from traceback import format_exc
from stat import FILE_ATTRIBUTE_HIDDEN # pyright: ignore [reportAttributeAccessIssue, reportUnknownVariableType]
from asyncio import StreamReader, StreamWriter, start_server, gather, open_connection
from aiohttp import ClientSession
from aiohttp import ClientSession, hdrs
from aiohttp.web import Request, StreamResponse, Response, json_response, post
from typing import TYPE_CHECKING, Callable, Coroutine, Dict, Any, List, TypedDict
@@ -153,14 +153,14 @@ class Utilities:
headers["User-Agent"] = helpers.user_agent
for excluded_header in excluded_default_headers:
self.logger.debug(f"Excluding default header {excluded_header}")
if excluded_header in headers:
self.logger.debug(f"Excluding default header {excluded_header}: {headers[excluded_header]}")
del headers[excluded_header]
if "X-Decky-Fetch-Excluded-Headers" in req.headers:
for excluded_header in req.headers["X-Decky-Fetch-Excluded-Headers"].split(", "):
self.logger.debug(f"Excluding header {excluded_header}")
if excluded_header in headers:
self.logger.debug(f"Excluding header {excluded_header}: {headers[excluded_header]}")
del headers[excluded_header]
for header in req.headers:
@@ -187,7 +187,21 @@ class Utilities:
# defeat the point of this proxy.
async with ClientSession(auto_decompress=False) as web:
async with web.request(req.method, url, headers=headers, data=body, ssl=helpers.get_ssl_context()) as web_res:
res = StreamResponse(headers=web_res.headers, status=web_res.status)
# Whenever the aiohttp_cors is used, it expects a near complete control over whatever headers are needed
# for `aiohttp_cors.ResourceOptions`. As a server, if you delegate CORS handling to aiohttp_cors,
# the headers below must NOT be set. Otherwise they would be overwritten by aiohttp_cors and there would be
# logic bugs, so it was probably a smart choice to assert if the headers are present.
#
# However, this request handler method does not act like our own local server, it always acts like a proxy
# where we do not have control over the response headers. For responses that do not allow CORS, we add the support
# via aiohttp_cors. For responses that allow CORS, we have to remove the conflicting headers to allow
# aiohttp_cors handle it for us as if there was no CORS support.
aiohttp_cors_compatible_headers = web_res.headers.copy()
aiohttp_cors_compatible_headers.popall(hdrs.ACCESS_CONTROL_ALLOW_ORIGIN, default=None)
aiohttp_cors_compatible_headers.popall(hdrs.ACCESS_CONTROL_ALLOW_CREDENTIALS, default=None)
aiohttp_cors_compatible_headers.popall(hdrs.ACCESS_CONTROL_EXPOSE_HEADERS, default=None)
res = StreamResponse(headers=aiohttp_cors_compatible_headers, status=web_res.status)
if web_res.headers.get('Transfer-Encoding', '').lower() == 'chunked':
res.enable_chunked_encoding()
+3 -3
View File
@@ -47,7 +47,8 @@
}
},
"dependencies": {
"@decky/ui": "^4.7.2",
"@decky/ui": "^4.8.1",
"compare-versions": "^6.1.1",
"filesize": "^10.1.2",
"i18next": "^23.11.5",
"i18next-http-backend": "^2.5.2",
@@ -55,7 +56,6 @@
"react-i18next": "^14.1.2",
"react-icons": "^5.2.1",
"react-markdown": "^9.0.1",
"remark-gfm": "^4.0.0",
"compare-versions": "^6.1.1"
"remark-gfm": "^4.0.0"
}
}
+5 -5
View File
@@ -9,8 +9,8 @@ importers:
.:
dependencies:
'@decky/ui':
specifier: ^4.7.2
version: 4.7.2
specifier: ^4.8.1
version: 4.8.1
compare-versions:
specifier: ^6.1.1
version: 6.1.1
@@ -218,8 +218,8 @@ packages:
'@decky/api@1.1.1':
resolution: {integrity: sha512-R5fkBRHBt5QIQY7Q0AlbVIhlIZ/nTzwBOoi8Rt4Go2fjFnoMKPInCJl6cPjXzimGwl2pyqKJgY6VnH6ar0XrHQ==}
'@decky/ui@4.7.2':
resolution: {integrity: sha512-jYXVhbyyupXAcCuFqr7G2qjYVjp8hlMGF8zl8ALv67y0YhikAtfhA2rGUjCuaV3kdo9YrpBh8djRUJXdFPg/Eg==}
'@decky/ui@4.8.1':
resolution: {integrity: sha512-lM4jdeyHjIbxHWxDBhbk+GQvdIT50p6RW9DC+oWSWXlaNWU/iG+8aUAcnfxygFkTP43EkCgjFASsYTfB55CMXA==}
'@esbuild/aix-ppc64@0.20.2':
resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==}
@@ -2295,7 +2295,7 @@ snapshots:
'@decky/api@1.1.1': {}
'@decky/ui@4.7.2': {}
'@decky/ui@4.8.1': {}
'@esbuild/aix-ppc64@0.20.2':
optional: true
@@ -2,11 +2,11 @@ import {
DialogButton,
DialogCheckbox,
DialogCheckboxProps,
Export,
Marquee,
Menu,
MenuItem,
findModuleExport,
Module,
findModule,
showContextMenu,
} from '@decky/ui';
import { FC, useCallback, useEffect, useState } from 'react';
@@ -14,9 +14,7 @@ import { useTranslation } from 'react-i18next';
import { FaChevronDown } from 'react-icons/fa';
// TODO add to dfl
const dropDownControlButtonClass = findModuleExport((e: Export) =>
e?.toString()?.includes('gamepaddropdown_DropDownControlButton'),
);
const dropDownControlButtonClasses = findModule((m: Module) => m?.DropDownControlButton && m?.['duration-app-launch']);
const DropdownMultiselectItem: FC<
{
@@ -76,7 +74,7 @@ const DropdownMultiselect: FC<{
alignItems: 'center',
maxWidth: '100%',
}}
className={dropDownControlButtonClass}
className={dropDownControlButtonClasses?.DropDownControlButton}
onClick={(evt) => {
evt.preventDefault();
showContextMenu(
+3 -2
View File
@@ -168,8 +168,9 @@ class PluginLoader extends Logger {
Promise.all([this.getUserInfo(), this.updateVersion()])
.then(() => this.loadPlugins())
.then(() => this.checkPluginUpdates())
.then(() => this.log('Initialized'));
.then(() => this.log('Initialized'))
.then(() => sleep(30000)) // Internet might not immediately be up
.then(() => this.checkPluginUpdates());
}
private checkForSP(): boolean {
+1 -1
View File
@@ -135,7 +135,7 @@ class RouterHook extends Logger {
private async patchDesktopRouter() {
const root = getReactRoot(document.getElementById('root') as any);
const findRouterNode = () =>
findInReactTree(root, (node) => node?.elementType?.type?.toString()?.includes('bShowDesktopUIContent:'));
findInReactTree(root, (node) => node?.elementType?.type?.toString?.()?.includes('bShowDesktopUIContent:'));
let routerNode = findRouterNode();
while (!routerNode) {
this.warn('Failed to find Router node, reattempting in 5 seconds.');
+2 -2
View File
@@ -41,9 +41,9 @@ class TabsHook extends Logger {
init() {
// TODO patch the "embedded" renderer in this module too (seems to be for VR? unsure)
const qamModule = findModuleByExport((e) => e?.type?.toString()?.includes('QuickAccessMenuBrowserView'));
const qamModule = findModuleByExport((e) => e?.type?.toString?.()?.includes('QuickAccessMenuBrowserView'));
const qamRenderer = Object.values(qamModule).find((e: any) =>
e?.type?.toString()?.includes('QuickAccessMenuBrowserView'),
e?.type?.toString?.()?.includes('QuickAccessMenuBrowserView'),
);
const patchHandler = createReactTreePatcher(
+1 -1
View File
@@ -28,7 +28,7 @@ class Toaster extends Logger {
window.__TOASTER_INSTANCE?.deinit?.();
window.__TOASTER_INSTANCE = this;
const ValveToastRenderer = findModuleExport((e) => e?.toString()?.includes(`controller:"notification",method:`));
const ValveToastRenderer = findModuleExport((e) => e?.toString?.()?.includes(`controller:"notification",method:`));
// TODO find a way to undo this if possible?
const patchedRenderer = injectFCTrampoline(ValveToastRenderer);
this.toastPatch = replacePatch(patchedRenderer, 'component', (args: any[]) => {
+1 -1
View File
@@ -30,7 +30,7 @@ while :; do
if [[ $NEWTARGET != "" ]] && [[ $NEWTARGET != $TARGET ]]; then
echo found new tab at $NEWTARGET
TARGET=$NEWTARGET
TARGETURL="devtools://devtools/bundled/inspector.html?remoteFrontend=true&ws=$ADDR/devtools/page/$TARGET"
TARGETURL="http://$ADDR/devtools/inspector.html?ws=$ADDR/devtools/page/$TARGET"
LOCALTARGET=$(echo '{"id": 1, "method": "Target.createTarget", "params": {"background": true, "url": "'$TARGETURL'"}}
{"id": 2, "method": "Target.closeTarget", "params": {"targetId": "'$LOCALTARGET'"}}' \