Compare commits

..

27 Commits

Author SHA1 Message Date
AAGaming 064c897c86 Merge branch 'main' into aa/type-cleanup-py 2023-10-22 19:58:05 -04:00
marios8543 e0592f0959 fix uninstall bug 2023-10-17 18:59:13 +03:00
marios8543 0736a6f995 fix bad type on store.tsx 2023-10-17 16:53:13 +03:00
marios d454a404a0 Merge branch 'main' into aa/type-cleanup-py 2023-10-17 16:50:13 +03:00
marios8543 ffbc79d919 fix bad type on store.tsx 2023-10-17 16:46:53 +03:00
Party Wumpus a2312256b3 fix typo
this is what i get for commiting to main 😔
2023-10-17 16:42:39 +03:00
Party Wumpus 2cdb49168c fix logical error when no store was set 2023-10-17 16:42:39 +03:00
marios8543 944e0e6e07 Fix decky_plugin on windows CI 2023-10-17 16:39:45 +03:00
marios8543 4a17474133 fix decky_plugin path in pyinstaller 2023-10-11 23:46:26 +03:00
AAGaming ade7cb7640 fix paths 2023-09-30 13:15:35 -04:00
AAGaming e8cbeb1805 oops 2023-09-30 12:46:48 -04:00
AAGaming 4b89fc1f9d fix broken import 2023-09-30 12:43:35 -04:00
AAGaming 00e10be93f fix ci (hopefully, because act wont work) 2023-09-30 12:42:02 -04:00
AAGaming b7043655b3 speed up stupid make 2023-09-30 12:36:17 -04:00
WerWolv 75ae7dfe69 Moved locales folder and requirements.txt 2023-09-26 14:58:27 +02:00
WerWolv a0d50baaca Moved main.py 2023-09-26 14:55:09 +02:00
WerWolv 0c2079fa85 Moved backend entirely into the backend folder 2023-09-26 14:54:52 +02:00
AAGaming 3960d28b06 with, not env 2023-09-25 13:37:28 -04:00
AAGaming 2d68809c1b run lint and typecheck on PRs 2023-09-25 13:37:15 -04:00
AAGaming b81c41f667 remove quotes on some types 2023-09-25 13:28:15 -04:00
AAGaming e22cc6269d make ci title consistent 2023-09-25 13:24:31 -04:00
AAGaming 300885f724 move type checking to other workflow, fix TS errors, add TSC checking 2023-09-25 13:23:38 -04:00
AAGaming 5838ddca56 add pyright ci 2023-09-25 13:09:33 -04:00
AAGaming f6401f4995 move to module imports 2023-09-25 13:06:46 -04:00
marios8543 75fbc7524f type hints on main,plugin,updater,utilites.localsocket 2023-09-25 11:27:36 -04:00
AAGaming ecc5f5c2fa begin adding static types to backend code 2023-09-25 11:27:36 -04:00
AAGaming ae399b8c0e remove useless main.py imports 2023-09-25 11:27:36 -04:00
57 changed files with 1172 additions and 2068 deletions
+2 -12
View File
@@ -42,7 +42,7 @@ body:
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.5.7 Stable"
placeholder: "SteamOS 3.4.3 Stable"
validations:
required: true
@@ -67,18 +67,8 @@ body:
- type: textarea
attributes:
label: Backend Logs
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: true
- type: textarea
attributes:
label: Frontend Logs
description: Please copy from your deck ~/.steam/steam/logs/cef_log.txt and ~/.steam/steam/logs/cef_log.previous.txt. Make sure to scrub your Steam username as it may appear in these logs.
placeholder: cef_log.txt
validations:
required: true
+2 -2
View File
@@ -44,10 +44,10 @@ jobs:
run: pnpm run build
- name: Build Python Backend 🛠️
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data "./backend/static;/static" --add-data "./backend/locales;/locales" --add-data "./backend/src/legacy;/src/legacy" --add-data "./plugin;/plugin" --hidden-import=logging.handlers --hidden-import=sqlite3 ./backend/main.py
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data "./backend/static;/static" --add-data "./backend/locales;/locales" --add-data "./backend/src/legacy;/src/legacy" --add-data "./plugin/*;/" --hidden-import=sqlite3 ./backend/main.py
- name: Build Python Backend (noconsole) 🛠️
run: pyinstaller --noconfirm --noconsole --onefile --name "PluginLoader_noconsole" --add-data "./backend/static;/static" --add-data "./backend/locales;/locales" --add-data "./backend/src/legacy;/src/legacy" --add-data "./plugin;/plugin" --hidden-import=logging.handlers --hidden-import=sqlite3 ./backend/main.py
run: pyinstaller --noconfirm --noconsole --onefile --name "PluginLoader_noconsole" --add-data "./backend/static;/static" --add-data "./backend/locales;/locales" --add-data "./backend/src/legacy;/src/legacy" --add-data "./plugin/*;/" --hidden-import=sqlite3 ./backend/main.py
- name: Upload package artifact ⬆️
uses: actions/upload-artifact@v3
+1 -1
View File
@@ -87,7 +87,7 @@ jobs:
run: pnpm run build
- name: Build Python Backend 🛠️
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data ./backend/static:/static --add-data ./backend/locales:/locales --add-data ./backend/src/legacy:/src/legacy --add-data ./plugin:/plugin --hidden-import=logging.handlers --hidden-import=sqlite3 ./backend/main.py
run: pyinstaller --noconfirm --onefile --name "PluginLoader" --add-data ./backend/static:/static --add-data ./backend/locales:/locales --add-data ./backend/src/legacy:/src/legacy --add-data ./plugin/*:/ --hidden-import=sqlite3 ./backend/main.py
- name: Upload package artifact ⬆️
if: ${{ !env.ACT }}
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v41.0.0
uses: tj-actions/changed-files@v35.6.3
with:
separator: ","
files: |
+2 -2
View File
@@ -38,7 +38,7 @@
"type": "shell",
"group": "none",
"detail": "Check for local runs, create a plugins folder",
"command": "rsync -azp --rsh='ssh -p ${config:deckport} ${config:deckkey}' backend/requirements.txt deck@${config:deckip}:${config:deckdir}/homebrew/dev/pluginloader/backend/requirements.txt && ssh deck@${config:deckip} -p ${config:deckport} ${config:deckkey} 'python -m ensurepip && python -m pip install --upgrade --break-system-packages pip && python -m pip install --break-system-packages --upgrade setuptools && python -m pip install --break-system-packages -r ${config:deckdir}/homebrew/dev/pluginloader/backend/requirements.txt'",
"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": []
},
{
@@ -105,7 +105,7 @@
"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='**/__pycache__/' --exclude='.gitignore' . deck@${config:deckip}:${config:deckdir}/homebrew/dev/pluginloader",
"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": []
},
// RUN
+2 -2
View File
@@ -26,10 +26,10 @@ cd ..
if [[ "$type" == "release" ]]; then
printf "release!\n"
act workflow_dispatch -e act/release.json --artifact-server-path act/artifacts --container-architecture linux/amd64 --platform ubuntu-22.04=catthehacker/ubuntu:act-22.04
act workflow_dispatch -e act/release.json --artifact-server-path act/artifacts --container-architecture linux/amd64
elif [[ "$type" == "prerelease" ]]; then
printf "prerelease!\n"
act workflow_dispatch -e act/prerelease.json --artifact-server-path act/artifacts --container-architecture linux/amd64 --platform ubuntu-22.04=catthehacker/ubuntu:act-22.04
act workflow_dispatch -e act/prerelease.json --artifact-server-path act/artifacts --container-architecture linux/amd64
else
printf "Release type unspecified/badly specified.\n"
printf "Options: 'release' or 'prerelease'\n"
-110
View File
@@ -1,110 +0,0 @@
{
"FilePickerIndex": {
"files": {
"file_type": "نوع الملف",
"show_hidden": "أظهر الملفات المخفية",
"all_files": "جميع الملفات"
},
"filter": {
"created_asce": "المنشئة (الأقدم)",
"modified_asce": "المعدلة (الأقدم)",
"modified_desc": "المعدلة (الأحدث)",
"name_asce": "أ-ي",
"name_desc": "أ-ي",
"size_asce": "الحجم ( الأصغر)",
"size_desc": "الحجم ( الأكبر)",
"created_desc": "المنشئة (الأحدث)"
},
"folder": {
"label": "المجلد",
"show_more": "أظهر المزيد من الملفات",
"select": "إستخدم هذا المجلد"
},
"file": {
"select": "إختر هذا الملف"
}
},
"MultiplePluginsInstallModal": {
"confirm": "هل أنت متأكد من التعديلات التالية؟",
"description": {
"reinstall": "إعادة تنصيب {{name}} {{version}}",
"update": "تحديث {{name}} إلى {{version}}",
"install": "تنصيب {{name}} {{version}}"
},
"ok_button": {
"idle": "تأكيد"
}
},
"DropdownMultiselect": {
"button": {
"back": "الخلف"
}
},
"BranchSelect": {
"update_channel": {
"label": "قناة التحديثات",
"prerelease": "الإصدار التجريبي",
"stable": "إصدار مستقر",
"testing": "إصدار تحت الإختبار"
}
},
"PluginCard": {
"plugin_full_access": "هذه الإضافة لديها الصلاحية للوصول لمحتويات Steam Deck.",
"plugin_install": "تنصيب",
"plugin_version_label": "رقم إصدار الإضافة",
"plugin_no_desc": "لا يوجد وصف متاح."
},
"PluginInstallModal": {
"install": {
"button_idle": "تنصيب",
"button_processing": "يتم التنصيب",
"title": "تنصيب {{artifact}}",
"desc": "هل أنت متأكد من رغبتك في تنصيب {{artifact}} {{version}}؟"
},
"reinstall": {
"button_idle": "إعادة تنصيب",
"button_processing": "تتم إعادة التنصيب",
"desc": "هل أنت متأكد من رغبتك في إعادة تنصيب {{artifact}} {{version}}؟",
"title": "إعادة تنصيب {{artifact}}"
},
"update": {
"button_idle": "تحديث",
"button_processing": "يتم التحديث",
"title": "تحديث {{artifact}}",
"desc": "هل أنت متأكد من رغبتك في تحديث {{artifact}} {{version}}؟"
}
},
"PluginListIndex": {
"hide": "إخفاء من قائمة الوصول السريع",
"reinstall": "إعادة التنصيب",
"reload": "إعادة التحميل",
"show": "إظهار في قائمة الوصول السريع",
"unfreeze": "السماح بالتحديثات",
"uninstall": "إزالة التنصيب",
"update_to": "التحديث إلى {{name}}"
},
"PluginListLabel": {
"hidden": "مخفي من قائمة الوصول السريع"
},
"PluginLoader": {
"decky_title": "Decky",
"error": "خطا",
"plugin_uninstall": {
"button": "إزالة التنصيب",
"title": "إزالة التنصيب {{name}}"
}
},
"SettingsDeveloperIndex": {
"react_devtools": {
"ip_label": "عنوان الشبكة"
},
"third_party_plugins": {
"button_zip": "تصفح",
"button_install": "تنصيب"
},
"header": "أخرى"
},
"Developer": {
"5secreload": "إعادة التحميل في 5 ثواني"
}
}
+3 -13
View File
@@ -109,9 +109,7 @@
"hide": "Rychlý přístup: Skrýt",
"update_all_one": "Aktualizovat 1 plugin",
"update_all_few": "Aktualizovat {{count}} pluginů",
"update_all_other": "Aktualizovat {{count}} pluginů",
"freeze": "Pozastavit aktualizace",
"unfreeze": "Povolit aktualizace"
"update_all_other": "Aktualizovat {{count}} pluginů"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -189,8 +187,7 @@
"SettingsIndex": {
"developer_title": "Vývojář",
"general_title": "Obecné",
"plugins_title": "Pluginy",
"testing_title": "Testování"
"plugins_title": "Pluginy"
},
"Store": {
"store_contrib": {
@@ -216,11 +213,7 @@
"about": "O Decky Plugin Store",
"alph_asce": "Abecedně (Z do A)",
"alph_desc": "Abecedně (A do Z)",
"title": "Procházet",
"date_asce": "Nejstarší",
"downloads_desc": "Nejvíce stažené",
"date_desc": "Nejnovější",
"downloads_asce": "Nejméně stažené"
"title": "Procházet"
},
"store_testing_cta": "Zvažte prosím testování nových pluginů, pomůžete tím týmu Decky Loader!",
"store_testing_warning": {
@@ -270,8 +263,5 @@
"TitleView": {
"settings_desc": "Otevřít nastavení Decky",
"decky_store_desc": "Otevřít obchod Decky"
},
"Testing": {
"download": "Stáhnout"
}
}
+20 -95
View File
@@ -8,33 +8,13 @@
}
},
"Developer": {
"disabling": "Deaktiviere React DevTools",
"enabling": "Aktiviere React DevTools",
"disabling": "Deaktiviere",
"enabling": "Aktiviere",
"5secreload": "Neu laden in 5 Sekunden"
},
"FilePickerIndex": {
"folder": {
"select": "Diesen Ordner verwenden",
"label": "Ordner",
"show_more": "Mehr Dateien anzeigen"
},
"filter": {
"name_desc": "A-Z",
"size_asce": "Größe (Kleinste)",
"size_desc": "Größe (Größte)",
"created_asce": "Erstellt (Älteste)",
"created_desc": "Erstellt (Neuste)",
"modified_asce": "Geändert (Älteste)",
"modified_desc": "Geändert (Neuste)",
"name_asce": "Z-A"
},
"file": {
"select": "Diese Datei auswählen"
},
"files": {
"all_files": "Alle Dateien",
"file_type": "Dateityp",
"show_hidden": "Versteckte Dateien anzeigen"
"select": "Diesen Ordner verwenden"
}
},
"PluginCard": {
@@ -71,12 +51,8 @@
"reload": "Neu laden",
"uninstall": "Deinstallieren",
"update_to": "Aktualisieren zu {{name}}",
"update_all_one": "{{count}} Plugin aktualisieren",
"update_all_other": "{{count}} Plugins aktualisieren",
"show": "Schnellzugriff: Anzeigen",
"hide": "Schnellzugriff: Ausblenden",
"freeze": "Updates einfrieren",
"unfreeze": "Updates erlauben"
"update_all_one": "",
"update_all_other": ""
},
"PluginLoader": {
"decky_title": "Decky",
@@ -120,11 +96,6 @@
"desc2": "Fasse in diesem Menü nichts an, es sei denn, du weißt was du tust.",
"label": "Aktiviere Valve-internes Menü",
"desc1": "Aktiviert das Valve-interne Entwickler Menü."
},
"cef_console": {
"button": "Konsole öffnen",
"label": "CEF Konsole",
"desc": "Öffnet die CEF Konsole. Nur für Debugzwecke. Dinge hier sind potentiell gefährlich und sollten nur durch oder unter Anleitung von Pluginentwickler/innen geschehen."
}
},
"SettingsGeneralIndex": {
@@ -143,18 +114,12 @@
},
"updates": {
"header": "Aktualisierungen"
},
"notifications": {
"decky_updates_label": "Decky Update verfügbar",
"header": "Benachrichtigungen",
"plugin_updates_label": "Plugin Updates verfügbar"
}
},
"SettingsIndex": {
"developer_title": "Entwickler",
"general_title": "Allgemein",
"plugins_title": "Erweiterungen",
"testing_title": "Testen"
"plugins_title": "Erweiterungen"
},
"Store": {
"store_contrib": {
@@ -180,27 +145,19 @@
"about": "Über",
"alph_asce": "Alphabetisch (Z zu A)",
"alph_desc": "Alphabetisch (A zu Z)",
"title": "Durchstöbern",
"date_desc": "Neuste Zuerst",
"downloads_asce": "Wenigste Downloads Zuerst",
"downloads_desc": "Meiste Downloads Zuerst",
"date_asce": "Älteste Zuerst"
"title": "Durchstöbern"
},
"store_testing_cta": "Unterstütze das Decky Loader Team mit dem Testen von neuen Erweiterungen!",
"store_testing_warning": {
"label": "Willkommen zum Test Store Kanal",
"desc": "Du kannst diesen Store Kanal nutzen, um brandneue Testversionen von Plugins auszuprobieren. Denk daran Feedback auf GitHub zu hinterlassen, sodass das Plugin für alle Nutzer verbessert werden kann."
}
"store_testing_cta": "Unterstütze das Decky Loader Team mit dem Testen von neuen Erweiterungen!"
},
"StoreSelect": {
"custom_store": {
"label": "Benutzerdefiniertes Store",
"label": "Benutzerdefinierter Marktplatz",
"url_label": "URL"
},
"store_channel": {
"custom": "Benutzerdefiniert",
"default": "Standard",
"label": "Store Kanal",
"label": "Marktplatz Kanal",
"testing": "Test"
}
},
@@ -220,51 +177,19 @@
"no_patch_notes_desc": "Für diese Version gibt es keine Patchnotizen"
},
"PluginView": {
"hidden_one": "{{count}} Plugin ist in dieser Liste ausgeblendet",
"hidden_other": "{{count}} Plugins sind in dieser Liste ausgeblendet"
"hidden_one": "",
"hidden_other": ""
},
"MultiplePluginsInstallModal": {
"title": {
"install_one": "{{count}} Plugin installieren",
"install_other": "{{count}} Plugins installieren",
"mixed_one": "{{count}} Plugin bearbeiten",
"mixed_other": "{{count}} Plugins bearbeiten",
"update_one": "{{count}} Plugin aktualisieren",
"update_other": "{{count}} Plugins aktualisieren",
"reinstall_one": "{{count}} Plugin neu installieren",
"reinstall_other": "{{count}} Plugins neu installieren"
},
"description": {
"install": "{{name}} {{version}} installieren",
"update": "{{name}} auf {{version}} aktualisieren",
"reinstall": "{{name}} {{version}} neu installieren"
},
"confirm": "Bist du sicher, dass du die folgenden Änderungen vornehmen möchtest?",
"ok_button": {
"loading": "An der Arbeit",
"idle": "Bestätigen"
"install_one": "",
"install_other": "",
"mixed_one": "",
"mixed_other": "",
"update_one": "",
"update_other": "",
"reinstall_one": "",
"reinstall_other": ""
}
},
"PluginListLabel": {
"hidden": "Im Schnellzugriff-Menu ausgeblendet"
},
"TitleView": {
"decky_store_desc": "Decky Store Öffnen",
"settings_desc": "Decky Einstellungen Öffnen"
},
"DropdownMultiselect": {
"button": {
"back": "Zurück"
}
},
"FilePickerError": {
"errors": {
"unknown": "Ein unbekannter Fehler ist aufgetreten. Die ursprüngliche Fehlermeldung ist: {{raw_error}}",
"file_not_found": "Der Pfad ist ungültig. Bitte prüfen und erneut eingeben.",
"perm_denied": "Kein Zugriff auf den angegebenen Dateipfad. Bitte prüfen, ob der Nutzer (deck auf dem Steam Deck) die entsprechenden Zugriffsrechte auf den angegebenen Ordner/die angegebene Datei hat."
}
},
"Testing": {
"download": "Download"
}
}
+257 -267
View File
@@ -1,270 +1,260 @@
{
"BranchSelect": {
"update_channel": {
"label": "Update Channel",
"prerelease": "Prerelease",
"stable": "Stable",
"testing": "Testing"
}
},
"Developer": {
"5secreload": "Reloading in 5 seconds",
"disabling": "Disabling React DevTools",
"enabling": "Enabling React DevTools"
},
"DropdownMultiselect": {
"button": {
"back": "Back"
}
},
"FilePickerError": {
"errors": {
"file_not_found": "The path specified is not valid. Please check it and reenter it correctly.",
"perm_denied": "You do not have access to the specified directory. Please check if your user (deck on Steam Deck) has the corresponding permission to access the specified folder/file.",
"unknown": "An unknown error occurred. The raw error is: {{raw_error}}"
}
},
"FilePickerIndex": {
"file": {
"select": "Select this file"
},
"files": {
"all_files": "All Files",
"file_type": "File Type",
"show_hidden": "Show Hidden Files"
},
"filter": {
"created_asce": "Created (Oldest)",
"created_desc": "Created (Newest)",
"modified_asce": "Modified (Oldest)",
"modified_desc": "Modified (Newest)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Size (Smallest)",
"size_desc": "Size (Largest)"
},
"folder": {
"label": "Folder",
"select": "Use this folder",
"show_more": "Show more files"
}
},
"MultiplePluginsInstallModal": {
"confirm": "Are you sure you want to make the following modifications?",
"description": {
"install": "Install {{name}} {{version}}",
"reinstall": "Reinstall {{name}} {{version}}",
"update": "Update {{name}} to {{version}}"
},
"ok_button": {
"idle": "Confirm",
"loading": "Working"
},
"title": {
"install_one": "Install 1 plugin",
"install_other": "Install {{count}} plugins",
"mixed_one": "Modify {{count}} plugin",
"mixed_other": "Modify {{count}} plugins",
"reinstall_one": "Reinstall 1 plugin",
"reinstall_other": "Reinstall {{count}} plugins",
"update_one": "Update 1 plugin",
"update_other": "Update {{count}} plugins"
}
},
"PluginCard": {
"plugin_full_access": "This plugin has full access to your Steam Deck.",
"plugin_install": "Install",
"plugin_no_desc": "No description provided.",
"plugin_version_label": "Plugin Version"
},
"PluginInstallModal": {
"install": {
"button_idle": "Install",
"button_processing": "Installing",
"desc": "Are you sure you want to install {{artifact}} {{version}}?",
"title": "Install {{artifact}}"
},
"no_hash": "This plugin does not have a hash, you are installing it at your own risk.",
"reinstall": {
"button_idle": "Reinstall",
"button_processing": "Reinstalling",
"desc": "Are you sure you want to reinstall {{artifact}} {{version}}?",
"title": "Reinstall {{artifact}}"
},
"update": {
"button_idle": "Update",
"button_processing": "Updating",
"desc": "Are you sure you want to update {{artifact}} {{version}}?",
"title": "Update {{artifact}}"
}
},
"PluginListIndex": {
"freeze": "Freeze updates",
"hide": "Quick access: Hide",
"no_plugin": "No plugins installed!",
"plugin_actions": "Plugin Actions",
"reinstall": "Reinstall",
"reload": "Reload",
"show": "Quick access: Show",
"unfreeze": "Allow updates",
"uninstall": "Uninstall",
"update_all_one": "Update 1 plugin",
"update_all_other": "Update {{count}} plugins",
"update_to": "Update to {{name}}"
},
"PluginListLabel": {
"hidden": "Hidden from the quick access menu"
},
"PluginLoader": {
"decky_title": "Decky",
"decky_update_available": "Update to {{tag_name}} available!",
"error": "Error",
"plugin_error_uninstall": "Loading {{name}} caused an exception as shown above. This usually means that the plugin requires an update for the new version of SteamUI. Check if an update is present or evaluate its removal in the Decky settings, in the Plugins section.",
"plugin_load_error": {
"message": "Error loading plugin {{name}}",
"toast": "Error loading {{name}}"
},
"plugin_uninstall": {
"button": "Uninstall",
"desc": "Are you sure you want to uninstall {{name}}?",
"title": "Uninstall {{name}}"
},
"plugin_update_one": "Updates available for 1 plugin!",
"plugin_update_other": "Updates available for {{count}} plugins!"
},
"PluginView": {
"hidden_one": "1 plugin is hidden from this list",
"hidden_other": "{{count}} plugins are hidden from this list"
},
"RemoteDebugging": {
"remote_cef": {
"desc": "Allow unauthenticated access to the CEF debugger to anyone in your network",
"label": "Allow Remote CEF Debugging"
}
},
"SettingsDeveloperIndex": {
"cef_console": {
"button": "Open Console",
"desc": "Opens the CEF Console. Only useful for debugging purposes. Stuff here is potentially dangerous and should only be used if you are a plugin dev, or are directed here by one.",
"label": "CEF Console"
},
"header": "Other",
"react_devtools": {
"desc": "Enables connection to a computer running React DevTools. Changing this setting will reload Steam. Set the IP address before enabling.",
"ip_label": "IP",
"label": "Enable React DevTools"
},
"third_party_plugins": {
"button_install": "Install",
"button_zip": "Browse",
"header": "Third-Party Plugins",
"label_desc": "URL",
"label_url": "Install Plugin from URL",
"label_zip": "Install Plugin from ZIP File"
},
"valve_internal": {
"desc1": "Enables the Valve internal developer menu.",
"desc2": "Do not touch anything in this menu unless you know what it does.",
"label": "Enable Valve Internal"
}
},
"SettingsGeneralIndex": {
"about": {
"decky_version": "Decky Version",
"header": "About"
},
"beta": {
"header": "Beta participation"
},
"developer_mode": {
"label": "Developer mode"
},
"notifications": {
"decky_updates_label": "Decky update available",
"header": "Notifications",
"plugin_updates_label": "Plugin updates available"
},
"other": {
"header": "Other"
},
"updates": {
"header": "Updates"
}
},
"SettingsIndex": {
"developer_title": "Developer",
"general_title": "General",
"plugins_title": "Plugins",
"testing_title": "Testing"
},
"Store": {
"store_contrib": {
"desc": "If you would like to contribute to the Decky Plugin Store, check the SteamDeckHomebrew/decky-plugin-template repository on GitHub. Information on development and distribution is available in the README.",
"label": "Contributing"
},
"store_filter": {
"label": "Filter",
"label_def": "All"
},
"store_search": {
"label": "Search"
},
"store_sort": {
"label": "Sort",
"label_def": "Last Updated (Newest)"
},
"store_source": {
"desc": "All plugin source code is available on SteamDeckHomebrew/decky-plugin-database repository on GitHub.",
"label": "Source Code"
},
"store_tabs": {
"about": "About",
"alph_asce": "Alphabetical (Z to A)",
"alph_desc": "Alphabetical (A to Z)",
"date_asce": "Oldest First",
"date_desc": "Newest First",
"downloads_asce": "Least Downloaded First",
"downloads_desc": "Most Downloaded First",
"title": "Browse"
},
"store_testing_cta": "Please consider testing new plugins to help the Decky Loader team!",
"store_testing_warning": {
"desc": "You can use this store channel to test bleeding-edge plugin versions. Be sure to leave feedback on GitHub so the plugin can be updated for all users.",
"label": "Welcome to the Testing Store Channel"
}
},
"StoreSelect": {
"custom_store": {
"label": "Custom Store",
"url_label": "URL"
},
"store_channel": {
"custom": "Custom",
"default": "Default",
"label": "Store Channel",
"testing": "Testing"
}
},
"TitleView": {
"decky_store_desc": "Open Decky Store",
"settings_desc": "Open Decky Settings"
},
"Updater": {
"decky_updates": "Decky Updates",
"no_patch_notes_desc": "no patch notes for this version",
"patch_notes_desc": "Patch Notes",
"updates": {
"check_button": "Check For Updates",
"checking": "Checking",
"cur_version": "Current version: {{ver}}",
"install_button": "Install Update",
"label": "Updates",
"lat_version": "Up to date: running {{ver}}",
"reloading": "Reloading",
"updating": "Updating"
}
},
"Testing": {
"download": "Download"
"BranchSelect": {
"update_channel": {
"label": "Update Channel",
"prerelease": "Prerelease",
"stable": "Stable",
"testing": "Testing"
}
},
"Developer": {
"5secreload": "Reloading in 5 seconds",
"disabling": "Disabling React DevTools",
"enabling": "Enabling React DevTools"
},
"DropdownMultiselect": {
"button": {
"back": "Back"
}
},
"FilePickerError": {
"errors": {
"file_not_found": "The path specified is not valid. Please check it and reenter it correctly.",
"perm_denied": "You do not have access to the specified directory. Please check if your user (deck on Steam Deck) has the corresponding permission to access the specified folder/file.",
"unknown": "An unknown error occurred. The raw error is: {{raw_error}}"
}
},
"FilePickerIndex": {
"file": {
"select": "Select this file"
},
"files": {
"all_files": "All Files",
"file_type": "File Type",
"show_hidden": "Show Hidden Files"
},
"filter": {
"created_asce": "Created (Oldest)",
"created_desc": "Created (Newest)",
"modified_asce": "Modified (Oldest)",
"modified_desc": "Modified (Newest)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Size (Smallest)",
"size_desc": "Size (Largest)"
},
"folder": {
"label": "Folder",
"select": "Use this folder",
"show_more": "Show more files"
}
},
"MultiplePluginsInstallModal": {
"confirm": "Are you sure you want to make the following modifications?",
"description": {
"install": "Install {{name}} {{version}}",
"reinstall": "Reinstall {{name}} {{version}}",
"update": "Update {{name}} to {{version}}"
},
"ok_button": {
"idle": "Confirm",
"loading": "Working"
},
"title": {
"install_one": "Install 1 plugin",
"install_other": "Install {{count}} plugins",
"mixed_one": "Modify {{count}} plugin",
"mixed_other": "Modify {{count}} plugins",
"reinstall_one": "Reinstall 1 plugin",
"reinstall_other": "Reinstall {{count}} plugins",
"update_one": "Update 1 plugin",
"update_other": "Update {{count}} plugins"
}
},
"PluginCard": {
"plugin_full_access": "This plugin has full access to your Steam Deck.",
"plugin_install": "Install",
"plugin_no_desc": "No description provided.",
"plugin_version_label": "Plugin Version"
},
"PluginInstallModal": {
"install": {
"button_idle": "Install",
"button_processing": "Installing",
"desc": "Are you sure you want to install {{artifact}} {{version}}?",
"title": "Install {{artifact}}"
},
"no_hash": "This plugin does not have a hash, you are installing it at your own risk.",
"reinstall": {
"button_idle": "Reinstall",
"button_processing": "Reinstalling",
"desc": "Are you sure you want to reinstall {{artifact}} {{version}}?",
"title": "Reinstall {{artifact}}"
},
"update": {
"button_idle": "Update",
"button_processing": "Updating",
"desc": "Are you sure you want to update {{artifact}} {{version}}?",
"title": "Update {{artifact}}"
}
},
"PluginListIndex": {
"hide": "Quick access: Hide",
"no_plugin": "No plugins installed!",
"plugin_actions": "Plugin Actions",
"reinstall": "Reinstall",
"reload": "Reload",
"show": "Quick access: Show",
"uninstall": "Uninstall",
"update_all_one": "Update 1 plugin",
"update_all_other": "Update {{count}} plugins",
"update_to": "Update to {{name}}"
},
"PluginListLabel": {
"hidden": "Hidden from the quick access menu"
},
"PluginLoader": {
"decky_title": "Decky",
"decky_update_available": "Update to {{tag_name}} available!",
"error": "Error",
"plugin_error_uninstall": "Loading {{name}} caused an exception as shown above. This usually means that the plugin requires an update for the new version of SteamUI. Check if an update is present or evaluate its removal in the Decky settings, in the Plugins section.",
"plugin_load_error": {
"message": "Error loading plugin {{name}}",
"toast": "Error loading {{name}}"
},
"plugin_uninstall": {
"button": "Uninstall",
"desc": "Are you sure you want to uninstall {{name}}?",
"title": "Uninstall {{name}}"
},
"plugin_update_one": "Updates available for 1 plugin!",
"plugin_update_other": "Updates available for {{count}} plugins!"
},
"PluginView": {
"hidden_one": "1 plugin is hidden from this list",
"hidden_other": "{{count}} plugins are hidden from this list"
},
"RemoteDebugging": {
"remote_cef": {
"desc": "Allow unauthenticated access to the CEF debugger to anyone in your network",
"label": "Allow Remote CEF Debugging"
}
},
"SettingsDeveloperIndex": {
"cef_console": {
"button": "Open Console",
"desc": "Opens the CEF Console. Only useful for debugging purposes. Stuff here is potentially dangerous and should only be used if you are a plugin dev, or are directed here by one.",
"label": "CEF Console"
},
"header": "Other",
"react_devtools": {
"desc": "Enables connection to a computer running React DevTools. Changing this setting will reload Steam. Set the IP address before enabling.",
"ip_label": "IP",
"label": "Enable React DevTools"
},
"third_party_plugins": {
"button_install": "Install",
"button_zip": "Browse",
"header": "Third-Party Plugins",
"label_desc": "URL",
"label_url": "Install Plugin from URL",
"label_zip": "Install Plugin from ZIP File"
},
"valve_internal": {
"desc1": "Enables the Valve internal developer menu.",
"desc2": "Do not touch anything in this menu unless you know what it does.",
"label": "Enable Valve Internal"
}
},
"SettingsGeneralIndex": {
"about": {
"decky_version": "Decky Version",
"header": "About"
},
"beta": {
"header": "Beta participation"
},
"developer_mode": {
"label": "Developer mode"
},
"notifications": {
"decky_updates_label": "Decky update available",
"header": "Notifications",
"plugin_updates_label": "Plugin updates available"
},
"other": {
"header": "Other"
},
"updates": {
"header": "Updates"
}
},
"SettingsIndex": {
"developer_title": "Developer",
"general_title": "General",
"plugins_title": "Plugins"
},
"Store": {
"store_contrib": {
"desc": "If you would like to contribute to the Decky Plugin Store, check the SteamDeckHomebrew/decky-plugin-template repository on GitHub. Information on development and distribution is available in the README.",
"label": "Contributing"
},
"store_filter": {
"label": "Filter",
"label_def": "All"
},
"store_search": {
"label": "Search"
},
"store_sort": {
"label": "Sort",
"label_def": "Last Updated (Newest)"
},
"store_source": {
"desc": "All plugin source code is available on SteamDeckHomebrew/decky-plugin-database repository on GitHub.",
"label": "Source Code"
},
"store_tabs": {
"about": "About",
"alph_asce": "Alphabetical (Z to A)",
"alph_desc": "Alphabetical (A to Z)",
"title": "Browse"
},
"store_testing_cta": "Please consider testing new plugins to help the Decky Loader team!",
"store_testing_warning": {
"desc": "You can use this store channel to test bleeding-edge plugin versions. Be sure to leave feedback on GitHub so the plugin can be updated for all users.",
"label": "Welcome to the Testing Store Channel"
}
},
"StoreSelect": {
"custom_store": {
"label": "Custom Store",
"url_label": "URL"
},
"store_channel": {
"custom": "Custom",
"default": "Default",
"label": "Store Channel",
"testing": "Testing"
}
},
"TitleView": {
"decky_store_desc": "Open Decky Store",
"settings_desc": "Open Decky Settings"
},
"Updater": {
"decky_updates": "Decky Updates",
"no_patch_notes_desc": "no patch notes for this version",
"patch_notes_desc": "Patch Notes",
"updates": {
"check_button": "Check For Updates",
"checking": "Checking",
"cur_version": "Current version: {{ver}}",
"install_button": "Install Update",
"label": "Updates",
"lat_version": "Up to date: running {{ver}}",
"reloading": "Reloading",
"updating": "Updating"
}
}
}
+28 -104
View File
@@ -17,13 +17,7 @@
"desc1": "Active le menu développeur interne de Valve.",
"desc2": "Ne touchez à rien dans ce menu à moins que vous ne sachiez ce qu'il fait.",
"label": "Activer Valve Internal"
},
"cef_console": {
"button": "Ouvrir la console",
"label": "CEF Console",
"desc": "Ouvre la console CEF. Utile uniquement à des fins de débogage. Les éléments présentés ici sont potentiellement dangereux et ne doivent être utilisés que si vous êtes un développeur de plugins ou si vous êtes dirigé ici par un de ces développeurs."
},
"header": "Autre"
}
},
"BranchSelect": {
"update_channel": {
@@ -62,32 +56,12 @@
},
"Developer": {
"5secreload": "Rechargement dans 5 secondes",
"disabling": "Désactivation des React DevTools",
"enabling": "Activation des React DevTools"
"disabling": "Désactivation",
"enabling": "Activation"
},
"FilePickerIndex": {
"folder": {
"select": "Utiliser ce dossier",
"label": "Dossier",
"show_more": "Afficher plus de fichiers"
},
"files": {
"show_hidden": "Afficher les fichiers cachés",
"all_files": "Tout les fichiers",
"file_type": "Type de fichier"
},
"file": {
"select": "Sélectionner ce fichier"
},
"filter": {
"created_desc": "Création (Plus récent)",
"modified_asce": "Modifié (Plus vieux)",
"modified_desc": "Modifié (Plus récent)",
"created_asce": "Création (Plus vieux)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Taille (Plus petit)",
"size_desc": "Taille (Plus grand)"
"select": "Utiliser ce dossier"
}
},
"PluginCard": {
@@ -124,13 +98,9 @@
"uninstall": "Désinstaller",
"update_to": "Mettre à jour vers {{name}}",
"no_plugin": "Aucun plugin installé !",
"update_all_one": "Mettre à jour 1 plugin",
"update_all_many": "Mettre à jour {{count}} plugins",
"update_all_other": "Mettre à jour {{count}} plugins",
"show": "Accès Rapide : Afficher",
"hide": "Accès rapide : Cacher",
"unfreeze": "Autoriser les mises à jour",
"freeze": "Geler les mises à jour"
"update_all_one": "",
"update_all_many": "",
"update_all_other": ""
},
"PluginLoader": {
"decky_title": "Decky",
@@ -146,9 +116,9 @@
"title": "Désinstaller {{name}}",
"desc": "Êtes-vous sûr.e de vouloir désinstaller {{name}} ?"
},
"plugin_update_one": "Mise à jour disponible pour 1 plugin !",
"plugin_update_many": "Mises à jour disponible pour {{count}} plugins !",
"plugin_update_other": "Mises à jour disponible pour {{count}} plugins !"
"plugin_update_one": "",
"plugin_update_many": "",
"plugin_update_other": ""
},
"RemoteDebugging": {
"remote_cef": {
@@ -172,18 +142,12 @@
},
"updates": {
"header": "Mises à jour"
},
"notifications": {
"decky_updates_label": "Mise à jour Decky disponible",
"header": "Notifications",
"plugin_updates_label": "Mises à jour du plugin disponibles"
}
},
"SettingsIndex": {
"developer_title": "Développeur",
"general_title": "Général",
"plugins_title": "Plugins",
"testing_title": "Essai"
"plugins_title": "Plugins"
},
"Store": {
"store_contrib": {
@@ -209,69 +173,29 @@
"about": "À propos",
"alph_asce": "Alphabétique (Z à A)",
"alph_desc": "Alphabétique (A à Z)",
"title": "Explorer",
"date_asce": "Plus ancien en premier",
"date_desc": "Le plus récent d'abord",
"downloads_asce": "Le moins téléchargé en premier",
"downloads_desc": "Les plus téléchargés en premier"
"title": "Explorer"
},
"store_testing_cta": "Pensez à tester de nouveaux plugins pour aider l'équipe Decky Loader !",
"store_testing_warning": {
"label": "Bienvenue sur la chaîne du magasin de tests",
"desc": "Vous pouvez utiliser cette chaîne de magasin pour tester des versions de plugins. Assurez-vous de laisser des commentaires sur GitHub afin que le plugin puisse être mis à jour pour tous les utilisateurs."
}
"store_testing_cta": "Pensez à tester de nouveaux plugins pour aider l'équipe Decky Loader !"
},
"PluginView": {
"hidden_one": "1 plugin est masqué dans cette liste",
"hidden_many": "{{count}} plugins sont masqués de cette liste",
"hidden_other": "{{count}} plugins sont masqués de cette liste"
"hidden_one": "",
"hidden_many": "",
"hidden_other": ""
},
"MultiplePluginsInstallModal": {
"title": {
"reinstall_one": "Réinstaller 1 plugin",
"reinstall_many": "Réinstaller {{count}} plugins",
"reinstall_other": "Réinstaller {{count}} plugins",
"install_one": "Installer 1 plugin",
"install_many": "Installer {{count}} plugins",
"install_other": "Installer {{count}} plugins",
"mixed_one": "Modifier {{count}} plugin",
"mixed_many": "Modifier {{count}} plugins",
"mixed_other": "Modifier {{count}} plugins",
"update_one": "Mettre à jour 1 plugin",
"update_many": "Mettre à jour {{count}} plugins",
"update_other": "Mettre à jour {{count}} plugins"
},
"confirm": "Êtes-vous sûr de vouloir apporter les modifications suivantes ?",
"description": {
"install": "Installer {{name}} {{version}}",
"update": "Mettre à jour {{name}} à {{version}}",
"reinstall": "Réinstaller {{name}} {{version}}"
},
"ok_button": {
"idle": "Confirmer",
"loading": "En cours"
"reinstall_one": "",
"reinstall_many": "",
"reinstall_other": "",
"install_one": "",
"install_many": "",
"install_other": "",
"mixed_one": "",
"mixed_many": "",
"mixed_other": "",
"update_one": "",
"update_many": "",
"update_other": ""
}
},
"PluginListLabel": {
"hidden": "Caché du menu d'accès rapide"
},
"FilePickerError": {
"errors": {
"perm_denied": "Vous n'avez pas accès au dossier spécifié. Veuillez vérifier que votre utilisateur (deck sur un Steam Deck) possède les permissions requises pour accéder au dossier/fichier spécifié.",
"file_not_found": "Le chemin spécifié n'est pas valide. Veuillez vérifier et essayer à nouveau.",
"unknown": "Une erreur inconnue est survenue. L'erreur est : {{raw_error}}"
}
},
"DropdownMultiselect": {
"button": {
"back": "Retour"
}
},
"TitleView": {
"decky_store_desc": "Ouvrir le magasin Decky",
"settings_desc": "Ouvrir les paramètres de Decky"
},
"Testing": {
"download": "Télécharger"
}
}
+3 -13
View File
@@ -113,9 +113,7 @@
"update_all_one": "Aggiorna un plugin",
"update_all_many": "Aggiorna {{count}} plugins",
"update_all_other": "Aggiorna {{count}} plugins",
"update_to": "Aggiorna a {{name}}",
"unfreeze": "Permetti aggiornamenti",
"freeze": "Congela aggiornamenti"
"update_to": "Aggiorna a {{name}}"
},
"PluginListLabel": {
"hidden": "Nascosto dal menu di accesso rapido"
@@ -201,8 +199,7 @@
"SettingsIndex": {
"developer_title": "Sviluppatore",
"general_title": "Generali",
"plugins_title": "Plugins",
"testing_title": "Testing"
"plugins_title": "Plugins"
},
"Store": {
"store_contrib": {
@@ -228,11 +225,7 @@
"about": "Riguardo a",
"alph_asce": "Alfabetico (Z a A)",
"alph_desc": "Alfabetico (A a Z)",
"title": "Sfoglia",
"date_desc": "Per più recente",
"date_asce": "Per più vecchio",
"downloads_desc": "Per più scaricato",
"downloads_asce": "Per meno scaricato"
"title": "Sfoglia"
},
"store_testing_cta": "Valuta la possibilità di testare nuovi plugin per aiutare il team di Decky Loader!",
"store_testing_warning": {
@@ -270,8 +263,5 @@
"TitleView": {
"settings_desc": "Apri le impostazioni di Decky",
"decky_store_desc": "Apri lo store di Decky"
},
"Testing": {
"download": "Scarica"
}
}
-253
View File
@@ -1,253 +0,0 @@
{
"BranchSelect": {
"update_channel": {
"stable": "安定",
"testing": "テスト",
"label": "アップデートチャンネル",
"prerelease": "プレリリース"
}
},
"DropdownMultiselect": {
"button": {
"back": "戻る"
}
},
"FilePickerIndex": {
"file": {
"select": "ファイルを選択"
},
"files": {
"all_files": "すべてのファイル",
"file_type": "ファイルタイプ",
"show_hidden": "非表示ファイルを表示する"
},
"filter": {
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "サイズ(小さい順)",
"size_desc": "サイズ(大きい順)",
"created_asce": "作成日(古い順)",
"created_desc": "作成日(新しい順)",
"modified_asce": "更新日(古い順)",
"modified_desc": "更新日(新しい順)"
},
"folder": {
"label": "フォルダ",
"select": "このフォルダを使用",
"show_more": "その他のファイルを表示"
}
},
"MultiplePluginsInstallModal": {
"description": {
"install": "インストール {{name}} {{version}}",
"reinstall": "再インストール {{name}} {{version}}",
"update": "アップデート {{name}} {{version}}"
},
"ok_button": {
"idle": "確認",
"loading": "作業中"
},
"title": {
"install_other": "{{count}} 個のプラグインをインストール",
"mixed_other": "{{count}} 個のプラグインを修正",
"update_other": "{{count}} 個のプラグインをアップデート",
"reinstall_other": "{{count}} 個のプラグインを再インストール"
},
"confirm": "以下の変更を加えてもよろしいですか?"
},
"Developer": {
"enabling": "React DevToolsを有効",
"disabling": "React DevToolsを無効",
"5secreload": "5秒以内に再読み込みされます"
},
"PluginInstallModal": {
"install": {
"button_idle": "インストール",
"title": "{{artifact}} をインストール",
"button_processing": "インストール中",
"desc": "{{artifact}} {{version}} をインストールしてもよろしいですか?"
},
"no_hash": "このプラグインにはハッシュがありません。ご自身の責任でインストールしてください。",
"reinstall": {
"button_idle": "再インストール",
"button_processing": "再インストール中",
"desc": "{{artifact}} {{version}} を再インストールしてもよろしいですか?",
"title": "{{artifact}} を再インストール"
},
"update": {
"button_idle": "アップデート",
"title": "{{artifact}} をアップデート",
"desc": "{{artifact}} {{version}} をアップデートしてもよろしいですか?",
"button_processing": "アップデート中"
}
},
"PluginListIndex": {
"hide": "クイックアクセス: 非表示",
"no_plugin": "プラグインがインストールされていません!",
"reinstall": "再インストール",
"reload": "再読み込み",
"uninstall": "アンインストール",
"plugin_actions": "プラグインアクション",
"update_all_other": "{{count}} 個のプラグインをアップデート",
"show": "クイックアクセス: 表示",
"update_to": "{{name}} を更新"
},
"PluginListLabel": {
"hidden": "クイックアクセスメニューから表示されません"
},
"PluginLoader": {
"error": "エラー",
"plugin_load_error": {
"message": "プラグイン {{name}} の読み込みエラー",
"toast": "{{name}} の読み込みエラー"
},
"plugin_uninstall": {
"button": "アンインストール",
"desc": "{{name}} をアンインストールしてもよろしいですか?",
"title": "{{name}} をアンインストール"
},
"decky_title": "Decky",
"decky_update_available": "{{tag_name}} のアップデートが利用可能です!",
"plugin_update_other": "{{count}} 個のプラグインのアップデートが利用可能です!",
"plugin_error_uninstall": "{{name}} プラグインを読み込む際に上記のような例外が発生しました。 これは通常、SteamUIの最新バージョンに合ったプラグインのアップデートが必要な場合に発生します。Decky設定のプラグインセクションでアップデートがあるかどうかを確認するか、アンインストールをお試しください。"
},
"SettingsDeveloperIndex": {
"cef_console": {
"button": "コンソールを開く",
"label": "CEFコンソール",
"desc": "CEFコンソールを開きます。デバッグ目的でのみ使用してください。これらの項目は危険な可能性があるので、プラグイン開発者であるか、開発者のガイドに従う場合のみ使用する必要があります。"
},
"react_devtools": {
"ip_label": "IP",
"label": "React DevTools を有効化",
"desc": "React DevToolsを実行しているコンピューターへの接続を有効にします。この設定を変更すると、Steam が再ロードされます。有効にする前にIPアドレスを設定してください。"
},
"third_party_plugins": {
"button_install": "インストール",
"button_zip": "開く",
"header": "サードパーティプラグイン",
"label_desc": "URL",
"label_url": "URLからプラグインをインストール",
"label_zip": "ZIPファイルからプラグインをインストール"
},
"valve_internal": {
"desc1": "Valveの内部開発者メニューを有効にします。",
"desc2": "このメニューの機能が分からない場合、このメニューには触れないでください。",
"label": "Valve Internalを有効"
},
"header": "その他"
},
"PluginView": {
"hidden_other": "{{count}} 個のプラグインがこのリストから非表示になっています"
},
"SettingsGeneralIndex": {
"about": {
"decky_version": "Deckyバージョン",
"header": "情報"
},
"developer_mode": {
"label": "開発者モード"
},
"notifications": {
"header": "通知",
"plugin_updates_label": "プラグインのアップデートが利用可能な場合に通知",
"decky_updates_label": "Deckyのアップデートが利用可能な場合に通知"
},
"beta": {
"header": "ベータ版への参加"
},
"other": {
"header": "その他"
},
"updates": {
"header": "アップデート"
}
},
"SettingsIndex": {
"developer_title": "開発者",
"plugins_title": "プラグイン",
"general_title": "一般"
},
"Store": {
"store_filter": {
"label": "フィルター",
"label_def": "すべて"
},
"store_search": {
"label": "検索"
},
"store_sort": {
"label": "並べ替え",
"label_def": "直近のアップデート(新しい順)"
},
"store_source": {
"desc": "すべてのプラグインのソース コードは、GitHubのSteamDeckHomebrew/decky-plugin-databaseリポジトリで入手できます。",
"label": "ソースコード"
},
"store_tabs": {
"alph_asce": "アルファベット(Z to A)",
"alph_desc": "アルファベット(A to Z)",
"title": "閲覧",
"about": "概要"
},
"store_testing_warning": {
"label": "テストストア チャンネルへようこそ",
"desc": "このストアチャンネルを使用して、最先端のプラグイン バージョンをテストできます。 すべてのユーザーがプラグインを更新できるように、必ずGitHubにフィードバックを残してください。"
},
"store_contrib": {
"desc": "Decky Plugin Storeに貢献したい場合は、GitHubのSteamDeckHomebrew/decky-plugin-templateリポジトリを確認してください。 開発と配布に関する情報は README で入手できます。",
"label": "貢献"
},
"store_testing_cta": "Decky Loaderチームを支援するために、新しいプラグインのテストを検討してください!"
},
"StoreSelect": {
"custom_store": {
"label": "カスタムストア",
"url_label": "URL"
},
"store_channel": {
"custom": "カスタム",
"default": "デフォルト",
"label": "ストアチャンネル",
"testing": "テスト"
}
},
"TitleView": {
"decky_store_desc": "Deckyストアを開く",
"settings_desc": "Decky設定を開く"
},
"Updater": {
"decky_updates": "Deckyアップデート",
"no_patch_notes_desc": "このバージョンのパッチノートはありません",
"patch_notes_desc": "パッチノート",
"updates": {
"check_button": "アップデートを確認",
"checking": "確認中",
"cur_version": "現在のバージョン: {{ver}}",
"install_button": "アップデートをインストール",
"label": "アップデート",
"lat_version": "アップデート: {{ver}} を実行中",
"reloading": "再読み込み中",
"updating": "アップデート中"
}
},
"FilePickerError": {
"errors": {
"file_not_found": "指定されたパスは無効です。 内容をご確認の上、正しく入力し直してください。",
"unknown": "不明なエラーが発生しました。 エラー内容は次のとおりです: {{raw_error}}",
"perm_denied": "選択したパスへのアクセス権がありません。選択したフォルダ/ファイルのアクセス権がユーザー(Steam Deckのdeckユーザー)に合わせて正しく設定されていることを確認してください。"
}
},
"PluginCard": {
"plugin_version_label": "プラグインバージョン",
"plugin_no_desc": "説明はありません。",
"plugin_full_access": "このプラグインはSteam Deckの全てのアクセス権を持ちます。",
"plugin_install": "インストール"
},
"RemoteDebugging": {
"remote_cef": {
"label": "リモート CEF デバッグを許可する",
"desc": "ネットワーク上のすべてのユーザーにCEFデバッガへの非認証アクセスを許可します"
}
}
}
+3 -13
View File
@@ -97,9 +97,7 @@
"hide": "빠른 액세스 메뉴: 숨김",
"update_all_other": "플러그인 {{count}}개 업데이트",
"no_plugin": "설치된 플러그인이 없습니다!",
"update_to": "{{name}}(으)로 업데이트",
"freeze": "업데이트 일시 중지",
"unfreeze": "업데이트 허용"
"update_to": "{{name}}(으)로 업데이트"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -175,8 +173,7 @@
"SettingsIndex": {
"developer_title": "개발자",
"general_title": "일반",
"plugins_title": "플러그인",
"testing_title": "테스트"
"plugins_title": "플러그인"
},
"Store": {
"store_contrib": {
@@ -202,11 +199,7 @@
"about": "정보",
"alph_asce": "알파벳순 (Z-A)",
"alph_desc": "알파벳순 (A-Z)",
"title": "검색",
"downloads_asce": "다운로드 수 낮은 순",
"date_desc": "최신 순",
"date_asce": "오래된 순",
"downloads_desc": "다운로드 많은 순"
"title": "검색"
},
"store_testing_cta": "새로운 플러그인을 테스트하여 Decky Loader 팀을 도와주세요!",
"store_testing_warning": {
@@ -256,8 +249,5 @@
"TitleView": {
"settings_desc": "Decky 설정 열기",
"decky_store_desc": "Decky 스토어 열기"
},
"Testing": {
"download": "다운로드"
}
}
+89 -116
View File
@@ -1,16 +1,16 @@
{
"BranchSelect": {
"update_channel": {
"prerelease": "Prerelease",
"prerelease": "Vooruitgave",
"stable": "Stabiel",
"label": "Updatekanaal",
"testing": "Testing"
"label": "Update Kanaal",
"testing": "Test"
}
},
"Developer": {
"5secreload": "Bezig met herstarten in 5 seconden",
"disabling": "Bezig met uitschakelen van React DevTools",
"enabling": "Bezig met inschakelen van React DevTools"
"5secreload": "Herlaad in 5 seconden",
"disabling": "Uitschakelen React DevTools",
"enabling": "Inschakelen React DevTools"
},
"DropdownMultiselect": {
"button": {
@@ -19,34 +19,30 @@
},
"FilePickerError": {
"errors": {
"unknown": "Er is een onbekende fout opgetreden. De foutmelding is: {{raw_error}}",
"file_not_found": "Het opgegeven pad is niet geldig. Controleer het en voer het opnieuw correct in.",
"perm_denied": "U heeft geen toegang tot de opgegeven map. Controleer of uw gebruiker (deck op Steam Deck) de juiste permissies heeft om toegang te krijgen tot de opgegeven map/het opgegeven bestand."
"unknown": "Een onbekende fout is opgetreden. De ruwe fout is: {{raw_error}}",
"file_not_found": "Het opgegeven pad is niet geldig. Controleer het en voer het opnieuw correct in."
}
},
"FilePickerIndex": {
"files": {
"all_files": "Alle bestanden",
"file_type": "Bestandstype",
"show_hidden": "Verborgen bestanden tonen"
"show_hidden": "Toon verborgen bestanden"
},
"filter": {
"created_desc": "Aanmaakdatum (nieuwste)",
"modified_asce": "Gewijzigd op (oudste)",
"modified_desc": "Gewijzigd op (nieuwste)",
"name_asce": "Naam (Z-A)",
"name_desc": "Naam (A-Z)",
"size_asce": "Grootte (kleinste)",
"size_desc": "Grootte (grootste)",
"created_asce": "Aanmaakdatum (oudste)"
"created_desc": "Gecreëerd ( Nieuwste)",
"modified_asce": "Veranderd (Oudste)",
"modified_desc": "Veranderd (Nieuwste)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Grootte (Kleinste)",
"size_desc": "Grootte (Grootste)",
"created_asce": "Gecreëerd (Oudste)"
},
"folder": {
"label": "Map",
"select": "Deze map gebruiken",
"show_more": "Meer bestanden tonen"
},
"file": {
"select": "Dit bestand selecteren"
"select": "Gebruik deze map",
"show_more": "Toon meer bestanden"
}
},
"PluginView": {
@@ -54,13 +50,13 @@
"hidden_other": "{{count}} plug-ins zijn verborgen in deze lijst"
},
"PluginListLabel": {
"hidden": "Verborgen in snelle toegang"
"hidden": "Verborgen in het snelmenu"
},
"PluginCard": {
"plugin_install": "Installeren",
"plugin_no_desc": "Geen beschrijving gegeven.",
"plugin_version_label": "Plug-inversie",
"plugin_full_access": "Deze plug-in heeft volledige toegang tot uw Steam Deck."
"plugin_version_label": "Plugin Versie",
"plugin_full_access": "Deze plug-in heeft volledige toegang tot je Steam Deck."
},
"PluginInstallModal": {
"install": {
@@ -69,61 +65,59 @@
"title": "Installeer {{artifact}}",
"desc": "Weet je zeker dat je {{artifact}} {{version}} wilt installeren?"
},
"no_hash": "Deze plug-in heeft geen hash, je installeert deze op eigen risico.",
"no_hash": "Deze plug-in heeft geen hash, u installeert deze op eigen risico.",
"reinstall": {
"button_idle": "Opnieuw installeren",
"button_processing": "Bezig met opnieuw te installeren",
"button_idle": "Herinstalleren",
"button_processing": "Bezig te herinstalleren",
"desc": "Weet je zeker dat je {{artifact}} {{version}} opnieuw wilt installeren?",
"title": "Installeer {{artifact}} opnieuw"
},
"update": {
"button_idle": "Bijwerken",
"button_processing": "Bezig met bijwerken",
"button_idle": "Update",
"button_processing": "Bezig met updaten",
"title": "{{artifact}} bijwerken",
"desc": "Weet je zeker dat je {{artifact}} {{version}} wilt bijwerken?"
"desc": "Weet je zeker dat je {{artifact}} {{version}} wilt updaten?"
}
},
"MultiplePluginsInstallModal": {
"title": {
"mixed_one": "Wijzig 1 plug-in",
"mixed_other": "Wijzig {{count}} plug-ins",
"update_one": "Werk 1 plug-in bij",
"update_other": "Werk {{count}} plug-ins bij",
"install_one": "Installeer 1 plug-in",
"install_other": "Installeer {{count}} plug-ins",
"reinstall_one": "Installeer 1 plug-in opnieuw",
"reinstall_other": "Installeer {{count}} plug-ins opnieuw"
"mixed_one": "Wijzig {{count}} plug-in",
"mixed_other": "Pas {{count}} plug-ins aan",
"update_one": "1 plugin bijwerken",
"update_other": "{{count}} plug-ins bijwerken",
"install_one": "Installeer 1 plugin",
"install_other": "Installeer {{count}} plugins",
"reinstall_one": "1 plugin opnieuw installeren",
"reinstall_other": "{{count}} plugins opnieuw installeren"
},
"ok_button": {
"idle": "Bevestigen",
"loading": "Bezig"
"loading": "Werkend"
},
"confirm": "Weet je zeker dat je de volgende wijzigingen wilt aanbrengen?",
"confirm": "Weet u zeker dat u de volgende wijzigingen wilt aanbrengen?",
"description": {
"install": "Installeer {{name}} {{version}}",
"update": "Werk {{name}} bij naar {{version}}",
"reinstall": "Installeer {{name}} {{version}} opnieuw"
"update": "Update {{name}} naar {{version}}",
"reinstall": "Installeer opnieuw {{name}} {{version}}"
}
},
"PluginListIndex": {
"no_plugin": "Geen plug-ins geïnstalleerd!",
"plugin_actions": "Plug-inacties",
"reload": "Herstarten",
"no_plugin": "Geen plugins geïnstalleerd!",
"plugin_actions": "Plugin Acties",
"reload": "Herladen",
"uninstall": "Verwijderen",
"update_to": "Bijwerken naar {{name}}",
"hide": "Verberg in snelle toegang",
"update_all_one": "Werk 1 plug-in bij",
"update_all_other": "Werk {{count}} plug-ins bij",
"update_to": "Update naar {{name}}",
"hide": "Snelle toegang: Verberg",
"update_all_one": "Update 1 plugin",
"update_all_other": "Update {{count}} plugins",
"reinstall": "Opnieuw installeren",
"show": "Toon in snelle toegang",
"unfreeze": "Updates toestaan",
"freeze": "Updates bevriezen"
"show": "Snelle toegang: Toon"
},
"PluginLoader": {
"decky_title": "Decky",
"error": "Fout",
"plugin_load_error": {
"message": "Fout bij het laden van plug-in {{name}}",
"message": "Fout bij het laden van plugin {{name}}",
"toast": "Fout bij het laden van {{name}}"
},
"plugin_uninstall": {
@@ -131,71 +125,65 @@
"desc": "Weet je zeker dat je {{name}} wilt verwijderen?",
"title": "Verwijder {{name}}"
},
"plugin_update_one": "Updates beschikbaar voor 1 plug-in!",
"plugin_update_other": "Updates beschikbaar voor {{count}} plug-ins!",
"plugin_update_one": "Updates beschikbaar voor 1 plugin!",
"plugin_update_other": "Updates beschikbaar voor {{count}} plugins!",
"decky_update_available": "Update naar {{tag_name}} beschikbaar!",
"plugin_error_uninstall": "Het laden van {{name}} veroorzaakte een fout zoals hierboven weergegeven. Dit betekent meestal dat de plug-in moet worden bijgewerkt voor de nieuwe versie van SteamUI. Controleer of er een update aanwezig is of evalueer de verwijdering ervan in de Decky-instellingen, in het gedeelte Plug-ins."
"plugin_error_uninstall": "Het laden van {{name}} veroorzaakte een uitzondering zoals hierboven weergegeven. Dit betekent meestal dat de plug-in een update vereist voor de nieuwe versie van SteamUI. Controleer of er een update aanwezig is of evalueer de verwijdering ervan in de Decky-instellingen, in het gedeelte Plug-ins."
},
"RemoteDebugging": {
"remote_cef": {
"desc": "Sta ongeauthenticeerde toegang tot de CEF-debugger toe aan iedereen in uw netwerk",
"label": "Externe CEF-debugging toestaan"
"desc": "Sta ongeauthenticeerde toegang tot de CEF-foutopsporing toe aan iedereen in uw netwerk",
"label": "Externe CEF-foutopsporing toestaan"
}
},
"SettingsDeveloperIndex": {
"cef_console": {
"button": "Console openen",
"label": "CEF-console",
"desc": "Opent de CEF-console. Alleen nuttig voor foutopsporingsdoeleinden. Dingen hier zijn potentieel gevaarlijk en mogen alleen worden gebruikt als je een ontwikkelaar van plug-ins bent, of hier door een ontwikkelaar naartoe wordt geleid."
"label": "CEF Bedieningsscherm",
"desc": "Opent de CEF-console. Alleen nuttig voor foutopsporingsdoeleinden. Dingen hier zijn potentieel gevaarlijk en mogen alleen worden gebruikt als u een ontwikkelaar van plug-ins bent, of hier door een ontwikkelaar naartoe wordt geleid."
},
"header": "Overige",
"header": "Andere",
"react_devtools": {
"ip_label": "IP-adres",
"label": "React DevTools inschakelen",
"desc": "Maakt verbinding met een computer met React DevTools mogelijk. Als je deze instelling wijzigt, wordt Steam opnieuw geladen. Stel het IP-adres in voordat je het inschakelt."
"ip_label": "IP",
"label": "Aanzetten React DevTools",
"desc": "Maakt verbinding met een computer met React DevTools mogelijk. Als je deze instelling wijzigt, wordt Steam opnieuw geladen. Stel het IP-adres in voordat u het inschakelt."
},
"third_party_plugins": {
"header": "Plug-ins van derden",
"label_desc": "URL",
"label_url": "Installeer plug-in via een URL",
"label_zip": "Installeer plug-in via een ZIP-bestand",
"label_url": "Installeer Plugin van URL",
"label_zip": "Installeer Plugin van Zip bestand",
"button_install": "Installeren",
"button_zip": "Bladeren"
},
"valve_internal": {
"desc1": "Schakelt het interne ontwikkelaarsmenu van Valve in.",
"desc2": "Pas niets in dit menu aan, tenzij je weet wat het doet.",
"desc2": "Raak niets in dit menu aan tenzij u weet wat het doet.",
"label": "Valve Internal inschakelen"
}
},
"SettingsGeneralIndex": {
"about": {
"decky_version": "Decky-versie",
"decky_version": "Decky versie",
"header": "Over"
},
"beta": {
"header": "Beta-deelname"
"header": "Beta deelname"
},
"developer_mode": {
"label": "Ontwikkelaarsmodus"
"label": "Ontwikkelaars modus"
},
"other": {
"header": "Overige"
},
"updates": {
"header": "Updates"
},
"notifications": {
"decky_updates_label": "Wanneer er een Decky-update beschikbaar is",
"header": "Meldingen",
"plugin_updates_label": "Wanneer er plug-in-updates beschikbaar zijn"
"header": "Nieuwe Versies"
}
},
"SettingsIndex": {
"developer_title": "Ontwikkelaar",
"general_title": "Algemeen",
"plugins_title": "Plug-ins",
"testing_title": "Testen"
"plugins_title": "Plugins"
},
"Store": {
"store_filter": {
@@ -203,68 +191,53 @@
"label_def": "Alles"
},
"store_search": {
"label": "Zoeken"
"label": "Zoek"
},
"store_sort": {
"label": "Sorteren",
"label_def": "Laatst bijgewerkt (nieuwste)"
"label_def": "Laatste Geupdate (Nieuwste)"
},
"store_source": {
"label": "Broncode",
"label": "Bron Code",
"desc": "Alle broncode van de plug-in is beschikbaar in de SteamDeckHomebrew/decky-plugin-database-repository op GitHub."
},
"store_tabs": {
"about": "Over",
"alph_asce": "Alfabetisch (Z naar A)",
"alph_desc": "Alfabetisch (A naar Z)",
"title": "Bladeren",
"date_desc": "Nieuwste eerst",
"downloads_asce": "Minste gedownload eerst",
"downloads_desc": "Meeste gedownload eerst",
"date_asce": "Oudste eerst"
"title": "Bladeren"
},
"store_testing_cta": "Overweeg om nieuwe plug-ins te testen om het Decky Loader-team te helpen!",
"store_testing_cta": "Overweeg alsjeblieft om nieuwe plug-ins te testen om het Decky Loader-team te helpen!",
"store_contrib": {
"desc": "Als je wilt bijdragen aan de Decky Plugin Store, kijk dan in de SteamDeckHomebrew/decky-plugin-template repository op GitHub. Informatie over ontwikkeling en distributie is beschikbaar in de README.",
"label": "Bijdragen"
},
"store_testing_warning": {
"label": "Welkom bij het Testing-winkelkanaal",
"desc": "Je kunt dit winkelkanaal gebruiken om nog in ontwikkeling zijnde plug-inversies te testen. Zorg ervoor dat je feedback geeft op GitHub, zodat de plug-in voor alle gebruikers kan worden bijgewerkt."
"desc": "Als je wilt bijdragen aan de Decky Plugin winkel, kijk dan in de SteamDeckHomebrew/decky-plugin-template repository op GitHub. Informatie over ontwikkeling en distributie is beschikbaar in de README.",
"label": "Bijdragende"
}
},
"StoreSelect": {
"custom_store": {
"label": "Aangepaste winkel",
"label": "Aangepassingen winkel",
"url_label": "URL"
},
"store_channel": {
"custom": "Aangepast",
"custom": "Aanpassingen",
"default": "Standaard",
"label": "Winkelkanaal",
"testing": "Testing"
"label": "Winkel Kanaal",
"testing": "Testen"
}
},
"Updater": {
"patch_notes_desc": "Patch-opmerkingen",
"patch_notes_desc": "Correctie opmerkingen",
"updates": {
"check_button": "Op updates controleren",
"checking": "Bezig met controleren op updates",
"check_button": "Controleer op updates",
"checking": "Controleren",
"cur_version": "Huidige versie: {{ver}}",
"install_button": "Bijwerken",
"label": "Updates",
"lat_version": "Bijwerkt: versie {{ver}}",
"reloading": "Bezig met herstarten",
"updating": "Bezig met bijwerken"
"install_button": "Installeer Update",
"label": "Update",
"lat_version": "Up-to-date: loopt {{ver}}",
"reloading": "Herstarten",
"updating": "Aan het updaten"
},
"decky_updates": "Decky-updates",
"no_patch_notes_desc": "geen patch-opmerkingen voor deze versie"
},
"TitleView": {
"decky_store_desc": "Decky Store openen",
"settings_desc": "Decky-instellingen openen"
},
"Testing": {
"download": "Downloaden"
"decky_updates": "Decky Nieuwe Versies",
"no_patch_notes_desc": "geen correctie-opmerkingen voor deze versie"
}
}
+3 -13
View File
@@ -113,9 +113,7 @@
"reinstall": "Reinstalacja",
"show": "Szybki dostęp: Pokaż",
"uninstall": "Odinstaluj",
"update_to": "Zaktualizuj do {{name}}",
"unfreeze": "Odblokuj aktualizacje",
"freeze": "Zablokuj aktualizacje"
"update_to": "Zaktualizuj do {{name}}"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -201,8 +199,7 @@
"SettingsIndex": {
"developer_title": "Deweloper",
"general_title": "Ogólne",
"plugins_title": "Pluginy",
"testing_title": "Testowanie"
"plugins_title": "Pluginy"
},
"Store": {
"store_contrib": {
@@ -228,11 +225,7 @@
"alph_asce": "Alfabetycznie (od Z do A)",
"alph_desc": "Alfabetycznie (od A do Z)",
"title": "Przeglądaj",
"about": "Informacje",
"date_desc": "Od najnowszych",
"downloads_desc": "Najwięcej pobrań",
"downloads_asce": "Najmniej pobrań",
"date_asce": "Od najstarszych"
"about": "Informacje"
},
"store_testing_cta": "Rozważ przetestowanie nowych pluginów, aby pomóc zespołowi Decky Loader!",
"store_testing_warning": {
@@ -270,8 +263,5 @@
"TitleView": {
"settings_desc": "Otwórz ustawienia Decky",
"decky_store_desc": "Otwórz sklep Decky"
},
"Testing": {
"download": "Pobierz"
}
}
+2 -11
View File
@@ -104,8 +104,7 @@
"update_all_one": "Atualizar 1 plugin",
"update_all_many": "Atualizar {{count}} plugins",
"update_all_other": "Atualizar {{count}} plugins",
"hide": "Acesso Rápido: Ocultar",
"freeze": "Congelar updates"
"hide": "Acesso Rápido: Ocultar"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -211,11 +210,7 @@
"title": "Navegar",
"alph_asce": "Alfabética (Z - A)"
},
"store_testing_cta": "Por favor, considere testar os novos plugins para ajudar o time do Decky Loader!",
"store_testing_warning": {
"desc": "Você pode usar este canal da loja para testar versões avançadas do plugin. Certifique-se de deixar feedback no GitHub para que o plugin possa ser atualizado para todos os usuários.",
"label": "Bem-vindo ao Canal de Testes da Loja"
}
"store_testing_cta": "Por favor, considere testar os novos plugins para ajudar o time do Decky Loader!"
},
"StoreSelect": {
"custom_store": {
@@ -260,9 +255,5 @@
"unknown": "Ocorreu um erro desconhecido. O erro completo é: {{raw_error}}",
"perm_denied": "Você não tem acesso à este diretório. Por favor, verifiquei se seu usuário (deck no Steam Deck) tem as permissões necessárias para acessar este arquivo/pasta."
}
},
"TitleView": {
"decky_store_desc": "Abrir Loja Decky",
"settings_desc": "Abrir Definições Decky"
}
}
+2 -47
View File
@@ -1,27 +1,7 @@
{
"FilePickerIndex": {
"folder": {
"select": "Usar esta pasta",
"label": "Pasta",
"show_more": "Mostrar mais ficheiros"
},
"file": {
"select": "Selecionar este ficheiro"
},
"filter": {
"size_desc": "Tamanho (maior)",
"created_asce": "Criado (mais antigo)",
"created_desc": "Criado (mais recente)",
"modified_asce": "Modificado (mais antigo)",
"modified_desc": "Modificado (mais recente)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Tamanho (mais pequeno)"
},
"files": {
"file_type": "Tipo de ficheiro",
"show_hidden": "Mostrar ficheiros ocultos",
"all_files": "Todos os ficheiros"
"select": "Usar esta pasta"
}
},
"PluginView": {
@@ -177,11 +157,6 @@
},
"updates": {
"header": "Actualizações"
},
"notifications": {
"decky_updates_label": "Atualização Decky disponível",
"header": "Notificações",
"plugin_updates_label": "Atualizações de plugins disponíveis"
}
},
"SettingsIndex": {
@@ -215,11 +190,7 @@
"alph_desc": "Alfabeticamente (A-Z)",
"title": "Navegar"
},
"store_testing_cta": "Testa novos plugins e ajuda a equipa do Decky Loader!",
"store_testing_warning": {
"desc": "Pode usar esta versão da loja para testar versões experimentais de plugins. Certifique-se de deixar feedback no GitHub para que o plugin possa ser atualizado para todos os utilizadores.",
"label": "Bem-vindo ao Canal de Testes da Loja"
}
"store_testing_cta": "Testa novos plugins e ajuda a equipa do Decky Loader!"
},
"StoreSelect": {
"custom_store": {
@@ -247,21 +218,5 @@
"reloading": "Recarregar",
"install_button": "Instalar actualização"
}
},
"FilePickerError": {
"errors": {
"perm_denied": "Não tem acesso ao diretório especificado. Por favor, verifique se o seu utilizador (deck na Steam Deck) possui as permissões correspondentes para aceder à pasta/ficheiro especificado.",
"unknown": "Ocorreu um erro desconhecido. O erro é: {{raw_error}}",
"file_not_found": "O caminho especificado não é válido. Por favor, verifique e insira-o corretamente."
}
},
"TitleView": {
"decky_store_desc": "Abrir a Loja Decky",
"settings_desc": "Abrir as Definições Decky"
},
"DropdownMultiselect": {
"button": {
"back": "Voltar"
}
}
}
+3 -13
View File
@@ -36,9 +36,7 @@
"show": "Быстрый доступ: Показать",
"plugin_actions": "Действия с плагинами",
"no_plugin": "Не установлено ни одного плагина!",
"reinstall": "Переустановить",
"freeze": "Остановить обновления",
"unfreeze": "Разрешить обновления"
"reinstall": "Переустановить"
},
"PluginLoader": {
"plugin_update_one": "Обновления доступны для {{count}} плагина!",
@@ -186,11 +184,7 @@
"about": "Информация",
"alph_desc": "По алфавиту (A - Z)",
"title": "Обзор",
"alph_asce": "По алфавиту (Z - A)",
"date_asce": "Сначала старые",
"date_desc": "Сначала новые",
"downloads_asce": "Наименее загружаемые сначала",
"downloads_desc": "Наиболее загружаемые сначала"
"alph_asce": "По алфавиту (Z - A)"
},
"store_testing_cta": "Пожалуйста, рассмотрите возможность тестирования новых плагинов, чтобы помочь команде Decky Loader!",
"store_contrib": {
@@ -264,14 +258,10 @@
"SettingsIndex": {
"developer_title": "Разработчик",
"general_title": "Общее",
"plugins_title": "Плагины",
"testing_title": "Тестирование"
"plugins_title": "Плагины"
},
"TitleView": {
"decky_store_desc": "Открыть магазин Decky",
"settings_desc": "Открыть настройки Decky"
},
"Testing": {
"download": "Загрузить"
}
}
-68
View File
@@ -1,68 +0,0 @@
{
"BranchSelect": {
"update_channel": {
"prerelease": "Förhandsversion",
"stable": "Stabil",
"testing": "Testning",
"label": "Uppdateringskanal"
}
},
"Developer": {
"5secreload": "Omladdning på 5 sekunder",
"disabling": "Inaktivera React DevTools",
"enabling": "Aktivera React DevTools"
},
"DropdownMultiselect": {
"button": {
"back": "Tillbaka"
}
},
"FilePickerError": {
"errors": {
"file_not_found": "Den angivna sökvägen är inte giltig. Kontrollera den och ange den korrekt igen.",
"unknown": "Ett okänt fel har inträffat. Det råa felet är: {{raw_error}}",
"perm_denied": "Du har inte tillgång till den angivna katalogen. Kontrollera om din användare (deck på Steam Deck) har motsvarande behörighet för att komma åt den angivna mappen/filen."
}
},
"FilePickerIndex": {
"file": {
"select": "Välj denna fil"
},
"files": {
"all_files": "Alla Filer",
"file_type": "Filtyp",
"show_hidden": "Visa dolda filer"
},
"filter": {
"created_asce": "Skapad (Äldst)",
"created_desc": "Skapad (nyast)",
"modified_asce": "Modifierad (Äldst)",
"modified_desc": "Modifierad (nyaste)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Storlek (minst)",
"size_desc": "Storlek (Störst)"
},
"folder": {
"label": "Mapp",
"select": "Använd denna mapp",
"show_more": "Visa fler filer"
}
},
"MultiplePluginsInstallModal": {
"description": {
"install": "Installera {{name}} {{version}}",
"reinstall": "Installera om {{name}} {{version}}",
"update": "Uppdatera {{name}} {{version}}"
},
"ok_button": {
"idle": "Bekräfta",
"loading": "Arbetar"
},
"title": {
"install_one": "Install 1 tillägg",
"install_other": "Installerar {{count}} tillägg"
},
"confirm": "Är du säker på att du vill göra följande ändringar?"
}
}
-225
View File
@@ -1,225 +0,0 @@
{
"BranchSelect": {
"update_channel": {
"prerelease": "Önsürüm",
"stable": "Stabil",
"testing": "Test",
"label": "Güncelleme Kanalı"
}
},
"DropdownMultiselect": {
"button": {
"back": "Geri"
}
},
"FilePickerIndex": {
"file": {
"select": "Bu dosyayı seçin"
},
"files": {
"all_files": "Tüm Dosyalar",
"file_type": "Dosya Türü",
"show_hidden": "Gizli Dosyaları Göster"
},
"filter": {
"created_asce": "Oluşturuldu (En Eski)",
"created_desc": "Oluşturuldu (En Yeni)",
"modified_asce": "Değiştirildi (En Eski)",
"modified_desc": "Değiştirildi (En Yeni)",
"name_asce": "Z-A",
"name_desc": "A-Z",
"size_asce": "Boyut (En Küçük)",
"size_desc": "Boyut (En Büyük)"
},
"folder": {
"label": "Klasör",
"select": "Bu klasörü kullan",
"show_more": "Daha fazla dosya göster"
}
},
"MultiplePluginsInstallModal": {
"confirm": "Aşağıdaki değişiklikleri yapmak istediğinizden emin misiniz?",
"description": {
"install": "Yükle {{name}} {{version}}",
"reinstall": "Yeniden yükle {{name}} {{version}}"
},
"ok_button": {
"idle": "Onayla",
"loading": "Çalışıyor"
},
"title": {
"reinstall_one": "1 eklentiyi yeniden yükle",
"reinstall_other": "{{count}} eklentiyi yeniden yükle",
"install_one": "1 eklenti yükle",
"install_other": "{{count}} eklenti yükle"
}
},
"PluginCard": {
"plugin_full_access": "Bu eklenti Steam Deck'inize tam erişime sahiptir.",
"plugin_install": "Yükle",
"plugin_version_label": "Eklenti Versiyonu"
},
"PluginInstallModal": {
"install": {
"button_idle": "Yükle",
"button_processing": "Yükleniyor",
"title": "Yükle {{artifact}}",
"desc": "Yüklemek istediğinizden emin misiniz {{artifact}} {{version}}?"
},
"reinstall": {
"button_idle": "Yeniden Yükle",
"desc": "Yeniden yüklemek istediğinizden emin misiniz {{artifact}} {{version}}?",
"title": "Yeniden Yükle {{artifact}}",
"button_processing": "Yeniden Yükleniyor"
},
"update": {
"button_idle": "Güncelle",
"button_processing": "Güncelleniyor",
"title": "Güncelle {{artifact}}",
"desc": "Güncellemek istediğinizden emin misiniz {{artifact}} {{version}}?"
}
},
"PluginListIndex": {
"freeze": "Güncellemeleri durdur",
"hide": "Hızlı erişim: Gizle",
"no_plugin": "Yüklü eklenti yok!",
"plugin_actions": "Eklenti İşlemleri",
"reinstall": "Yeniden Yükle",
"show": "Hızlı erişim: Göster",
"unfreeze": "Güncellemelere izin ver",
"uninstall": "Kaldır",
"update_all_one": "1 eklentiyi güncelle",
"update_all_other": "{{count}} eklentiyi güncelle"
},
"PluginListLabel": {
"hidden": "Hızlı erişim menüsünden gizlenmiş"
},
"PluginLoader": {
"decky_title": "Decky",
"decky_update_available": "{{tag_name}} güncellemesi mevcut!",
"error": "Hata",
"plugin_load_error": {
"toast": "{{name}} yüklenirken hata oluştu",
"message": "{{name}} eklentisi yüklenirken bir hata oluştu"
},
"plugin_uninstall": {
"button": "Kaldır",
"desc": "{{name}} kaldırmak istediğinizden emin misiniz?",
"title": "Kaldır {{name}}"
},
"plugin_update_one": "1 eklenti için güncelleme mevcut!",
"plugin_update_other": "{{count}} eklenti için güncelleme mevcut!"
},
"SettingsDeveloperIndex": {
"cef_console": {
"button": "Konsolu Aç"
},
"header": "Diğer",
"react_devtools": {
"ip_label": "IP"
},
"third_party_plugins": {
"button_install": "Yükle",
"button_zip": "Gözat",
"header": "Üçüncü Parti Eklentiler",
"label_desc": "URL",
"label_url": "URL'den Eklenti Yükle",
"label_zip": "ZIP Dosyasından Eklenti Yükle"
}
},
"SettingsGeneralIndex": {
"about": {
"decky_version": "Decky Versiyonu",
"header": "Hakkında"
},
"beta": {
"header": "Betaya katılım"
},
"developer_mode": {
"label": "Geliştirici modu"
},
"notifications": {
"decky_updates_label": "Decky güncellemesi mevcut",
"header": "Bildirimler",
"plugin_updates_label": "Eklenti güncellemesi mevcut"
},
"other": {
"header": "Diğer"
},
"updates": {
"header": "Güncellemeler"
}
},
"SettingsIndex": {
"developer_title": "Geliştirici",
"general_title": "Genel",
"plugins_title": "Eklentiler"
},
"Store": {
"store_contrib": {
"label": "Katkıda Bulunma"
},
"store_filter": {
"label": "Filtre",
"label_def": "Tümü"
},
"store_search": {
"label": "Ara"
},
"store_sort": {
"label": "Sırala",
"label_def": "Son Güncellenme (En Yeni)"
},
"store_source": {
"label": "Kaynak Kodu"
},
"store_tabs": {
"about": "Hakkında",
"alph_asce": "Alfabetik (Z'den A'ya)",
"alph_desc": "Alfabetik (A'dan Z'ye)",
"date_asce": "Önce En Eski",
"date_desc": "Önce En Yeni",
"downloads_desc": "Önce En Çok İndirilen",
"title": "Gözat",
"downloads_asce": "Önce En Az İndirilen"
}
},
"StoreSelect": {
"custom_store": {
"url_label": "URL"
},
"store_channel": {
"custom": "Özel",
"default": "Varsayılan"
}
},
"TitleView": {
"decky_store_desc": "Decky Mağazasını Aç",
"settings_desc": "Decky Ayarlarını Aç"
},
"Updater": {
"decky_updates": "Decky Güncellemeleri",
"patch_notes_desc": "Yama Notları",
"no_patch_notes_desc": "bu sürüm için yama notları mevcut değil",
"updates": {
"check_button": "Güncellemeleri Kontrol Et",
"checking": "Kontrol ediliyor",
"cur_version": "Mevcut Versiyon: {{ver}}",
"install_button": "Güncellemeyi Yükle",
"label": "Güncellemeler",
"updating": "Güncelleniyor"
}
},
"Testing": {
"download": "İndir"
},
"FilePickerError": {
"errors": {
"file_not_found": "Belirtilen yol geçerli değil. Lütfen yolu kontrol edin ve doğru şekilde yeniden girin."
}
},
"PluginView": {
"hidden_one": "1 eklenti bu listeden gizlenmiştir",
"hidden_other": "{{count}} eklenti bu listeden gizlenmiştir"
}
}
+9 -19
View File
@@ -73,9 +73,7 @@
"update_to": "更新 {{name}}",
"update_all_other": "更新 {{count}} 个插件",
"show": "在快速访问菜单中显示",
"hide": "在快速访问菜单中隐藏",
"unfreeze": "允许更新",
"freeze": "暂停更新"
"hide": "在快速访问菜单中隐藏"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -95,15 +93,15 @@
},
"RemoteDebugging": {
"remote_cef": {
"desc": "允许你网络中的任何人无需身份验证即可访问 CEF 调试器",
"label": "允许 CEF 远程调试"
"desc": "允许你网络中的任何人无需身份验证即可访问CEF调试器",
"label": "允许远程访问CEF调试"
}
},
"SettingsDeveloperIndex": {
"react_devtools": {
"ip_label": "IP",
"label": "启用 React DevTools",
"desc": "允许连接到运行着 React DevTools 的计算机更改此设置将重新加载 Steam请在启用前设置 IP 地址。"
"desc": "允许连接到运行着 React DevTools 的计算机更改此设置将重新加载Steam请在启用前设置IP地址。"
},
"third_party_plugins": {
"button_install": "安装",
@@ -151,13 +149,12 @@
"SettingsIndex": {
"developer_title": "开发者",
"general_title": "通用",
"plugins_title": "插件",
"testing_title": "测试"
"plugins_title": "插件"
},
"Store": {
"store_contrib": {
"label": "贡献",
"desc": "如果你想要提交你的插件到 Decky 插件商店,请访问 GitHub 上的 SteamDeckHomebrew/decky-plugin-template 存储库。有关开发和分发插件的信息,请查看 README 文件。"
"desc": "如果你想要提交你的插件到 Decky 插件商店,请访问 GitHub 上的 SteamDeckHomebrew/decky-plugin-template 存储库,关于开发和分发的相关信息,请查看 README 文件。"
},
"store_filter": {
"label": "过滤器",
@@ -172,17 +169,13 @@
},
"store_source": {
"label": "源代码",
"desc": "所有插件的源代码都可 GitHub 上的 SteamDeckHomebrew/decky-plugin-database 存储库中获得。"
"desc": "所有插件的源代码都可以在 GitHub 上的 SteamDeckHomebrew/decky-plugin-database 存储库中获得。"
},
"store_tabs": {
"about": "关于",
"alph_asce": "字母排序 (Z 到 A)",
"alph_desc": "字母排序 (A 到 Z)",
"title": "浏览",
"downloads_desc": "下载量倒序",
"date_asce": "更新时间正序",
"date_desc": "更新时间倒序",
"downloads_asce": "下载量正序"
"title": "浏览"
},
"store_testing_cta": "请考虑测试新插件以帮助 Decky Loader 团队!",
"store_testing_warning": {
@@ -250,14 +243,11 @@
"errors": {
"file_not_found": "指定路径无效。请检查并输入正确的路径。",
"unknown": "发生了一个未知错误。原始错误为:{{raw_error}}",
"perm_denied": "你没有访问特定目录的权限。请检查你的用户(Steam Deck 中的 deck 账户)是否有权访问特定的文件夹或文件。"
"perm_denied": "你没有访问特定目录的权限。请检查你的用户(Steam Deck 中的 deck 账户)有着相对应的权限以访问特定的文件夹或文件。"
}
},
"TitleView": {
"decky_store_desc": "打开 Decky 商店",
"settings_desc": "打开 Decky 设置"
},
"Testing": {
"download": "下载"
}
}
+7 -25
View File
@@ -39,7 +39,7 @@
},
"PluginCard": {
"plugin_install": "安裝",
"plugin_no_desc": "未提描述。",
"plugin_no_desc": "未提描述。",
"plugin_version_label": "外掛程式版本",
"plugin_full_access": "此外掛程式擁有您的 Steam Deck 的完整存取權。"
},
@@ -73,9 +73,7 @@
"reload": "重新載入",
"show": "快速存取:顯示",
"hide": "快速存取:隱藏",
"update_all_other": "更新 {{count}} 個外掛程式",
"freeze": "禁止更新",
"unfreeze": "允許更新"
"update_all_other": "更新 {{count}} 個外掛程式"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -101,7 +99,7 @@
},
"SettingsDeveloperIndex": {
"third_party_plugins": {
"button_zip": "瀏覽",
"button_zip": "開啟",
"label_desc": "網址",
"label_url": "從網址安裝外掛程式",
"label_zip": "從 ZIP 檔案安裝外掛程式",
@@ -151,8 +149,7 @@
"SettingsIndex": {
"developer_title": "開發人員",
"general_title": "一般",
"plugins_title": "外掛程式",
"testing_title": "測試"
"plugins_title": "外掛程式"
},
"Store": {
"store_contrib": {
@@ -178,17 +175,9 @@
"about": "關於",
"alph_asce": "依字母排序 (Z 到 A)",
"alph_desc": "依字母排序 (A 到 Z)",
"title": "瀏覽",
"downloads_desc": "下載量高到低",
"downloads_asce": "下載量低到高",
"date_asce": "日期舊到新",
"date_desc": "日期新到舊"
"title": "瀏覽"
},
"store_testing_cta": "請考慮測試新的外掛程式來幫助 Decky Loader 團隊!",
"store_testing_warning": {
"label": "歡迎來到測試頻道",
"desc": "您可以使用此商店頻道來體驗測試外掛版本。請務必在 GitHub 上留下回饋,以便為所有用戶更新該外掛程式。"
}
"store_testing_cta": "請考慮測試新的外掛程式來幫助 Decky Loader 團隊!"
},
"StoreSelect": {
"custom_store": {
@@ -237,7 +226,7 @@
"confirm": "您確定要進行以下的修改嗎?",
"description": {
"install": "安裝 {{name}} {{version}}",
"update": "更新 {{name}} 的版本到 {{version}}",
"update": "更新 {{name}} 到 {{version}}",
"reinstall": "重新安裝 {{name}} {{version}}"
}
},
@@ -252,12 +241,5 @@
"button": {
"back": "返回"
}
},
"TitleView": {
"decky_store_desc": "開啟 Decky 商店",
"settings_desc": "開啟 Decky 設定"
},
"Testing": {
"download": "下載"
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
aiohttp==3.9.0
aiohttp==3.8.4
aiohttp-jinja2==1.5.1
aiohttp_cors==0.7.0
watchdog==2.1.7
+8 -41
View File
@@ -10,7 +10,6 @@ from hashlib import sha256
from io import BytesIO
from logging import getLogger
from os import R_OK, W_OK, path, listdir, access, mkdir
from re import sub
from shutil import rmtree
from time import time
from zipfile import ZipFile
@@ -138,7 +137,7 @@ class PluginBrowser:
# logger.debug("current plugins: %s", snapshot_string)
if name in self.plugins:
logger.debug("Plugin %s was found", name)
self.plugins[name].stop(uninstall=True)
self.plugins[name].stop()
logger.debug("Plugin %s was stopped", name)
del self.plugins[name]
logger.debug("Plugin %s was removed from the dictionary", name)
@@ -163,6 +162,12 @@ class PluginBrowser:
current_plugin_order = self.settings.getSetting("pluginOrder")[:]
if self.loader.watcher:
self.loader.watcher.disabled = True
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.")
# Check if the file is a local file or a URL
if artifact.startswith("file://"):
@@ -181,40 +186,6 @@ class PluginBrowser:
else:
logger.fatal(f"Could not fetch from URL. {await res.text()}")
storeUrl = ""
match self.settings.getSetting("store", 0):
case 0: storeUrl = "https://plugins.deckbrew.xyz/plugins" # default
case 1: storeUrl = "https://testing.deckbrew.xyz/plugins" # testing
case 2: storeUrl = self.settings.getSetting("store-url", "https://plugins.deckbrew.xyz/plugins") # custom
case _: storeUrl = "https://plugins.deckbrew.xyz/plugins"
logger.info(f"Incrementing installs for {name} from URL {storeUrl} (version {version})")
async with ClientSession() as client:
res = await client.post(storeUrl+f"/{name}/versions/{version}/increment?isUpdate={isInstalled}", ssl=get_ssl_context())
if res.status != 200:
logger.error(f"Server did not accept install count increment request. code: {res.status}")
if res_zip and version == "dev":
with ZipFile(res_zip) as plugin_zip:
plugin_json_list = [file for file in plugin_zip.namelist() if file.endswith("/plugin.json") and file.count("/") == 1]
if len(plugin_json_list) == 0:
logger.fatal("No plugin.json found in plugin ZIP")
return
elif len(plugin_json_list) > 1:
logger.fatal("Multiple plugin.json found in plugin ZIP")
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.")
# Check to make sure we got the file
if res_zip is None:
logger.fatal(f"Could not fetch {artifact}")
@@ -289,16 +260,12 @@ class PluginBrowser:
Args:
name (string): The name of the plugin
"""
frozen_plugins = self.settings.getSetting("frozenPlugins", [])
if name in frozen_plugins:
frozen_plugins.remove(name)
self.settings.setSetting("frozenPlugins", frozen_plugins)
hidden_plugins = self.settings.getSetting("hiddenPlugins", [])
if name in hidden_plugins:
hidden_plugins.remove(name)
self.settings.setSetting("hiddenPlugins", hidden_plugins)
plugin_order = self.settings.getSetting("pluginOrder", [])
if name in plugin_order:
+2 -2
View File
@@ -412,7 +412,7 @@ async def get_tab_lambda(test: Callable[[Tab], bool]) -> Tab:
SHARED_CTX_NAMES = ["SharedJSContext", "Steam Shared Context presented by Valve™", "Steam", "SP"]
CLOSEABLE_URLS = ["about:blank", "data:text/html,%3Cbody%3E%3C%2Fbody%3E"] # Closing anything other than these *really* likes to crash Steam
DO_NOT_CLOSE_URLS = ["Valve Steam Gamepad/default", "Valve%20Steam%20Gamepad"] # Steam Big Picture Mode tab
DO_NOT_CLOSE_URL = "Valve Steam Gamepad/default" # Steam Big Picture Mode tab
def tab_is_gamepadui(t: Tab) -> bool:
return "https://steamloopback.host/routes/" in t.url and t.title in SHARED_CTX_NAMES
@@ -432,7 +432,7 @@ async def inject_to_tab(tab_name: str, js: str, run_async: bool = False):
async def close_old_tabs():
tabs = await get_tabs()
for t in tabs:
if not t.title or (t.title not in SHARED_CTX_NAMES and any(url in t.url for url in CLOSEABLE_URLS) and not any(url in t.url for url in DO_NOT_CLOSE_URLS)):
if not t.title or (t.title not in SHARED_CTX_NAMES and any(url in t.url for url in CLOSEABLE_URLS) and DO_NOT_CLOSE_URL not in t.url):
logger.debug("Closing tab: " + getattr(t, "title", "Untitled"))
await t.close()
await sleep(0.5)
+4 -4
View File
@@ -5,7 +5,7 @@ from logging import getLogger
from os import listdir, path
from pathlib import Path
from traceback import print_exc
from typing import Any, Tuple, cast
from typing import Any, Tuple
from aiohttp import web
from os.path import exists
@@ -38,7 +38,7 @@ class FileChangeHandler(RegexMatchingEventHandler):
self.queue.put_nowait((path.join(self.plugin_path, plugin_dir, "main.py"), plugin_dir, True))
def on_created(self, event: DirCreatedEvent | FileCreatedEvent):
src_path = cast(str, event.src_path) #type: ignore # this is the correct type for this is in later versions of watchdog
src_path = event.src_path
if "__pycache__" in src_path:
return
@@ -52,7 +52,7 @@ class FileChangeHandler(RegexMatchingEventHandler):
self.maybe_reload(src_path)
def on_modified(self, event: DirModifiedEvent | FileModifiedEvent):
src_path = cast(str, event.src_path) # type: ignore
src_path = event.src_path
if "__pycache__" in src_path:
return
@@ -235,4 +235,4 @@ class Loader:
await self.reload_queue.put((plugin.file, plugin.plugin_directory))
return web.Response(status=200)
return web.Response(status=200)
+2 -30
View File
@@ -58,22 +58,8 @@ def chown(path : str, user : UserType = UserType.HOST_USER, recursive : bool =
def chmod(path : str, permissions : int, recursive : bool = True) -> bool:
if _get_effective_user_id() != 0:
return True
try:
octal_permissions = int(str(permissions), 8)
if recursive:
for root, dirs, files in os.walk(path):
for d in dirs:
os.chmod(os.path.join(root, d), octal_permissions)
for d in files:
os.chmod(os.path.join(root, d), octal_permissions)
os.chmod(path, octal_permissions)
except:
return False
return True
result = call(["chmod", "-R", str(permissions), path] if recursive else ["chmod", str(permissions), path])
return result == 0
def folder_owner(path : str) -> UserType|None:
user_owner = _get_user_owner(path)
@@ -139,29 +125,15 @@ async def service_restart(service_name : str) -> bool:
return res.returncode == 0
async def service_stop(service_name : str) -> bool:
if not await service_active(service_name):
# Service isn't running. pretend we stopped it
return True
cmd = ["systemctl", "stop", service_name]
res = run(cmd, stdout=PIPE, stderr=STDOUT)
return res.returncode == 0
async def service_start(service_name : str) -> bool:
if await service_active(service_name):
# Service is running. pretend we started it
return True
cmd = ["systemctl", "start", service_name]
res = run(cmd, stdout=PIPE, stderr=STDOUT)
return res.returncode == 0
async def restart_webhelper() -> bool:
logger.info("Restarting steamwebhelper")
# TODO move to pkill
res = run(["killall", "-s", "SIGTERM", "steamwebhelper"], stdout=DEVNULL, stderr=DEVNULL)
return res.returncode == 0
def get_privileged_path() -> str:
path = os.getenv("PRIVILEGED_PATH")
+8 -8
View File
@@ -2,9 +2,9 @@
import sys
from typing import Dict
from .localplatform import (chmod, chown, service_stop, service_start,
ON_WINDOWS, ON_LINUX, get_log_level, get_live_reload,
ON_WINDOWS, get_log_level, get_live_reload,
get_server_port, get_server_host, get_chown_plugin_path,
get_privileged_path, restart_webhelper)
get_privileged_path)
if hasattr(sys, '_MEIPASS'):
chmod(sys._MEIPASS, 755) # type: ignore
# Full imports
@@ -25,7 +25,7 @@ from .browser import PluginBrowser
from .helpers import (REMOTE_DEBUGGER_UNIT, csrf_middleware, get_csrf_token,
mkdir_as_user, get_system_pythonpaths, get_effective_user_id)
from .injector import get_gamepadui_tab, Tab
from .injector import get_gamepadui_tab, Tab, close_old_tabs
from .loader import Loader
from .settings import SettingsManager
from .updater import Updater
@@ -158,10 +158,10 @@ class PluginManager:
async def inject_javascript(self, tab: Tab, first: bool=False, request: Request|None=None):
logger.info("Loading Decky frontend!")
try:
# if first:
if ON_LINUX and await tab.has_global_var("deckyHasLoaded", False):
await restart_webhelper()
await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => SteamClient.Browser.RestartJSContext(), 100)}else{window.deckyHasLoaded = true;(async()=>{try{while(!window.webpackChunksteamui){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)
if first:
if await tab.has_global_var("deckyHasLoaded", False):
await close_old_tabs()
await tab.evaluate_js("try{if (window.deckyHasLoaded){setTimeout(() => location.reload(), 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
@@ -182,7 +182,7 @@ def main():
logger.warning(f"decky is running as an unprivileged user, this is not officially supported and may cause issues")
# Append the loader's plugin path to the recognized python paths
sys.path.append(path.join(path.dirname(__file__), "..", "plugin"))
sys.path.append(path.join(path.dirname(__file__), "plugin"))
# Append the system and user python paths
sys.path.extend(get_system_pythonpaths())
+4 -26
View File
@@ -7,7 +7,7 @@ from logging import getLogger
from traceback import format_exc
from os import path, environ
from signal import SIGINT, signal
from sys import exit, path as syspath, modules as sysmodules
from sys import exit, path as syspath
from typing import Any, Dict
from .localsocket import LocalSocket
from .localplatform import setgid, setuid, get_username, get_home_path
@@ -79,11 +79,6 @@ class PluginWrapper:
# append the plugin's `py_modules` to the recognized python paths
syspath.append(path.join(environ["DECKY_PLUGIN_DIR"], "py_modules"))
#TODO: FIX IN A LESS CURSED WAY
keys = [key.replace("src.", "") for key in sysmodules if key.startswith("src.")]
for key in keys:
sysmodules[key] = sysmodules["src"].__dict__[key]
spec = spec_from_file_location("_", self.file)
assert spec is not None
module = module_from_spec(spec)
@@ -113,29 +108,12 @@ class PluginWrapper:
self.log.error("Failed to unload " + self.name + "!\n" + format_exc())
exit(0)
async def _uninstall(self):
try:
self.log.info("Attempting to uninstall with plugin " + self.name + "'s \"_uninstall\" function.\n")
if hasattr(self.Plugin, "_uninstall"):
await self.Plugin._uninstall(self.Plugin)
self.log.info("Uninstalled " + self.name + "\n")
else:
self.log.info("Could not find \"_uninstall\" in " + self.name + "'s main.py" + "\n")
except:
self.log.error("Failed to uninstall " + self.name + "!\n" + format_exc())
exit(0)
async def _on_new_message(self, message : str) -> str|None:
data = loads(message)
if "stop" in data:
self.log.info("Calling Loader unload function.")
await self._unload()
if data.get('uninstall'):
self.log.info("Calling Loader uninstall function.")
await self._uninstall()
get_event_loop().stop()
while get_event_loop().is_running():
await sleep(0)
@@ -158,12 +136,12 @@ class PluginWrapper:
multiprocessing.Process(target=self._init).start()
return self
def stop(self, uninstall: bool = False):
def stop(self):
if self.passive:
return
async def _(self: PluginWrapper):
await self.socket.write_single_line(dumps({ "stop": True, "uninstall": uninstall }, ensure_ascii=False))
await self.socket.write_single_line(dumps({ "stop": True }, ensure_ascii=False))
await self.socket.close_socket_connection()
get_event_loop().create_task(_(self))
@@ -172,7 +150,7 @@ class PluginWrapper:
if self.passive:
raise RuntimeError("This plugin is passive (aka does not implement main.py)")
async with self.method_call_lock:
# reader, writer =
# reader, writer =
await self.socket.get_socket_connection()
await self.socket.write_single_line(dumps({ "method": method_name, "args": kwargs }, ensure_ascii=False))
+40 -114
View File
@@ -1,29 +1,20 @@
from __future__ import annotations
import os
import shutil
from asyncio import sleep
from json.decoder import JSONDecodeError
from logging import getLogger
import os
from os import getcwd, path, remove
import shutil
from typing import List, TYPE_CHECKING, TypedDict
import zipfile
from typing import TYPE_CHECKING, List, TypedDict
if TYPE_CHECKING:
from .main import PluginManager
from .localplatform import chmod, service_restart, ON_LINUX, get_keep_systemd_service, get_selinux
from aiohttp import ClientSession, web
from . import helpers
from .injector import get_gamepadui_tab
from .localplatform import (
ON_LINUX,
ON_WINDOWS,
chmod,
get_keep_systemd_service,
get_selinux,
service_restart,
)
from .settings import SettingsManager
if TYPE_CHECKING:
from .main import PluginManager
logger = getLogger("Updater")
@@ -34,12 +25,6 @@ class RemoteVer(TypedDict):
tag_name: str
prerelease: bool
assets: List[RemoteVerAsset]
class TestingVersion(TypedDict):
id: int
name: str
link: str
head_sha: str
class Updater:
def __init__(self, context: PluginManager) -> None:
@@ -51,9 +36,7 @@ class Updater:
"get_version": self.get_version,
"do_update": self.do_update,
"do_restart": self.do_restart,
"check_for_updates": self.check_for_updates,
"get_testing_versions": self.get_testing_versions,
"download_testing_version": self.download_testing_version
"check_for_updates": self.check_for_updates
}
self.remoteVer: RemoteVer | None = None
self.allRemoteVers: List[RemoteVer] = []
@@ -167,53 +150,6 @@ class Updater:
pass
await sleep(60 * 60 * 6) # 6 hours
async def download_decky_binary(self, download_url: str, version: str, is_zip: bool = False):
download_filename = "PluginLoader" if ON_LINUX else "PluginLoader.exe"
download_temp_filename = download_filename + ".new"
tab = await get_gamepadui_tab()
await tab.open_websocket()
async with ClientSession() as web:
logger.debug("Downloading binary")
async with web.request("GET", download_url, ssl=helpers.get_ssl_context(), allow_redirects=True) as res:
total = int(res.headers.get('content-length', 0))
with open(path.join(getcwd(), download_temp_filename), "wb") as out:
progress = 0
raw = 0
async for c in res.content.iter_chunked(512):
out.write(c)
if total != 0:
raw += len(c)
new_progress = round((raw / total) * 100)
if progress != new_progress:
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:
out.write(version)
if ON_LINUX:
remove(path.join(getcwd(), download_filename))
if (is_zip):
with zipfile.ZipFile(path.join(getcwd(), download_temp_filename), 'r') as file:
file.getinfo(download_filename).filename = download_filename + ".unzipped"
file.extract(download_filename)
remove(path.join(getcwd(), download_temp_filename))
shutil.move(path.join(getcwd(), download_filename + ".unzipped"), path.join(getcwd(), download_filename))
else:
shutil.move(path.join(getcwd(), download_temp_filename), path.join(getcwd(), download_filename))
chmod(path.join(getcwd(), download_filename), 777, False)
if get_selinux():
from asyncio.subprocess import create_subprocess_exec
process = await create_subprocess_exec("chcon", "-t", "bin_t", path.join(getcwd(), download_filename))
logger.info(f"Setting the executable flag with chcon returned {await process.wait()}")
logger.info("Updated loader installation.")
await tab.evaluate_js("window.DeckyUpdater.finish()", False, False)
await self.do_restart()
await tab.close_websocket()
async def do_update(self):
logger.debug("Starting update.")
try:
@@ -225,6 +161,7 @@ class Updater:
version = self.remoteVer["tag_name"]
download_url = None
download_filename = "PluginLoader" if ON_LINUX else "PluginLoader.exe"
download_temp_filename = download_filename + ".new"
for x in self.remoteVer["assets"]:
if x["name"] == download_filename:
@@ -237,6 +174,8 @@ class Updater:
service_url = self.get_service_url()
logger.debug("Retrieved service URL")
tab = await get_gamepadui_tab()
await tab.open_websocket()
async with ClientSession() as web:
if ON_LINUX and not get_keep_systemd_service():
logger.debug("Downloading systemd service")
@@ -264,49 +203,36 @@ class Updater:
os.mkdir(path.join(getcwd(), ".systemd"))
shutil.move(service_file_path, path.join(getcwd(), ".systemd")+"/plugin_loader.service")
await self.download_decky_binary(download_url, version)
logger.debug("Downloading binary")
async with web.request("GET", download_url, ssl=helpers.get_ssl_context(), allow_redirects=True) as res:
total = int(res.headers.get('content-length', 0))
with open(path.join(getcwd(), download_temp_filename), "wb") as out:
progress = 0
raw = 0
async for c in res.content.iter_chunked(512):
out.write(c)
raw += len(c)
new_progress = round((raw / total) * 100)
if progress != new_progress:
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:
out.write(version)
if ON_LINUX:
remove(path.join(getcwd(), download_filename))
shutil.move(path.join(getcwd(), download_temp_filename), path.join(getcwd(), download_filename))
chmod(path.join(getcwd(), download_filename), 777, False)
if get_selinux():
from asyncio.subprocess import create_subprocess_exec
process = await create_subprocess_exec("chcon", "-t", "bin_t", path.join(getcwd(), download_filename))
logger.info(f"Setting the executable flag with chcon returned {await process.wait()}")
logger.info("Updated loader installation.")
await tab.evaluate_js("window.DeckyUpdater.finish()", False, False)
await self.do_restart()
await tab.close_websocket()
async def do_restart(self):
await service_restart("plugin_loader")
async def get_testing_versions(self) -> List[TestingVersion]:
result: List[TestingVersion] = []
async with ClientSession() as web:
async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/pulls",
headers={'X-GitHub-Api-Version': '2022-11-28'}, params={'state':'open'}, ssl=helpers.get_ssl_context()) as res:
open_prs = await res.json()
for pr in open_prs:
result.append({
"id": int(pr['number']),
"name": pr['title'],
"link": pr['html_url'],
"head_sha": pr['head']['sha'],
})
return result
async def download_testing_version(self, pr_id: int, sha_id: str):
down_id = ''
#Get all the associated workflow run for the given sha_id code hash
async with ClientSession() as web:
async with web.request("GET", "https://api.github.com/repos/SteamDeckHomebrew/decky-loader/actions/runs",
headers={'X-GitHub-Api-Version': '2022-11-28'}, params={'head_sha': sha_id}, ssl=helpers.get_ssl_context()) as res:
works = await res.json()
#Iterate over the workflow_run to get the two builds if they exists
for work in works['workflow_runs']:
if ON_WINDOWS and work['name'] == 'Builder Win':
down_id=work['id']
break
elif ON_LINUX and work['name'] == 'Builder':
down_id=work['id']
break
if down_id != '':
async with ClientSession() as web:
async with web.request("GET", f"https://api.github.com/repos/SteamDeckHomebrew/decky-loader/actions/runs/{down_id}/artifacts",
headers={'X-GitHub-Api-Version': '2022-11-28'}, ssl=helpers.get_ssl_context()) as res:
jresp = await res.json()
#If the request found at least one artifact to download...
if int(jresp['total_count']) != 0:
# this assumes that the artifact we want is the first one!
down_link = f"https://nightly.link/SteamDeckHomebrew/decky-loader/actions/artifacts/{jresp['artifacts'][0]['id']}.zip"
#Then fetch it and restart itself
await self.download_decky_binary(down_link, f'PR-{pr_id}' , True)
+2 -2
View File
@@ -238,7 +238,7 @@ class Utilities:
elif include_files:
# Handle requested extensions if present
if len(include_ext) == 0 or 'all_files' in include_ext \
or splitext(file.name)[1].lstrip('.').upper() in (ext.upper() for ext in include_ext):
or splitext(file.name)[1].lstrip('.') in include_ext:
if (is_hidden and include_hidden) or not is_hidden:
files.append({"file": file, "filest": filest, "is_dir": False})
# Filter logic
@@ -360,7 +360,7 @@ class Utilities:
tab = await get_gamepadui_tab()
self.rdt_script_id = None
await close_old_tabs()
await tab.evaluate_js("SteamClient.Browser.RestartJSContext();", False, True, False)
await tab.evaluate_js("location.reload();", False, True, False)
self.logger.info("React DevTools disabled")
async def get_user_info(self) -> Dict[str, str]:
+335
View File
@@ -0,0 +1,335 @@
#!/bin/bash
## Before using this script, enable sshd on the deck and setup an sshd key between the deck and your dev in sshd_config.
## 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.
## TODO: document latest changes to wiki
## Pre-parse arugments for ease of use
CLONEFOLDER=${1:-""}
INSTALLFOLDER=${2:-""}
DECKIP=${3:-""}
SSHPORT=${4:-""}
PASSWORD=${5:-""}
SSHKEYLOC=${6:-""}
LOADERBRANCH=${7:-""}
LIBRARYBRANCH=${8:-""}
TEMPLATEBRANCH=${9:-""}
LATEST=${10:-""}
## gather options into an array
OPTIONSARRAY=("$CLONEFOLDER" "$INSTALLFOLDER" "$DECKIP" "$SSHPORT" "$PASSWORD" "$SSHKEYLOC" "$LOADERBRANCH" "$LIBRARYBRANCH" "$TEMPLATEBRANCH" "$LATEST")
## iterate through options array to check their presence
count=0
for OPTION in ${OPTIONSARRAY[@]}; do
! [[ "$OPTION" == "" ]] && count=$(($count+1))
# printf "OPTION=$OPTION\n"
done
setfolder() {
if [[ "$2" == "clone" ]]; then
local ACTION="clone"
local DEFAULT="git"
elif [[ "$2" == "install" ]]; then
local ACTION="install"
local DEFAULT="dev"
fi
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
if ! [[ "$CLONEFOLDER" =~ ^[[:alnum:]]+$ ]]; then
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
CLONEFOLDER="${DEFAULT}"
fi
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
if ! [[ "$INSTALLFOLDER" =~ ^[[:alnum:]]+$ ]]; then
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
INSTALLFOLDER="${DEFAULT}"
fi
else
printf "Folder type could not be determined, exiting\n"
exit 1
fi
}
checkdeckip() {
### check that ip is provided
if [[ "$1" == "" ]]; then
printf "An ip address must be provided, exiting.\n"
exit 1
fi
### check to make sure it's a potentially valid ipv4 address
if ! [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
printf "A valid ip address must be provided, exiting.\n"
exit 1
fi
}
checksshport() {
### check to make sure a port was specified
if [[ "$1" == "" ]]; then
printf "ssh port not provided. Using default, '22'.\n"
SSHPORT="22"
fi
### check for valid ssh port
if [[ $1 -le 0 ]]; then
printf "A valid ssh port must be provided, exiting.\n"
exit 1
fi
}
checksshkey() {
### check if ssh key is present at location provided
if [[ "$1" == "" ]]; then
SSHKEYLOC="$HOME/.ssh/id_rsa"
printf "ssh key was not provided. Defaulting to $SSHKEYLOC if it exists.\n"
fi
### check if sshkey is present at location
if ! [[ -e "$1" ]]; then
SSHKEYLOC=""
printf "ssh key does not exist. This script will use password authentication.\n"
fi
}
checkpassword() {
### check to make sure a password for 'deck' was specified
if [[ "$1" == "" ]]; then
printf "Remote deck user password was not provided, exiting.\n"
exit 1
fi
}
clonefromto() {
# printf "repo=$1\n"
# printf "outdir=$2\n"
# printf "branch=$3\n"
printf "Repository: $1\n"
git clone $1 $2 &> '/dev/null'
CODE=$?
# printf "CODE=${CODE}"
if [[ $CODE -eq 128 ]]; then
cd $2
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
}
pnpmtransbundle() {
cd $1
if [[ "$2" == "library" ]]; then
npm install --quiet &> '/dev/null'
npm run build --quiet &> '/dev/null'
sudo npm link --quiet &> '/dev/null'
elif [[ "$2" == "frontend" ]]; then
pnpm i &> '/dev/null'
pnpm run build &> '/dev/null'
elif [[ "$2" == "template" ]]; then
pnpm i &> '/dev/null'
pnpm run build &> '/dev/null'
fi
}
if ! [[ $count -gt 9 ]] ; then
printf "Installing Steam Deck Plugin Loader contributor/developer (for Steam Deck)...\n"
printf "THIS SCRIPT ASSUMES YOU ARE RUNNING IT ON A PC, NOT THE DECK!
Not planning to contribute to or develop for PluginLoader?
If so, you should not be using this script.\n
If you have a release/nightly installed this script will disable it.\n"
printf "This script requires you to have nodejs installed. (If nodejs doesn't bundle npm on your OS/distro, then npm is required as well).\n"
fi
if ! [[ $count -gt 0 ]] ; then
read -p "Press any key to continue"
fi
printf "\n"
## User chooses preffered clone & install directories
if [[ "$CLONEFOLDER" == "" ]]; then
setfolder "$CLONEFOLDER" "clone"
fi
if [[ "$INSTALLFOLDER" == "" ]]; then
setfolder "$INSTALLFOLDER" "install"
fi
CLONEDIR="$HOME/$CLONEFOLDER"
INSTALLDIR="/home/deck/homebrew/$INSTALLFOLDER"
## Input ip address, port, password and sshkey
### DECKIP already been parsed?
if [[ "$DECKIP" == "" ]]; then
### get ip address of deck from user
read -p "Enter the ip address of your Steam Deck: " DECKIP
fi
### validate DECKIP
checkdeckip "$DECKIP"
### SSHPORT already been parsed?
if [[ "$SSHPORT" == "" ]]; then
### get ssh port from user
read -p "Enter the ssh port of your Steam Deck: " SSHPORT
fi
### validate SSHPORT
checksshport "$SSHPORT"
### PASSWORD already been parsed?
if [[ "$PASSWORD" == "" ]]; then
### prompt the user for their deck's password
printf "Enter the password for the Steam Deck user 'deck' : "
read -s PASSWORD
printf "\n"
fi
### validate PASSWORD
checkpassword "$PASSWORD"
### SSHKEYLOC already been parsed?
if [[ "$SSHKEYLOC" == "" ]]; then
### prompt the user for their ssh key
read -p "Enter the directory for your ssh key, for ease of connection : " SSHKEYLOC
fi
### validate SSHKEYLOC
checksshkey "$SSHKEYLOC"
if [[ "$SSHKEYLOC" == "" ]]; then
IDENINVOC=""
else
IDENINVOC="-i ${SSHKEYLOC}"
fi
## Create folder structure
printf "Cloning git repositories.\n"
mkdir -p ${CLONEDIR} &> '/dev/null'
### remove folders just in case
# rm -r ${CLONEDIR}/pluginloader
# rm -r ${CLONEDIR}/pluginlibrary
# rm -r ${CLONEDIR}/plugintemplate
clonefromto "https://github.com/SteamDeckHomebrew/PluginLoader" ${CLONEDIR}/pluginloader "$LOADERBRANCH"
clonefromto "https://github.com/SteamDeckHomebrew/decky-frontend-lib" ${CLONEDIR}/pluginlibrary "$LIBRARYBRANCH"
clonefromto "https://github.com/SteamDeckHomebrew/decky-plugin-template" ${CLONEDIR}/plugintemplate "$TEMPLATEBRANCH"
## install python dependencies to deck
printf "\nInstalling python dependencies.\n"
rsync -azp --rsh="ssh -p $SSHPORT $IDENINVOC" ${CLONEDIR}/pluginloader/requirements.txt deck@${DECKIP}:${INSTALLDIR}/pluginloader/requirements.txt &> '/dev/null'
ssh deck@${DECKIP} -p ${SSHPORT} ${IDENINVOC} "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install --upgrade setuptools && python -m pip install -r $INSTALLDIR/pluginloader/requirements.txt" &> '/dev/null'
## Transpile and bundle typescript
[ "$UID" -eq 0 ] || printf "Input password to proceed with install.\n"
sudo npm install -g pnpm &> '/dev/null'
type pnpm &> '/dev/null'
PNPMLIVES=$?
if ! [[ "$PNPMLIVES" -eq 0 ]]; then
printf "pnpm does not appear to be installed, exiting.\n"
exit 1
fi
printf "Transpiling and bundling typescript.\n"
pnpmtransbundle ${CLONEDIR}/pluginlibrary/ "library"
pnpmtransbundle ${CLONEDIR}/pluginloader/frontend "frontend"
pnpmtransbundle ${CLONEDIR}/plugintemplate "template"
## Transfer relevant files to deck
printf "Copying relevant files to install directory\n\n"
ssh deck@${DECKIP} -p ${SSHPORT} ${IDENINVOC} "mkdir -p $INSTALLDIR/pluginloader && mkdir -p $INSTALLDIR/plugins" &> '/dev/null'
### copy files for PluginLoader
rsync -avzp --rsh="ssh -p $SSHPORT $IDENINVOC" --exclude='.git/' --exclude='.github/' --exclude='.vscode/' --exclude='frontend/' --exclude='dist/' --exclude='contrib/' --exclude='*.log' --exclude='requirements.txt' --exclude='backend/__pycache__/' --exclude='.gitignore' --delete ${CLONEDIR}/pluginloader/* deck@${DECKIP}:${INSTALLDIR}/pluginloader &> '/dev/null'
if ! [[ $? -eq 0 ]]; then
printf "Error occurred when copying $CLONEDIR/pluginloader/ to $INSTALLDIR/pluginloader/\n"
printf "Check that your Steam Deck is active, ssh is enabled and running and is accepting connections.\n"
exit 1
fi
### copy files for plugin template
rsync -avzp --rsh="ssh -p $SSHPORT $IDENINVOC" --exclude='.git/' --exclude='.github/' --exclude='.vscode/' --exclude='node_modules/' --exclude='src/' --exclude='*.log' --exclude='.gitignore' --exclude='pnpm-lock.yaml' --exclude='package.json' --exclude='rollup.config.js' --exclude='tsconfig.json' --delete ${CLONEDIR}/plugintemplate deck@${DECKIP}:${INSTALLDIR}/plugins &> '/dev/null'
if ! [[ $? -eq 0 ]]; then
printf "Error occurred when copying $CLONEDIR/plugintemplate to $INSTALLDIR/plugins\n"
exit 1
fi
## TODO: direct contributors to wiki for this info?
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='.github/' --exclude='.vscode/' --exclude='frontend/' --exclude='dist/' --exclude='contrib/' --exclude='*.log' --exclude='requirements.txt' --exclude='backend/__pycache__/' --exclude='.gitignore' --delete $CLONEDIR/pluginloader/* deck@$DECKIP:$INSTALLDIR/pluginloader/'\n"
printf "'rsync -avzp --mkpath --rsh=""\"ssh -p $SSHPORT $IDENINVOC\""" --exclude='.git/' --exclude='.github/' --exclude='.vscode/' --exclude='node_modules/' --exclude='src/' --exclude='*.log' --exclude='.gitignore' --exclude='package-lock.json' --delete $CLONEDIR/pluginname deck@$DECKIP:$INSTALLDIR/plugins'\n\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
### ssh into deck and disable PluginLoader release/nightly service
printf "Connecting via ssh to disable any PluginLoader release versions.\n"
printf "Script will exit after this. All done!\n"
ssh deck@${DECKIP} -p ${SSHPORT} ${IDENINVOC} "printf $PASSWORD | sudo -S systemctl disable --now plugin_loader; echo $?" &> '/dev/null'
+168
View File
@@ -0,0 +1,168 @@
#!/bin/bash
## Pre-parse arugments for ease of use
CLONEFOLDER=${1:-""}
LOADERBRANCH=${2:-""}
LIBRARYBRANCH=${3:-""}
TEMPLATEBRANCH=${4:-""}
LATEST=${5:-""}
## gather options into an array
OPTIONSARRAY=("$CLONEFOLDER" "$LOADERBRANCH" "$LIBRARYBRANCH" "$TEMPLATEBRANCH" "$LATEST")
## iterate through options array to check their presence
count=0
for OPTION in ${OPTIONSARRAY[@]}; do
! [[ "$OPTION" == "" ]] && count=$(($count+1))
# printf "OPTION=$OPTION\n"
done
clonefromto() {
# printf "repo=$1\n"
# printf "outdir=$2\n"
# printf "branch=$3\n"
printf "Repository: $1\n"
git clone $1 $2 &> '/dev/null'
CODE=$?
# printf "CODE=${CODE}"
if [[ $CODE -eq 128 ]]; then
cd $2
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
}
pnpmtransbundle() {
cd $1
if [[ "$2" == "library" ]]; then
npm install --quiet &> '/dev/null'
npm run build --quiet &> '/dev/null'
sudo npm link --quiet &> '/dev/null'
elif [[ "$2" == "frontend" ]]; then
pnpm i &> '/dev/null'
pnpm run build &> '/dev/null'
elif [[ "$2" == "template" ]]; then
pnpm i &> '/dev/null'
pnpm run build &> '/dev/null'
fi
}
if ! [[ $count -gt 4 ]] ; then
printf "Installing Steam Deck Plugin Loader contributor/developer (no Steam Deck)..."
printf "\nTHIS SCRIPT ASSUMES YOU ARE RUNNING IT ON A PC, NOT THE DECK!
Not planning to contribute to or develop for PluginLoader?
Then you should not be using this script.\n"
printf "\nThis script requires you to have nodejs installed. (If nodejs doesn't bundle npm on your OS/distro, then npm is required as well).\n"
fi
if ! [[ $count -gt 0 ]] ; then
read -p "Press any key to continue"
fi
printf "\n"
if [[ "$CLONEFOLDER" == "" ]]; then
printf "Enter the directory in /home/user/ to clone to.\n"
printf "The clone directory would be: ${HOME}/git \n"
read -p "Enter your clone directory: " CLONEFOLDER
if ! [[ "$CLONEFOLDER" =~ ^[[:alnum:]]+$ ]]; then
printf "Folder name not provided. Using default, '${DEFAULT}'.\n"
CLONEFOLDER="${DEFAULT}"
fi
fi
CLONEDIR="$HOME/$CLONEFOLDER"
## Create folder structure
printf "Cloning git repositories.\n"
mkdir -p ${CLONEDIR} &> '/dev/null'
### remove folders just in case
# rm -r ${CLONEDIR}/pluginloader
# rm -r ${CLONEDIR}/pluginlibrary
# rm -r ${CLONEDIR}/plugintemplate
clonefromto "https://github.com/SteamDeckHomebrew/PluginLoader" ${CLONEDIR}/pluginloader "$LOADERBRANCH"
clonefromto "https://github.com/SteamDeckHomebrew/decky-frontend-lib" ${CLONEDIR}/pluginlibrary "$LIBRARYBRANCH"
clonefromto "https://github.com/SteamDeckHomebrew/decky-plugin-template" ${CLONEDIR}/plugintemplate "$TEMPLATEBRANCH"
## install python dependencies (maybe use venv?)
python -m pip install -r ${CLONEDIR}/pluginloader/requirements.txt &> '/dev/null'
## Transpile and bundle typescript
[ "$UID" -eq 0 ] || printf "Input password to proceed with install.\n"
type npm &> '/dev/null'
NPMLIVES=$?
if ! [[ "$PNPMLIVES" -eq 0 ]]; then
printf "npm does not appear to be installed, exiting.\n"
exit 1
fi
sudo npm install -g pnpm &> '/dev/null'
type pnpm &> '/dev/null'
PNPMLIVES=$?
if ! [[ "$PNPMLIVES" -eq 0 ]]; then
printf "pnpm does not appear to be installed, exiting.\n"
exit 1
fi
printf "Transpiling and bundling typescript.\n"
pnpmtransbundle ${CLONEDIR}/pluginlibrary/ "library"
pnpmtransbundle ${CLONEDIR}/pluginloader/frontend "frontend"
pnpmtransbundle ${CLONEDIR}/plugintemplate "template"
printf "Plugin Loader is located at '${CLONEDIR}/pluginloader/'.\n"
printf "Run in console or in a script these commands to run your development version:\n'export PLUGIN_PATH=${CLONEDIR}/plugins; export CHOWN_PLUGIN_PATH=0; sudo -E python3 ${CLONEDIR}/pluginloader/backend/main.py'\n"
printf "All done!\n"
+2 -2
View File
@@ -25,7 +25,7 @@
"i18next-parser": "^8.0.0",
"import-sort-style-module": "^6.0.0",
"inquirer": "^8.2.5",
"prettier": "^3.2.5",
"prettier": "^2.8.8",
"prettier-plugin-import-sort": "^0.0.7",
"react": "16.14.0",
"react-dom": "16.14.0",
@@ -44,7 +44,7 @@
}
},
"dependencies": {
"decky-frontend-lib": "3.25.0",
"decky-frontend-lib": "3.21.1",
"filesize": "^10.0.7",
"i18next": "^23.2.1",
"i18next-http-backend": "^2.2.1",
+15 -15
View File
@@ -6,8 +6,8 @@ settings:
dependencies:
decky-frontend-lib:
specifier: 3.25.0
version: 3.25.0
specifier: 3.21.1
version: 3.21.1
filesize:
specifier: ^10.0.7
version: 10.0.7
@@ -77,11 +77,11 @@ devDependencies:
specifier: ^8.2.5
version: 8.2.5
prettier:
specifier: ^3.2.5
version: 3.2.5
specifier: ^2.8.8
version: 2.8.8
prettier-plugin-import-sort:
specifier: ^0.0.7
version: 0.0.7(prettier@3.2.5)
version: 0.0.7(prettier@2.8.8)
react:
specifier: 16.14.0
version: 16.14.0
@@ -1482,8 +1482,8 @@ packages:
dependencies:
ms: 2.1.2
/decky-frontend-lib@3.25.0:
resolution: {integrity: sha512-2lBoHS2AIRmuluq/bGdHBz+uyToQE7k3K/vDq1MQbDZ4eC+8CGDuh2T8yZOj3D0yjGP2MdikNNAWPA9Z5l2qDg==}
/decky-frontend-lib@3.21.1:
resolution: {integrity: sha512-30605ET9qqZ6St6I9WmMmLGgSrTIdMwo7xy85+lRaF1miUd2icOGEJjwnbVcZDdkal+1fJ3tNEDXlchVfG4TrA==}
dev: false
/decode-named-character-reference@1.0.2:
@@ -1852,8 +1852,8 @@ packages:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
@@ -3108,7 +3108,7 @@ packages:
engines: {node: '>=8.6'}
dev: true
/prettier-plugin-import-sort@0.0.7(prettier@3.2.5):
/prettier-plugin-import-sort@0.0.7(prettier@2.8.8):
resolution: {integrity: sha512-O0KlUSq+lwvh+UiN3wZDT6wWkf7TNxTVv2/XXE5KqpRNbFJq3nRg2ftzBYFFO8QGpdWIrOB0uCTCtFjIxmVKQw==}
peerDependencies:
prettier: '>= 2.0'
@@ -3117,14 +3117,14 @@ packages:
import-sort-config: 6.0.0
import-sort-parser-babylon: 6.0.0
import-sort-parser-typescript: 6.0.0
prettier: 3.2.5
prettier: 2.8.8
transitivePeerDependencies:
- supports-color
dev: true
/prettier@3.2.5:
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
engines: {node: '>=14'}
/prettier@2.8.8:
resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'}
hasBin: true
dev: true
@@ -3477,7 +3477,7 @@ packages:
engines: {node: '>=10.0.0'}
hasBin: true
optionalDependencies:
fsevents: 2.3.3
fsevents: 2.3.2
dev: true
/rsvp@3.2.1:
-8
View File
@@ -8,7 +8,6 @@ import { VerInfo } from '../updater';
interface PublicDeckyState {
plugins: Plugin[];
pluginOrder: string[];
frozenPlugins: string[];
hiddenPlugins: string[];
activePlugin: Plugin | null;
updates: PluginUpdateMapping | null;
@@ -27,7 +26,6 @@ export interface UserInfo {
export class DeckyState {
private _plugins: Plugin[] = [];
private _pluginOrder: string[] = [];
private _frozenPlugins: string[] = [];
private _hiddenPlugins: string[] = [];
private _activePlugin: Plugin | null = null;
private _updates: PluginUpdateMapping | null = null;
@@ -43,7 +41,6 @@ export class DeckyState {
return {
plugins: this._plugins,
pluginOrder: this._pluginOrder,
frozenPlugins: this._frozenPlugins,
hiddenPlugins: this._hiddenPlugins,
activePlugin: this._activePlugin,
updates: this._updates,
@@ -70,11 +67,6 @@ export class DeckyState {
this.notifyUpdate();
}
setFrozenPlugins(frozenPlugins: string[]) {
this._frozenPlugins = frozenPlugins;
this.notifyUpdate();
}
setHiddenPlugins(hiddenPlugins: string[]) {
this._hiddenPlugins = hiddenPlugins;
this.notifyUpdate();
+5 -8
View File
@@ -30,14 +30,11 @@ const DeckyToaster: FC<DeckyToasterProps> = () => {
// not actually node but TS is shit
let interval: NodeJS.Timer | null;
if (renderedToast) {
interval = setTimeout(
() => {
interval = null;
console.log('clear toast', renderedToast.data);
removeToast(renderedToast.data);
},
(renderedToast.data.duration || 5e3) + 1000,
);
interval = setTimeout(() => {
interval = null;
console.log('clear toast', renderedToast.data);
removeToast(renderedToast.data);
}, (renderedToast.data.duration || 5e3) + 1000);
console.log('set int', interval);
}
return () => {
+14 -7
View File
@@ -1,4 +1,12 @@
import { ButtonItem, Focusable, PanelSection, PanelSectionRow } from 'decky-frontend-lib';
import {
ButtonItem,
Focusable,
PanelSection,
PanelSectionRow,
joinClassNames,
scrollClasses,
staticClasses,
} from 'decky-frontend-lib';
import { VFC, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash } from 'react-icons/fa';
@@ -28,7 +36,10 @@ const PluginView: VFC = () => {
return (
<Focusable onCancelButton={closeActivePlugin}>
<TitleView />
<div style={{ height: '100%', paddingTop: '16px' }}>
<div
className={joinClassNames(staticClasses.TabGroupPanel, scrollClasses.ScrollPanel, scrollClasses.ScrollY)}
style={{ height: '100%' }}
>
{(visible || activePlugin.alwaysRender) && activePlugin.content}
</div>
</Focusable>
@@ -37,11 +48,7 @@ const PluginView: VFC = () => {
return (
<>
<TitleView />
<div
style={{
paddingTop: '16px',
}}
>
<div className={joinClassNames(staticClasses.TabGroupPanel, scrollClasses.ScrollPanel, scrollClasses.ScrollY)}>
<PanelSection>
{pluginList
.filter((p) => p.content)
-2
View File
@@ -10,8 +10,6 @@ const titleStyles: CSSProperties = {
display: 'flex',
paddingTop: '3px',
paddingRight: '16px',
position: 'sticky',
top: '0px',
};
const TitleView: VFC = () => {
@@ -15,9 +15,8 @@ const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, butt
closeModal={closeModal}
onOK={async () => {
await window.DeckyPluginLoader.callServerMethod('uninstall_plugin', { name });
// uninstalling a plugin resets the frozen and hidden setting for it server-side
// uninstalling a plugin resets the hidden setting for it server-side
// we invalidate here so if you re-install it, you won't have an out-of-date hidden filter
await window.DeckyPluginLoader.frozenPluginsService.invalidate();
await window.DeckyPluginLoader.hiddenPluginsService.invalidate();
}}
strTitle={title}
+2 -14
View File
@@ -1,7 +1,7 @@
import { SidebarNavigation } from 'decky-frontend-lib';
import { lazy } from 'react';
import { useTranslation } from 'react-i18next';
import { FaCode, FaFlask, FaPlug } from 'react-icons/fa';
import { FaCode, FaPlug } from 'react-icons/fa';
import { useSetting } from '../../utils/hooks/useSetting';
import DeckyIcon from '../DeckyIcon';
@@ -10,7 +10,6 @@ import GeneralSettings from './pages/general';
import PluginList from './pages/plugin_list';
const DeveloperSettings = lazy(() => import('./pages/developer'));
const TestingMenu = lazy(() => import('./pages/testing'));
export default function SettingsPage() {
const [isDeveloper, setIsDeveloper] = useSetting<boolean>('developer.enabled', false);
@@ -25,7 +24,7 @@ export default function SettingsPage() {
},
{
title: t('SettingsIndex.plugins_title'),
content: <PluginList isDeveloper={isDeveloper} />,
content: <PluginList />,
route: '/decky/settings/plugins',
icon: <FaPlug />,
},
@@ -40,17 +39,6 @@ export default function SettingsPage() {
icon: <FaCode />,
visible: isDeveloper,
},
{
title: t('SettingsIndex.testing_title'),
content: (
<WithSuspense>
<TestingMenu />
</WithSuspense>
),
route: '/decky/settings/testing',
icon: <FaFlask />,
visible: isDeveloper,
},
];
return <SidebarNavigation pages={pages} />;
@@ -8,15 +8,10 @@ import { useSetting } from '../../../../utils/hooks/useSetting';
const logger = new Logger('BranchSelect');
export enum UpdateBranch {
Stable,
Prerelease,
Testing,
}
enum LessUpdateBranch {
enum UpdateBranch {
Stable,
Prerelease,
// Testing,
}
const BranchSelect: FunctionComponent<{}> = () => {
@@ -29,11 +24,11 @@ const BranchSelect: FunctionComponent<{}> = () => {
const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Stable);
return (
// Returns numerical values from 0 to 2 (with current branch setup as of 6/16/23)
// 0 being stable, 1 being pre-release and 2 being testing (not a branch!)
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
// 0 being stable, 1 being pre-release and 2 being nightly
<Field label={t('BranchSelect.update_channel.label')} childrenContainerWidth={'fixed'}>
<Dropdown
rgOptions={Object.values(selectedBranch == UpdateBranch.Testing ? UpdateBranch : LessUpdateBranch)
rgOptions={Object.values(UpdateBranch)
.filter((branch) => typeof branch == 'number')
.map((branch) => ({
label: tBranches[branch as number],
@@ -135,8 +135,8 @@ export default function UpdaterSettings() {
{checkingForUpdates
? t('Updater.updates.checking')
: !versionInfo?.remote || versionInfo?.remote?.tag_name == versionInfo?.current
? t('Updater.updates.check_button')
: t('Updater.updates.install_button')}
? t('Updater.updates.check_button')
: t('Updater.updates.install_button')}
</DialogButton>
) : (
<ProgressBarWithInfo
@@ -1,34 +1,18 @@
import { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash, FaLock } from 'react-icons/fa';
import { FaEyeSlash } from 'react-icons/fa';
interface PluginListLabelProps {
frozen: boolean;
hidden: boolean;
name: string;
version?: string;
}
const PluginListLabel: FC<PluginListLabelProps> = ({ name, frozen, hidden, version }) => {
const PluginListLabel: FC<PluginListLabelProps> = ({ name, hidden, version }) => {
const { t } = useTranslation();
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<div>
{name}
{version && (
<>
{' - '}
<span style={{ color: frozen ? '#67707b' : 'inherit' }}>
{frozen && (
<>
<FaLock />{' '}
</>
)}
{version}
</span>
</>
)}
</div>
<div>{version ? `${name} - ${version}` : name}</div>
{hidden && (
<div
style={{
@@ -33,16 +33,7 @@ async function reinstallPlugin(pluginName: string, currentVersion?: string) {
}
}
type PluginTableData = PluginData & {
name: string;
frozen: boolean;
onFreeze(): void;
onUnfreeze(): void;
hidden: boolean;
onHide(): void;
onShow(): void;
isDeveloper: boolean;
};
type PluginTableData = PluginData & { name: string; hidden: boolean; onHide(): void; onShow(): void };
function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }) {
const { t } = useTranslation();
@@ -52,7 +43,7 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }
return null;
}
const { name, update, version, onHide, onShow, hidden, onFreeze, onUnfreeze, frozen, isDeveloper } = props.entry.data;
const { name, update, version, onHide, onShow, hidden } = props.entry.data;
const showCtxMenu = (e: MouseEvent | GamepadEvent) => {
showContextMenu(
@@ -93,11 +84,6 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }
) : (
<MenuItem onSelected={onHide}>{t('PluginListIndex.hide')}</MenuItem>
)}
{frozen ? (
<MenuItem onSelected={onUnfreeze}>{t('PluginListIndex.unfreeze')}</MenuItem>
) : (
isDeveloper && <MenuItem onSelected={onFreeze}>{t('PluginListIndex.freeze')}</MenuItem>
)}
</Menu>,
e.currentTarget ?? window,
);
@@ -152,8 +138,8 @@ type PluginData = {
version?: string;
};
export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
const { plugins, updates, pluginOrder, setPluginOrder, frozenPlugins, hiddenPlugins } = useDeckyState();
export default function PluginList() {
const { plugins, updates, pluginOrder, setPluginOrder, hiddenPlugins } = useDeckyState();
const [_, setPluginOrderSetting] = useSetting<string[]>(
'pluginOrder',
plugins.map((plugin) => plugin.name),
@@ -165,27 +151,21 @@ export default function PluginList({ isDeveloper }: { isDeveloper: boolean }) {
}, []);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginTableData>[]>([]);
const frozenPluginsService = window.DeckyPluginLoader.frozenPluginsService;
const hiddenPluginsService = window.DeckyPluginLoader.hiddenPluginsService;
useEffect(() => {
setPluginEntries(
plugins.map(({ name, version }) => {
const frozen = frozenPlugins.includes(name);
const hidden = hiddenPlugins.includes(name);
return {
label: <PluginListLabel name={name} frozen={frozen} hidden={hidden} version={version} />,
label: <PluginListLabel name={name} hidden={hidden} version={version} />,
position: pluginOrder.indexOf(name),
data: {
name,
frozen,
hidden,
isDeveloper,
version,
update: updates?.get(name),
onFreeze: () => frozenPluginsService.update([...frozenPlugins, name]),
onUnfreeze: () => frozenPluginsService.update(frozenPlugins.filter((pluginName) => name !== pluginName)),
onHide: () => hiddenPluginsService.update([...hiddenPlugins, name]),
onShow: () => hiddenPluginsService.update(hiddenPlugins.filter((pluginName) => name !== pluginName)),
},
@@ -1,87 +0,0 @@
import { DialogBody, DialogButton, DialogControlsSection, Focusable, Navigation } from 'decky-frontend-lib';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaDownload, FaInfo } from 'react-icons/fa';
import { callUpdaterMethod } from '../../../../updater';
import { setSetting } from '../../../../utils/settings';
import { UpdateBranch } from '../general/BranchSelect';
interface TestingVersion {
id: number;
name: string;
link: string;
head_sha: string;
}
export default function TestingVersionList() {
const { t } = useTranslation();
const [testingVersions, setTestingVersions] = useState<TestingVersion[]>([]);
useEffect(() => {
(async () => {
setTestingVersions((await callUpdaterMethod('get_testing_versions')).result);
})();
}, []);
if (testingVersions.length === 0) {
return (
<div>
<p>No open PRs found</p>
</div>
);
}
return (
<DialogBody>
<DialogControlsSection>
<ul style={{ listStyleType: 'none', padding: '0' }}>
{testingVersions.map((version) => {
return (
<li style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', paddingBottom: '10px' }}>
<span>
{version.name} <span style={{ opacity: '50%' }}>{'#' + version.id}</span>
</span>
<Focusable style={{ height: '40px', marginLeft: 'auto', display: 'flex' }}>
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => {
callUpdaterMethod('download_testing_version', { pr_id: version.id, sha_id: version.head_sha });
setSetting('branch', UpdateBranch.Testing);
}}
>
<div
style={{
display: 'flex',
minWidth: '150px',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
{t('Testing.download')}
<FaDownload style={{ paddingLeft: '1rem' }} />
</div>
</DialogButton>
<DialogButton
style={{
height: '40px',
width: '40px',
padding: '10px 12px',
minWidth: '40px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
}}
onClick={() => Navigation.NavigateToExternalWeb(version.link)}
>
<FaInfo />
</DialogButton>
</Focusable>
</li>
);
})}
</ul>
</DialogControlsSection>
</DialogBody>
);
}
+65 -72
View File
@@ -8,19 +8,20 @@ import {
TextField,
findModule,
} from 'decky-frontend-lib';
import { Dispatch, FC, SetStateAction, useEffect, useMemo, useState } from 'react';
import { FC, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import logo from '../../../assets/plugin_store.png';
import Logger from '../../logger';
import { SortDirections, SortOptions, Store, StorePlugin, getPluginList, getStore } from '../../store';
import { Store, StorePlugin, getPluginList, getStore } from '../../store';
import PluginCard from './PluginCard';
const logger = new Logger('Store');
const StorePage: FC<{}> = () => {
const [currentTabRoute, setCurrentTabRoute] = useState<string>('browse');
const [pluginCount, setPluginCount] = useState<number | null>(null);
const [data, setData] = useState<StorePlugin[] | null>(null);
const [isTesting, setIsTesting] = useState<boolean>(false);
const { TabCount } = findModule((m) => {
if (m?.TabCount && m?.TabTitle) return true;
return false;
@@ -28,6 +29,17 @@ const StorePage: FC<{}> = () => {
const { t } = useTranslation();
useEffect(() => {
(async () => {
const res = await getPluginList();
logger.log('got data!', res);
setData(res);
const storeRes = await getStore();
logger.log(`store is ${storeRes}, isTesting is ${storeRes === Store.Testing}`);
setIsTesting(storeRes === Store.Testing);
})();
}, []);
return (
<>
<div
@@ -37,71 +49,52 @@ const StorePage: FC<{}> = () => {
background: '#0005',
}}
>
<Tabs
activeTab={currentTabRoute}
onShowTab={(tabId: string) => {
setCurrentTabRoute(tabId);
}}
tabs={[
{
title: t('Store.store_tabs.title'),
content: <BrowseTab setPluginCount={setPluginCount} />,
id: 'browse',
renderTabAddon: () => <span className={TabCount}>{pluginCount}</span>,
},
{
title: t('Store.store_tabs.about'),
content: <AboutTab />,
id: 'about',
},
]}
/>
{!data ? (
<div style={{ height: '100%' }}>
<SteamSpinner />
</div>
) : (
<Tabs
activeTab={currentTabRoute}
onShowTab={(tabId: string) => {
setCurrentTabRoute(tabId);
}}
tabs={[
{
title: t('Store.store_tabs.title'),
content: <BrowseTab children={{ data: data, isTesting: isTesting }} />,
id: 'browse',
renderTabAddon: () => <span className={TabCount}>{data.length}</span>,
},
{
title: t('Store.store_tabs.about'),
content: <AboutTab />,
id: 'about',
},
]}
/>
)}
</div>
</>
);
};
const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }> = ({ setPluginCount }) => {
const BrowseTab: FC<{ children: { data: StorePlugin[]; isTesting: boolean } }> = (data) => {
const { t } = useTranslation();
const dropdownSortOptions = useMemo(
const sortOptions = useMemo(
(): DropdownOption[] => [
// ascending and descending order are the wrong way around for the alphabetical sort
// this is because it was initially done incorrectly for i18n and 'fixing' it would
// make all the translations incorrect
{ data: [SortOptions.name, SortDirections.ascending], label: t('Store.store_tabs.alph_desc') },
{ data: [SortOptions.name, SortDirections.descending], label: t('Store.store_tabs.alph_asce') },
{ data: [SortOptions.date, SortDirections.ascending], label: t('Store.store_tabs.date_asce') },
{ data: [SortOptions.date, SortDirections.descending], label: t('Store.store_tabs.date_desc') },
{ data: [SortOptions.downloads, SortDirections.descending], label: t('Store.store_tabs.downloads_desc') },
{ data: [SortOptions.downloads, SortDirections.ascending], label: t('Store.store_tabs.downloads_asce') },
{ data: 1, label: t('Store.store_tabs.alph_desc') },
{ data: 2, label: t('Store.store_tabs.alph_asce') },
],
[],
);
// const filterOptions = useMemo((): DropdownOption[] => [{ data: 1, label: 'All' }], []);
const [selectedSort, setSort] = useState<[SortOptions, SortDirections]>(dropdownSortOptions[0].data);
const [selectedSort, setSort] = useState<number>(sortOptions[0].data);
// const [selectedFilter, setFilter] = useState<number>(filterOptions[0].data);
const [searchFieldValue, setSearchValue] = useState<string>('');
const [pluginList, setPluginList] = useState<StorePlugin[] | null>(null);
const [isTesting, setIsTesting] = useState<boolean>(false);
useEffect(() => {
(async () => {
const res = await getPluginList(selectedSort[0], selectedSort[1]);
logger.log('got data!', res);
setPluginList(res);
setPluginCount(res.length);
})();
}, [selectedSort]);
useEffect(() => {
(async () => {
const storeRes = await getStore();
logger.log(`store is ${storeRes}, isTesting is ${storeRes === Store.Testing}`);
setIsTesting(storeRes === Store.Testing);
})();
}, []);
return (
<>
@@ -124,7 +117,7 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
<span className="DialogLabel">{t("Store.store_sort.label")}</span>
<Dropdown
menuLabel={t("Store.store_sort.label") as string}
rgOptions={dropdownSortOptions}
rgOptions={sortOptions}
strDefaultLabel={t("Store.store_sort.label_def") as string}
selectedOption={selectedSort}
onChange={(e) => setSort(e.data)}
@@ -170,7 +163,7 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
<span className="DialogLabel">{t('Store.store_sort.label')}</span>
<Dropdown
menuLabel={t('Store.store_sort.label') as string}
rgOptions={dropdownSortOptions}
rgOptions={sortOptions}
strDefaultLabel={t('Store.store_sort.label_def') as string}
selectedOption={selectedSort}
onChange={(e) => setSort(e.data)}
@@ -189,7 +182,7 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
</div>
</Focusable>
</div>
{isTesting && (
{data.children.isTesting && (
<div
style={{
alignItems: 'center',
@@ -220,22 +213,22 @@ const BrowseTab: FC<{ setPluginCount: Dispatch<SetStateAction<number | null>> }>
</div>
)}
<div>
{!pluginList ? (
<div style={{ height: '100%' }}>
<SteamSpinner />
</div>
) : (
pluginList
.filter((plugin: StorePlugin) => {
return (
plugin.name.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
plugin.description.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
plugin.author.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
plugin.tags.some((tag: string) => tag.toLowerCase().includes(searchFieldValue.toLowerCase()))
);
})
.map((plugin: StorePlugin) => <PluginCard plugin={plugin} />)
)}
{data.children.data
.filter((plugin: StorePlugin) => {
return (
plugin.name.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
plugin.description.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
plugin.author.toLowerCase().includes(searchFieldValue.toLowerCase()) ||
plugin.tags.some((tag: string) => tag.toLowerCase().includes(searchFieldValue.toLowerCase()))
);
})
.sort((a, b) => {
if (selectedSort % 2 === 1) return a.name.localeCompare(b.name);
else return b.name.localeCompare(a.name);
})
.map((plugin: StorePlugin) => (
<PluginCard plugin={plugin} />
))}
</div>
</>
);
+20 -18
View File
@@ -1,4 +1,4 @@
import { sleep } from 'decky-frontend-lib';
import { findModuleChild, sleep } from 'decky-frontend-lib';
import { FaReact } from 'react-icons/fa';
import Logger from './logger';
@@ -9,30 +9,32 @@ const logger = new Logger('DeveloperMode');
let removeSettingsObserver: () => void = () => {};
declare global {
interface Window {
settingsStore: any;
}
}
export async function setShowValveInternal(show: boolean) {
if (show) {
const mobx =
window.settingsStore[
Object.getOwnPropertySymbols(window.settingsStore).find(
(x) => x.toString() == 'Symbol(mobx administration)',
) as any
];
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);
}
}
removeSettingsObserver = (mobx.observe_ || mobx.observe).call(mobx, (e: any) => {
if (show) {
removeSettingsObserver = settingsMod[
Object.getOwnPropertySymbols(settingsMod).find((x) => x.toString() == 'Symbol(mobx administration)') as any
].observe((e: any) => {
e.newValue.bIsValveEmail = true;
});
window.settingsStore.m_Settings.bIsValveEmail = true;
settingsMod.m_Settings.bIsValveEmail = true;
logger.log('Enabled Valve Internal menu');
} else {
removeSettingsObserver();
window.settingsStore.m_Settings.bIsValveEmail = false;
settingsMod.m_Settings.bIsValveEmail = false;
logger.log('Disabled Valve Internal menu');
}
}
-49
View File
@@ -1,49 +0,0 @@
import { DeckyState } from './components/DeckyState';
import { PluginUpdateMapping } from './store';
import { getSetting, setSetting } from './utils/settings';
/**
* A Service class for managing the state and actions related to the frozen plugins feature.
*
* It's mostly responsible for sending setting updates to the server and keeping the local state in sync.
*/
export class FrozenPluginService {
constructor(private deckyState: DeckyState) {}
init() {
getSetting<string[]>('frozenPlugins', []).then((frozenPlugins) => {
this.deckyState.setFrozenPlugins(frozenPlugins);
});
}
/**
* Sends the new frozen plugins list to the server and persists it locally in the decky state
*
* @param frozenPlugins The new list of frozen plugins
*/
async update(frozenPlugins: string[]) {
await setSetting('frozenPlugins', frozenPlugins);
this.deckyState.setFrozenPlugins(frozenPlugins);
// Remove pending updates for frozen plugins
const updates = this.deckyState.publicState().updates;
if (updates) {
const filteredUpdates = new Map() as PluginUpdateMapping;
updates.forEach((v, k) => {
if (!frozenPlugins.includes(k)) {
filteredUpdates.set(k, v);
}
});
this.deckyState.setUpdates(filteredUpdates);
}
}
/**
* Refreshes the state of frozen plugins in the local state
*/
async invalidate() {
this.deckyState.setFrozenPlugins(await getSetting('frozenPlugins', []));
}
}
-11
View File
@@ -1,16 +1,5 @@
// Sets up DFL, then loads start.ts which starts up the loader
interface Window {
SP_REACTDOM: any;
}
(async () => {
if (!window.SP_REACT) {
console.debug('Setting up React globals...');
// deliberate partial import
const DFLWebpack = await import('decky-frontend-lib/dist/webpack');
// TODO move these finds to dfl in v4
window.SP_REACT = DFLWebpack.findModule((m) => m.Component && m.PureComponent && m.useLayoutEffect);
window.SP_REACTDOM = DFLWebpack.findModule((m) => m.createPortal && m.createRoot);
}
console.debug('Setting up decky-frontend-lib...');
window.DFL = await import('decky-frontend-lib');
await import('./start');
+1 -6
View File
@@ -22,7 +22,6 @@ import PluginUninstallModal from './components/modals/PluginUninstallModal';
import NotificationBadge from './components/NotificationBadge';
import PluginView from './components/PluginView';
import WithSuspense from './components/WithSuspense';
import { FrozenPluginService } from './frozen-plugins-service';
import { HiddenPluginsService } from './hidden-plugins-service';
import Logger from './logger';
import { NotificationService } from './notification-service';
@@ -50,7 +49,6 @@ class PluginLoader extends Logger {
public toaster: Toaster = new Toaster();
private deckyState: DeckyState = new DeckyState();
public frozenPluginsService = new FrozenPluginService(this.deckyState);
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
public notificationService = new NotificationService(this.deckyState);
@@ -146,9 +144,7 @@ class PluginLoader extends Logger {
}
public async checkPluginUpdates() {
const frozenPlugins = this.deckyState.publicState().frozenPlugins;
const updates = await checkForUpdates(this.plugins.filter((p) => !frozenPlugins.includes(p.name)));
const updates = await checkForUpdates(this.plugins);
this.deckyState.setUpdates(updates);
return updates;
}
@@ -228,7 +224,6 @@ class PluginLoader extends Logger {
this.deckyState.setPluginOrder(pluginOrder);
});
this.frozenPluginsService.init();
this.hiddenPluginsService.init();
this.notificationService.init();
}
+2 -2
View File
@@ -1,4 +1,4 @@
// import reloadFix from './reload';
import reloadFix from './reload';
import restartFix from './restart';
let fixes: Function[] = [];
@@ -7,6 +7,6 @@ export function deinitSteamFixes() {
}
export async function initSteamFixes() {
// fixes.push(await reloadFix());
fixes.push(await reloadFix());
fixes.push(await restartFix());
}
+2 -22
View File
@@ -7,17 +7,6 @@ export enum Store {
Custom,
}
export enum SortOptions {
name = 'name',
date = 'date',
downloads = 'downloads',
}
export enum SortDirections {
ascending = 'asc',
descending = 'desc',
}
export interface StorePluginVersion {
name: string;
hash: string;
@@ -47,20 +36,11 @@ export async function getStore(): Promise<Store> {
return await getSetting<Store>('store', Store.Default);
}
export async function getPluginList(
sort_by: SortOptions | null = null,
sort_direction: SortDirections | null = null,
): Promise<StorePlugin[]> {
export async function getPluginList(): Promise<StorePlugin[]> {
let version = await window.DeckyPluginLoader.updateVersion();
let store = await getSetting<Store | null>('store', null);
let customURL = await getSetting<string>('store-url', 'https://plugins.deckbrew.xyz/plugins');
let query: URLSearchParams | string = new URLSearchParams();
sort_by && query.set('sort_by', sort_by);
sort_direction && query.set('sort_direction', sort_direction);
query = '?' + String(query);
let storeURL;
if (store === null) {
console.log('Could not get store, using Default.');
@@ -82,7 +62,7 @@ export async function getPluginList(
storeURL = 'https://plugins.deckbrew.xyz/plugins';
break;
}
return fetch(storeURL + query, {
return fetch(storeURL, {
method: 'GET',
headers: {
'X-Decky-Version': version.current,
+2 -2
View File
@@ -1,5 +1,5 @@
// TabsHook for versions before the Desktop merge
import { Patch, afterPatch, getReactRoot, sleep } from 'decky-frontend-lib';
import { Patch, afterPatch, sleep } from 'decky-frontend-lib';
import { memo } from 'react';
import NewTabsHook from './tabs-hook';
@@ -35,7 +35,7 @@ class TabsHook extends NewTabsHook {
init() {
const self = this;
const tree = getReactRoot(document.getElementById('root') as any);
const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current;
let scrollRoot: any;
async function findScrollRoot(currentNode: any, iters: number): Promise<any> {
if (iters >= 30) {
+5 -6
View File
@@ -1,5 +1,5 @@
// TabsHook for versions after the Desktop merge
import { Patch, QuickAccessTab, afterPatch, findInReactTree, getReactRoot, sleep } from 'decky-frontend-lib';
import { Patch, QuickAccessTab, afterPatch, findInReactTree, sleep } from 'decky-frontend-lib';
import { QuickAccessVisibleStateProvider } from './components/QuickAccessVisibleState';
import Logger from './logger';
@@ -32,16 +32,15 @@ class TabsHook extends Logger {
}
init() {
const tree = getReactRoot(document.getElementById('root') as any);
const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current;
let qAMRoot: any;
const findQAMRoot = (currentNode: any, iters: number): any => {
if (iters >= 80) {
// currently 67
if (iters >= 65) {
// currently 45
return null;
}
if (
(typeof currentNode?.memoizedProps?.visible == 'boolean' ||
typeof currentNode?.memoizedProps?.active == 'boolean') &&
typeof currentNode?.memoizedProps?.visible == 'boolean' &&
currentNode?.type?.toString()?.includes('QuickAccessMenuBrowserView')
) {
this.log(`QAM root was found in ${iters} recursion cycles`);
+8 -26
View File
@@ -1,14 +1,4 @@
import {
Module,
Patch,
ToastData,
afterPatch,
findClass,
findInReactTree,
findModuleChild,
getReactRoot,
sleep,
} from 'decky-frontend-lib';
import { Module, Patch, ToastData, afterPatch, findInReactTree, findModuleChild, sleep } from 'decky-frontend-lib';
import { ReactNode } from 'react';
import Toast from './components/Toast';
@@ -48,21 +38,16 @@ class Toaster extends Logger {
// </DeckyToasterStateContextProvider>
// ));
let instance: any;
const tree = getReactRoot(document.getElementById('root') as any);
const toasterClass1 = findClass('GamepadToastPlaceholder');
const toasterClass2 = findClass('ToastPlaceholder');
const toasterClass3 = findClass('ToastPopup');
const toasterClass4 = findClass('GamepadToastPopup');
const tree = (document.getElementById('root') as any)._reactRootContainer._internalRoot.current;
const findToasterRoot = (currentNode: any, iters: number): any => {
if (iters >= 80) {
// currently 66
if (iters >= 65) {
// currently 65
return null;
}
if (
currentNode?.memoizedProps?.className?.startsWith?.(toasterClass1) ||
currentNode?.memoizedProps?.className?.startsWith?.(toasterClass2) ||
currentNode?.memoizedProps?.className?.startsWith?.(toasterClass3) ||
currentNode?.memoizedProps?.className?.startsWith?.(toasterClass4)
currentNode?.memoizedProps?.className?.startsWith?.('gamepadtoasts_GamepadToastPlaceholder') ||
currentNode?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPlaceholder') ||
currentNode?.memoizedProps?.className?.startsWith?.('toastmanager_ToastPopup')
) {
this.log(`Toaster root was found in ${iters} recursion cycles`);
return currentNode;
@@ -86,10 +71,7 @@ class Toaster extends Logger {
instance = findToasterRoot(tree, 0);
}
this.node = instance.return;
this.rNode = findInReactTree(
this.node.return.return,
(node) => node?.stateNode && node.type?.InstallErrorReportingStore,
);
this.rNode = this.node.return;
let toast: any;
let renderedToast: ReactNode = null;
let innerPatched: any;