Compare commits

..

16 Commits

Author SHA1 Message Date
Party Wumpus e8dfe5a87d When decky is uncertain of branch, set the setting to match the guess (#480)
* If branch setting is missing, set it using the 'guess' from backend

* Make the frontend default to stable branch like the backend
2023-06-15 05:53:02 -07:00
WerWolvTranslationBot d0b7d1a4a6 Translations update from Weblate (#477)
* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hant/

* Translated using Weblate (German)

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/de/

* Translated using Weblate (Italian)

Currently translated at 91.8% (102 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (111 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/es/

* Translated using Weblate (Italian)

Currently translated at 100.0% (118 of 118 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

---------

Co-authored-by: david082321 <david082321@yahoo.com.tw>
Co-authored-by: Jonas Dellinger <jonas@dellinger.dev>
Co-authored-by: Marco Rodolfi <marco.rodolfi@tuta.io>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Sean <zhangshuyan@fuji.waseda.jp>
Co-authored-by: Avery <aveeryy@protonmail.com>
2023-06-08 16:49:07 +02:00
AAGaming 9a05c228a0 fix dumb crash when updating decky while a plugin that uses BrowserView is running 2023-06-07 18:03:41 -04:00
Party Wumpus 00d9b03322 add firefox .download clarification in readme 2023-06-07 16:56:47 +01:00
Jonas Dellinger 47bc910a84 Add functionality to hide plugins from quick access menu (#468) 2023-06-06 22:35:05 -07:00
WerWolvTranslationBot 1c6270ccd6 Translations update from Weblate (#476)
* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hant/

* Translated using Weblate (German)

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/de/

* Translated using Weblate (Italian)

Currently translated at 91.8% (102 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (111 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/es/

---------

Co-authored-by: david082321 <david082321@yahoo.com.tw>
Co-authored-by: Jonas Dellinger <jonas@dellinger.dev>
Co-authored-by: Marco Rodolfi <marco.rodolfi@tuta.io>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Sean <zhangshuyan@fuji.waseda.jp>
Co-authored-by: Avery <aveeryy@protonmail.com>
2023-06-04 17:09:14 +02:00
Avery 8f17f2b0fe Translated using Weblate (Spanish)
Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/es/
2023-06-04 17:08:01 +02:00
Sean 81b601c0e7 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hans/
2023-06-04 17:08:01 +02:00
Marco Rodolfi 83e8c89c97 Translated using Weblate (Italian)
Currently translated at 100.0% (114 of 114 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/
2023-06-04 17:08:01 +02:00
WerWolvTranslationBot ca107feb25 Translations update from Weblate (#475)
* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hant/

* Translated using Weblate (German)

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/de/

* Translated using Weblate (Italian)

Currently translated at 91.8% (102 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (111 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

---------

Co-authored-by: david082321 <david082321@yahoo.com.tw>
Co-authored-by: Jonas Dellinger <jonas@dellinger.dev>
Co-authored-by: Marco Rodolfi <marco.rodolfi@tuta.io>
Co-authored-by: Weblate <noreply@weblate.org>
2023-06-04 11:06:53 +02:00
AAGaming e5277190ed Revert "Refactor TabsHook (#458)"
This reverts commit b27b625921.

These changes broke Decky's QAM injection when the lock screen is enabled and need to be revised
2023-06-03 12:45:09 -04:00
suchmememanyskill 2e8e0fc7c1 Plugin backend reload (#463)
Co-authored-by: beebls <102569435+beebls@users.noreply.github.com>
2023-06-01 18:44:55 -07:00
Party Wumpus 8049417e03 Attempt to appease the linter
I think the first navigation being on one line looks nicer, but it's over the 120 character limit :(
2023-06-02 00:26:08 +01:00
Witherking25 f4c0a8b5aa add cef console button to developer settings (#441)
* add cef console button

* Small fix: handle missing localization in backend plus a small typo in the english language (#443)

* Hotfix for i18n where the detector was overriding localStorage

* Please, pnpm, cooperate

* Small fix regarding the backend getting hammered when switching to not supported languages plus a small english typo

* Add a get_tab_id function to utilities

* Go straight to SharedJSContext into console button

* clean up some log statements, and some extra parentheses

---------

Co-authored-by: Marco Rodolfi <marco.rodolfi@tuta.io>
Co-authored-by: Party Wumpus <48649272+PartyWumpus@users.noreply.github.com>
2023-06-02 00:01:21 +01:00
WerWolvTranslationBot d3584a9931 Translations update from Weblate (#469)
* Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/zh_Hant/

* Translated using Weblate (German)

Currently translated at 100.0% (100 of 100 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/de/

* Translated using Weblate (Italian)

Currently translated at 91.8% (102 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (111 of 111 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/it/

---------

Co-authored-by: david082321 <david082321@yahoo.com.tw>
Co-authored-by: Jonas Dellinger <jonas@dellinger.dev>
Co-authored-by: Marco Rodolfi <marco.rodolfi@tuta.io>
2023-05-31 13:36:02 +02:00
MIkhail Kozlov b27b625921 Refactor TabsHook (#458) 2023-05-30 23:53:48 -07:00
26 changed files with 588 additions and 109 deletions
+1 -1
View File
@@ -48,7 +48,7 @@ For more information about Decky Loader as well as documentation and development
1. Press the <img src="./docs/images/light/steam.svg#gh-dark-mode-only" height=16><img src="./docs/images/dark/steam.svg#gh-light-mode-only" height=16> button and open the Power menu.
1. Select "Switch to Desktop".
1. Navigate to this Github page on a browser of your choice.
1. Download the [installer file](https://github.com/SteamDeckHomebrew/decky-installer/releases/latest/download/decky_installer.desktop).
1. Download the [installer file](https://github.com/SteamDeckHomebrew/decky-installer/releases/latest/download/decky_installer.desktop). (If using firefox, it will be named `decky_installer.desktop.download`. Rename it to `decky_installer.desktop` before running it)
1. Drag the file onto your desktop and double click it to run it.
1. Either type your admin password or allow Decky to temporarily set your admin password to `Decky!` (this password will be removed after the installer finishes)
1. Choose the version of Decky Loader you want to install.
+16 -4
View File
@@ -122,10 +122,7 @@ class PluginBrowser:
logger.debug("Plugin %s was stopped", name)
del self.plugins[name]
logger.debug("Plugin %s was removed from the dictionary", name)
current_plugin_order = self.settings.getSetting("pluginOrder")
current_plugin_order.remove(name)
self.settings.setSetting("pluginOrder", current_plugin_order)
logger.debug("Plugin %s was removed from the pluginOrder setting", name)
self.cleanup_plugin_settings(name)
logger.debug("removing files %s" % str(name))
rmtree(plugin_dir)
except FileNotFoundError:
@@ -234,3 +231,18 @@ class PluginBrowser:
def cancel_plugin_install(self, request_id):
self.install_requests.pop(request_id)
def cleanup_plugin_settings(self, name):
"""Removes any settings related to a plugin. Propably called when a plugin is uninstalled.
Args:
name (string): The name of the plugin
"""
hidden_plugins = self.settings.getSetting("hiddenPlugins", [])
hidden_plugins.remove(name)
self.settings.setSetting("hiddenPlugins", hidden_plugins)
plugin_order = self.settings.getSetting("pluginOrder")
plugin_order.remove(name)
self.settings.setSetting("pluginOrder", plugin_order)
logger.debug("Removed any settings for plugin %s", name)
+2 -1
View File
@@ -395,6 +395,7 @@ async def get_tab_lambda(test) -> Tab:
return 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_URL = "Valve Steam Gamepad/default" # Steam Big Picture Mode tab
def tab_is_gamepadui(t: Tab) -> bool:
@@ -415,7 +416,7 @@ async def inject_to_tab(tab_name, js, run_async=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 DO_NOT_CLOSE_URL not in t.url):
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)
+13 -4
View File
@@ -62,19 +62,19 @@ class Loader:
self.logger = getLogger("Loader")
self.plugin_path = plugin_path
self.logger.info(f"plugin_path: {self.plugin_path}")
self.plugins = {}
self.plugins : dict[str, PluginWrapper] = {}
self.watcher = None
self.live_reload = live_reload
self.reload_queue = Queue()
self.loop.create_task(self.handle_reloads())
if live_reload:
self.reload_queue = Queue()
self.observer = Observer()
self.watcher = FileChangeHandler(self.reload_queue, plugin_path)
self.observer.schedule(self.watcher, self.plugin_path, recursive=True)
self.observer.start()
self.loop.create_task(self.handle_reloads())
self.loop.create_task(self.enable_reload_wait())
server_instance.add_routes([
web.get("/frontend/{path:.*}", self.handle_frontend_assets),
web.get("/locales/{path:.*}", self.handle_frontend_locales),
@@ -82,6 +82,7 @@ class Loader:
web.get("/plugins/{plugin_name}/frontend_bundle", self.handle_frontend_bundle),
web.post("/plugins/{plugin_name}/methods/{method_name}", self.handle_plugin_method_call),
web.get("/plugins/{plugin_name}/assets/{path:.*}", self.handle_plugin_frontend_assets),
web.post("/plugins/{plugin_name}/reload", self.handle_backend_reload_request),
# The following is legacy plugin code.
web.get("/plugins/load_main/{name}", self.load_plugin_main_view),
@@ -217,3 +218,11 @@ class Loader:
return web.Response(text=await tab.get_steam_resource(f"https://steamloopback.host/{request.match_info['path']}"), content_type="text/html")
except Exception as e:
return web.Response(text=str(e), status=400)
async def handle_backend_reload_request(self, request):
plugin_name : str = request.match_info["plugin_name"]
plugin = self.plugins[plugin_name]
await self.reload_queue.put((plugin.file, plugin.plugin_directory))
return web.Response(status=200)
+3 -3
View File
@@ -27,20 +27,20 @@
"install": {
"button_idle": "Installieren",
"button_processing": "Wird installiert",
"desc": "Bist du dir sicher, dass du {{artifact}}{{version}} installieren willst?",
"desc": "Bist du dir sicher, dass du {{artifact}} {{version}} installieren willst?",
"title": "Installiere {{artifact}}"
},
"reinstall": {
"button_idle": "Neu installieren",
"button_processing": "Wird neu installiert",
"desc": "Bist du dir sicher, dass du {{artifact}}{{version}} neu installieren willst?",
"desc": "Bist du dir sicher, dass du {{artifact}} {{version}} neu installieren willst?",
"title": "Neu installation {{artifact}}"
},
"update": {
"button_idle": "Aktualisieren",
"button_processing": "Wird aktualisiert",
"title": "Aktualisiere {{artifact}}",
"desc": "Bist du dir sicher, dass du {{artifact}}{{version}} aktualisieren willst?"
"desc": "Bist du dir sicher, dass du {{artifact}} {{version}} aktualisieren willst?"
},
"no_hash": "Diese Erweiterung besitzt keine Prüfsumme, Installation auf eigene Gefahr."
},
+14
View File
@@ -17,6 +17,13 @@
"select": "Use this folder"
}
},
"PluginView": {
"hidden_one": "1 plugin is hidden from this list",
"hidden_other": "{{count}} plugins are hidden from this list"
},
"PluginListLabel": {
"hidden": "Hidden from the quick access menu"
},
"PluginCard": {
"plugin_full_access": "This plugin has full access to your Steam Deck.",
"plugin_install": "Install",
@@ -73,6 +80,8 @@
"reload": "Reload",
"uninstall": "Uninstall",
"update_to": "Update to {{name}}",
"show": "Quick access: Show",
"hide": "Quick access: Hide",
"update_all_one": "Update 1 plugin",
"update_all_other": "Update {{count}} plugins"
},
@@ -100,6 +109,11 @@
}
},
"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.",
+131 -26
View File
@@ -2,45 +2,72 @@
"SettingsDeveloperIndex": {
"third_party_plugins": {
"button_install": "Instalar",
"button_zip": "Navegar"
"button_zip": "Navegar",
"label_desc": "URL",
"label_url": "Instalar plugin desde URL",
"label_zip": "Instalar plugin desde archivo ZIP",
"header": "Plugins de terceros"
},
"valve_internal": {
"desc2": "No toque nada en este menú a menos que sepa lo que hace."
"desc2": "No toques nada en este menú a menos que sepas lo que haces.",
"label": "Activar menú interno de Valve",
"desc1": "Activa el menú interno de desarrollo de Valve."
},
"toast_zip": {
"title": "Decky"
}
"title": "Decky",
"body": "¡Ha fallado la instalación! Solo se permiten archivos ZIP."
},
"cef_console": {
"button": "Abrir consola",
"label": "Consola CEF",
"desc": "Abre la consola del CEF. Solamente es útil para propósitos de depuración. Las cosas que hagas aquí pueden ser potencialmente peligrosas y solo se debería usar si eres un desarrollador de plugins, o uno te ha dirigido aquí."
},
"react_devtools": {
"ip_label": "IP",
"label": "Activar DevTools de React",
"desc": "Permite la conexión a un ordenador ejecutando las DevTools de React. Cambiar este ajuste recargará Steam. Configura la dirección IP antes de activarlo."
},
"header": "Otros"
},
"PluginInstallModal": {
"install": {
"button_idle": "Instalar",
"button_processing": "Instalando"
"button_processing": "Instalando",
"title": "Instalar {{artifact}}",
"desc": "¿Estás seguro de que quieres instalar {{artifact}} {{version}}?"
},
"reinstall": {
"button_idle": "Reinstalar",
"button_processing": "Reinstalando"
"button_processing": "Reinstalando",
"desc": "¿Estás seguro de que quieres reinstalar {{artifact}} {{version}}?",
"title": "Reinstalar {{artifact}}"
},
"update": {
"button_processing": "Actualizando",
"button_idle": "Actualizar"
}
"button_idle": "Actualizar",
"desc": "¿Estás seguro de que quieres actualizar {{artifact}} {{version}}?",
"title": "Actualizar {{artifact}}"
},
"no_hash": "Este plugin no tiene un hash, lo estás instalando bajo tu propia responsabilidad."
},
"Developer": {
"disabling": "Desactivando",
"enabling": "Activando",
"disabling": "Desactivando DevTools de React",
"enabling": "Activando DevTools de React",
"5secreload": "Recargando en 5 segundos"
},
"BranchSelect": {
"update_channel": {
"prerelease": "Prelanzamiento",
"stable": "Estable",
"label": "Canal de actualización"
"label": "Canal de actualización",
"testing": "Pruebas"
}
},
"PluginCard": {
"plugin_full_access": "Este plugin tiene acceso completo a su Steam Deck.",
"plugin_install": "Instalar",
"plugin_version_label": "Versión de Plugin"
"plugin_version_label": "Versión de Plugin",
"plugin_no_desc": "No se proporcionó una descripción."
},
"FilePickerIndex": {
"folder": {
@@ -51,19 +78,35 @@
"uninstall": "Desinstalar",
"reinstall": "Reinstalar",
"reload": "Recargar",
"plugin_actions": "Acciónes de Plugin",
"no_plugin": "¡No hay plugins instalados!"
"plugin_actions": "Acciones de plugin",
"no_plugin": "¡No hay plugins instalados!",
"update_all_one": "Actualizar 1 plugin",
"update_all_many": "Actualizar {{count}} plugins",
"update_all_other": "Actualizar {{count}} plugins",
"update_to": "Actualizar a {{name}}"
},
"PluginLoader": {
"error": "Error",
"plugin_uninstall": {
"button": "Desinstalar"
"button": "Desinstalar",
"desc": "¿Estás seguro de que quieres desinstalar {{name}}?",
"title": "Desinstalar {{name}}"
},
"decky_title": "Decky"
"decky_title": "Decky",
"plugin_update_one": "¡Actualización disponible para 1 plugin!",
"plugin_update_many": "¡Actualizaciones disponibles para {{count}} plugins!",
"plugin_update_other": "¡Actualizaciones disponibles para {{count}} plugins!",
"decky_update_available": "¡Actualización {{tag_name}} disponible!",
"plugin_load_error": {
"message": "Se ha producido un error al cargar el plugin {{name}}",
"toast": "Se ha producido un error al cargar {{name}}"
},
"plugin_error_uninstall": "Al cargar {{name}} se ha producido una excepción como se muestra arriba. Esto suele significar que el plugin requiere una actualización para la nueva versión de SteamUI. Comprueba si hay una actualización disponible o valora eliminarlo en los ajustes de Decky, en la sección Plugins."
},
"RemoteDebugging": {
"remote_cef": {
"desc": "Permitir acceso no autenticado al CEF debugger a cualquier persona en su red"
"desc": "Permitir acceso no autenticado al CEF debugger a cualquier persona en su red",
"label": "Permitir depuración remota del CEF"
}
},
"SettingsGeneralIndex": {
@@ -71,10 +114,18 @@
"header": "Actualizaciones"
},
"about": {
"header": "Información"
"header": "Acerca de",
"decky_version": "Versión de Decky"
},
"developer_mode": {
"label": "Modo Desarrollador"
"label": "Modo desarrollador",
"desc": "Activa los ajustes de desarrollador de Decky."
},
"beta": {
"header": "Participación en la beta"
},
"other": {
"header": "Otros"
}
},
"SettingsIndex": {
@@ -88,26 +139,80 @@
"label": "Buscar"
},
"store_sort": {
"label": "Ordenar"
"label": "Ordenar",
"label_def": "Actualizado por última vez (Nuevos)"
},
"store_contrib": {
"desc": "Si desea contribuir a la Tienda de Decky Plugin, revise el repositorio SteamDeckHomebrew/decky-plugin-template en GitHub. Información acerca del desarrollo y distribución está disponible en al archivo README.",
"desc": "Si desea contribuir a la tienda de plugins de Decky, mira el repositorio SteamDeckHomebrew/decky-plugin-template en GitHub. Hay información acerca del desarrollo y distribución en el archivo README.",
"label": "Contribuyendo"
},
"store_tabs": {
"about": "Información",
"title": "Navegar"
"title": "Navegar",
"alph_asce": "Alfabéticamente (Z-A)",
"alph_desc": "Alfabéticamente (A-Z)"
},
"store_testing_cta": "¡Por favor considera probando plugins nuevos para ayudar al equipo de Decky Loader!"
"store_testing_cta": "¡Por favor considera probar plugins nuevos para ayudar al equipo de Decky Loader!",
"store_source": {
"desc": "El código fuente de los plugins está disponible en el repositiorio SteamDeckHomebrew/decky-plugin-database en GitHub.",
"label": "Código fuente"
},
"store_filter": {
"label_def": "Todos",
"label": "Filtrar"
}
},
"Updater": {
"updates": {
"reloading": "Recargando",
"updating": "Actualizando",
"checking": "Buscando",
"check_button": "Buscar Actualizaciones",
"install_button": "Instalar Actualización",
"label": "Actualizaciones"
"check_button": "Buscar actualizaciones",
"install_button": "Instalar actualización",
"label": "Actualizaciones",
"lat_version": "Actualizado: ejecutando {{ver}}",
"cur_version": "Versión actual: {{ver}}"
},
"decky_updates": "Actualizaciones de Decky",
"no_patch_notes_desc": "No hay notas de parche para esta versión",
"patch_notes_desc": "Notas de parche"
},
"MultiplePluginsInstallModal": {
"title": {
"reinstall_one": "Reinstalar 1 plugin",
"reinstall_many": "Reinstalar {{count}} plugins",
"reinstall_other": "Reinstalar {{count}} plugins",
"update_one": "Actualizar 1 plugin",
"update_many": "Actualizar {{count}} plugins",
"update_other": "Actualizar {{count}} plugins",
"mixed_one": "Modificar 1 plugin",
"mixed_many": "Modificar {{count}} plugins",
"mixed_other": "Modificar {{count}} plugins",
"install_one": "Instalar 1 plugin",
"install_many": "Instalar {{count}} plugins",
"install_other": "Instalar {{count}} plugins"
},
"ok_button": {
"idle": "Confirmar",
"loading": "Trabajando"
},
"confirm": "¿Estás seguro de que quieres hacer las siguientes modificaciones?",
"description": {
"install": "Instalar {{name}} {{version}}",
"update": "Actualizar {{name}} a {{version}}",
"reinstall": "Reinstalar {{name}} {{version}}"
}
},
"StoreSelect": {
"custom_store": {
"url_label": "URL",
"label": "Tienda personalizada"
},
"store_channel": {
"custom": "Personalizada",
"default": "Por defecto",
"label": "Canál de la tienda",
"testing": "Pruebas"
}
}
}
+45 -1
View File
@@ -50,7 +50,12 @@
"reinstall": "Reinstalla",
"reload": "Ricarica",
"uninstall": "Rimuovi",
"update_to": "Aggiorna a {{name}}"
"update_to": "Aggiorna a {{name}}",
"update_all_one": "Aggiorna un plugin",
"update_all_many": "Aggiorna {{count}} plugins",
"update_all_other": "Aggiorna {{count}} plugins",
"show": "Accesso rapido: Mostra",
"hide": "Accesso rapido: Nascondi"
},
"PluginLoader": {
"decky_title": "Decky",
@@ -99,6 +104,11 @@
"desc1": "Abilita il menu di sviluppo interno di Valve.",
"desc2": "Non toccare nulla in questo menu se non sai quello che fa.",
"label": "Abilita Menu Sviluppatore"
},
"cef_console": {
"label": "Console CEF",
"button": "Apri la console",
"desc": "Apri la console di CEF. Utile solamente per ragioni di debug. Questa opzione deve essere usata solo se sei uno sviluppatore di plugin o se uno di questi ti ha chiesto di farlo, visto che questa feature potrebbe essere potenzialmente pericolosa."
}
},
"SettingsGeneralIndex": {
@@ -180,5 +190,39 @@
"reloading": "Ricaricando",
"updating": "Aggiornando"
}
},
"MultiplePluginsInstallModal": {
"title": {
"mixed_one": "Modifica un plugin",
"mixed_many": "Modifica {{count}} plugins",
"mixed_other": "Modifica {{count}} plugins",
"update_one": "Aggiorna un plugin",
"update_many": "Aggiorna {{count}} plugins",
"update_other": "Aggiorna {{count}} plugins",
"reinstall_one": "Reinstalla un plugin",
"reinstall_many": "Reinstalla {{count}} plugins",
"reinstall_other": "Reinstalla {{count}} plugins",
"install_one": "Installa un plugin",
"install_many": "Installa {{count}} plugins",
"install_other": "Installa {{count}} plugins"
},
"confirm": "Sei sicuro di voler effettuare le modifiche seguenti?",
"ok_button": {
"idle": "Conferma",
"loading": "Elaboro"
},
"description": {
"install": "Installa {{name}} {{version}}",
"update": "Aggiorna {{name}} alla versione {{version}}",
"reinstall": "Reinstalla {{name}} {{version}}"
}
},
"PluginView": {
"hidden_one": "Un plugin è nascosto in questa lista",
"hidden_many": "Sono nascosti {{count}} plugin da questa lista",
"hidden_other": "Sono nascosti {{count}} plugin da questa lista"
},
"PluginListLabel": {
"hidden": "Nascosti dal menu di accesso rapido"
}
}
+30 -5
View File
@@ -9,8 +9,8 @@
},
"Developer": {
"5secreload": "5 秒钟后重新加载",
"disabling": "正在禁用",
"enabling": "正在启用"
"disabling": "正在禁用 React DevTools",
"enabling": "正在启用 React DevTools"
},
"FilePickerIndex": {
"folder": {
@@ -50,12 +50,13 @@
"reinstall": "重新安装",
"reload": "重新加载",
"uninstall": "卸载",
"update_to": "更新 {{name}}"
"update_to": "更新 {{name}}",
"update_all_other": "更新 {{count}} 个插件"
},
"PluginLoader": {
"decky_title": "Decky",
"error": "错误",
"plugin_error_uninstall": "如果你想卸载插件请点击 Decky 菜单中的 <0></0> 图标",
"plugin_error_uninstall": "加载 {{name}} 时引起了上述异常。这通常意味着插件需要更新以适应 SteamUI 的新版本。请检查插件是否有更新,或在 Decky 设置中的插件部分将其移除。",
"plugin_load_error": {
"message": "加载插件 {{name}} 错误",
"toast": "加载插件 {{name}} 发生了错误"
@@ -96,7 +97,13 @@
"desc1": "启用 Valve 内部开发者菜单",
"desc2": "除非你知道你在干什么,否则请不要修改此菜单中的任何内容",
"label": "启用 Valve 内部开发者"
}
},
"cef_console": {
"button": "打开控制台",
"label": "CEF 控制台",
"desc": "打开 CEF 控制台。仅在调试目的下使用。这列选项均有风险,请仅在您是插件开发者或是在插件开发者指导时访问使用。"
},
"header": "其他"
},
"SettingsGeneralIndex": {
"about": {
@@ -177,5 +184,23 @@
"reloading": "重新加载中",
"updating": "更新中"
}
},
"MultiplePluginsInstallModal": {
"title": {
"mixed_other": "更改 {{count}} 个插件",
"update_other": "更新 {{count}} 个插件",
"reinstall_other": "重装 {{count}} 个插件",
"install_other": "安装 {{count}} 个插件"
},
"ok_button": {
"idle": "确认",
"loading": "工作中"
},
"confirm": "确定要进行以下修改吗?",
"description": {
"install": "安装 {{name}} {{version}}",
"update": "更新 {{name}} to {{version}}",
"reinstall": "重装 {{name}} {{version}}"
}
}
}
+5 -3
View File
@@ -55,7 +55,7 @@
"PluginLoader": {
"decky_title": "Decky",
"error": "錯誤",
"plugin_error_uninstall": "如果您要解除安裝外掛程式,請在 Decky 選單,按下 <0></0>。",
"plugin_error_uninstall": "載入 {{name}} 導致上述異常。這通常意味著該外掛程式需要針對新版本的 SteamUI 進行更新。在 Decky 設定中檢查是否存在更新,或評估刪除此外掛程式。",
"plugin_load_error": {
"message": "載入外掛程式 {{name}} 發生錯誤",
"toast": "{{name}} 載入出錯"
@@ -65,7 +65,8 @@
"title": "解除安裝 {{name}}",
"desc": "您確定要解除安裝 {{name}} 嗎?"
},
"decky_update_available": "可更新至版本 {{tag_name}}"
"decky_update_available": "可更新至版本 {{tag_name}}",
"plugin_update_other": "可更新 {{count}} 個外掛程式!"
},
"RemoteDebugging": {
"remote_cef": {
@@ -95,7 +96,8 @@
"desc": "啟用與執行 React DevTools 的電腦的連接。改變這個設定將重新載入 Steam。啟用前必須設定 IP 位址。",
"ip_label": "IP",
"label": "啟用 React DevTools"
}
},
"header": "其他"
},
"SettingsGeneralIndex": {
"about": {
+1 -1
View File
@@ -22,7 +22,7 @@ class SettingsManager:
for file in listdir(wrong_dir):
if file.endswith(".json"):
rename(path.join(wrong_dir,file),
path.join(settings_directory, file))
path.join(settings_directory, file))
self.path = path.join(settings_directory, name + ".json")
+2
View File
@@ -67,9 +67,11 @@ class Updater:
logger.info("Current branch is not set, determining branch from version...")
if self.localVer.startswith("v") and "-pre" in self.localVer:
logger.info("Current version determined to be pre-release")
manager.setSetting('branch', 1)
return 1
else:
logger.info("Current version determined to be stable")
manager.setSetting('branch', 0)
return 0
return ver
+6 -2
View File
@@ -7,7 +7,7 @@ from asyncio import sleep, start_server, gather, open_connection
from aiohttp import ClientSession, web
from logging import getLogger
from injector import inject_to_tab, get_gamepadui_tab, close_old_tabs
from injector import inject_to_tab, get_gamepadui_tab, close_old_tabs, get_tab
import helpers
import subprocess
from localplatform import service_stop, service_start
@@ -32,7 +32,8 @@ class Utilities:
"get_setting": self.get_setting,
"filepicker_ls": self.filepicker_ls,
"disable_rdt": self.disable_rdt,
"enable_rdt": self.enable_rdt
"enable_rdt": self.enable_rdt,
"get_tab_id": self.get_tab_id
}
self.logger = getLogger("Utilities")
@@ -287,3 +288,6 @@ class Utilities:
await close_old_tabs()
await tab.evaluate_js("location.reload();", False, True, False)
self.logger.info("React DevTools disabled")
async def get_tab_id(self, name):
return (await get_tab(name)).id
+2
View File
@@ -2,3 +2,5 @@ node_modules/
.yalc
yalc.lock
stats.html
+2 -1
View File
@@ -33,6 +33,7 @@
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-external-globals": "^0.6.1",
"rollup-plugin-polyfill-node": "^0.10.2",
"rollup-plugin-visualizer": "^5.9.0",
"tslib": "^2.5.2",
"typescript": "^4.9.5"
},
@@ -43,7 +44,7 @@
}
},
"dependencies": {
"decky-frontend-lib": "3.20.7",
"decky-frontend-lib": "3.21.1",
"i18next": "^22.5.0",
"i18next-http-backend": "^2.2.1",
"react-file-icon": "^1.3.0",
+98 -4
View File
@@ -2,8 +2,8 @@ lockfileVersion: '6.0'
dependencies:
decky-frontend-lib:
specifier: 3.20.7
version: 3.20.7
specifier: 3.21.1
version: 3.21.1
i18next:
specifier: ^22.5.0
version: 22.5.0
@@ -93,6 +93,9 @@ devDependencies:
rollup-plugin-polyfill-node:
specifier: ^0.10.2
version: 0.10.2(rollup@2.79.1)
rollup-plugin-visualizer:
specifier: ^5.9.0
version: 5.9.0(rollup@2.79.1)
tslib:
specifier: ^2.5.2
version: 2.5.2
@@ -1235,6 +1238,15 @@ packages:
engines: {node: '>= 10'}
dev: true
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
dev: true
/clone-buffer@1.0.0:
resolution: {integrity: sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==}
engines: {node: '>= 0.10'}
@@ -1393,8 +1405,8 @@ packages:
dependencies:
ms: 2.1.2
/decky-frontend-lib@3.20.7:
resolution: {integrity: sha512-Zwwbo50cqpTbCfSCZaqITgTRvWs7pK9KO1A+Oo2sCC/DqOfyUtEH5niNPid4Qxu+yh4lsbEjTurJk1nCfd+nZw==}
/decky-frontend-lib@3.21.1:
resolution: {integrity: sha512-30605ET9qqZ6St6I9WmMmLGgSrTIdMwo7xy85+lRaF1miUd2icOGEJjwnbVcZDdkal+1fJ3tNEDXlchVfG4TrA==}
dev: false
/decode-named-character-reference@1.0.2:
@@ -1414,6 +1426,11 @@ packages:
clone: 1.0.4
dev: true
/define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
dev: true
/define-properties@1.2.0:
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
engines: {node: '>= 0.4'}
@@ -1770,6 +1787,11 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
/get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
dev: true
/get-intrinsic@1.2.1:
resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
dependencies:
@@ -2126,6 +2148,12 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-docker@2.2.1:
resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
engines: {node: '>=8'}
hasBin: true
dev: true
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
@@ -2222,6 +2250,13 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-wsl@2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
dependencies:
is-docker: 2.2.1
dev: true
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: true
@@ -2891,6 +2926,15 @@ packages:
mimic-fn: 2.1.0
dev: true
/open@8.4.2:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
dependencies:
define-lazy-prop: 2.0.0
is-docker: 2.2.1
is-wsl: 2.2.0
dev: true
/ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
@@ -3237,6 +3281,11 @@ packages:
engines: {node: '>= 10'}
dev: true
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
dev: true
/resolve-from@3.0.0:
resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
engines: {node: '>=4'}
@@ -3318,6 +3367,23 @@ packages:
rollup: 2.79.1
dev: true
/rollup-plugin-visualizer@5.9.0(rollup@2.79.1):
resolution: {integrity: sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==}
engines: {node: '>=14'}
hasBin: true
peerDependencies:
rollup: 2.x || 3.x
peerDependenciesMeta:
rollup:
optional: true
dependencies:
open: 8.4.2
picomatch: 2.3.1
rollup: 2.79.1
source-map: 0.7.4
yargs: 17.7.2
dev: true
/rollup@2.79.1:
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
engines: {node: '>=10.0.0'}
@@ -3425,6 +3491,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/source-map@0.7.4:
resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
engines: {node: '>= 8'}
dev: true
/sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
deprecated: Please use @jridgewell/sourcemap-codec instead
@@ -3958,10 +4029,33 @@ packages:
engines: {node: '>=0.4'}
dev: true
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
/yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
dev: true
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: true
/yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
dependencies:
cliui: 8.0.1
escalade: 3.1.1
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
dev: true
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: false
+4 -2
View File
@@ -7,6 +7,7 @@ import typescript from '@rollup/plugin-typescript';
import { defineConfig } from 'rollup';
import del from 'rollup-plugin-delete';
import externalGlobals from 'rollup-plugin-external-globals';
import { visualizer } from 'rollup-plugin-visualizer';
const hiddenWarnings = ['THIS_IS_UNDEFINED', 'EVAL'];
@@ -16,7 +17,7 @@ export default defineConfig({
del({ targets: '../backend/static/*', force: true }),
commonjs(),
nodeResolve({
browser: true
browser: true,
}),
externalGlobals({
react: 'SP_REACT',
@@ -33,6 +34,7 @@ export default defineConfig({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
image(),
visualizer(),
],
preserveEntrySignatures: false,
output: {
@@ -46,4 +48,4 @@ export default defineConfig({
if (hiddenWarnings.some((warning) => message.code === warning)) return;
handleWarning(message);
},
});
});
+13 -5
View File
@@ -7,6 +7,7 @@ import { VerInfo } from '../updater';
interface PublicDeckyState {
plugins: Plugin[];
pluginOrder: string[];
hiddenPlugins: string[];
activePlugin: Plugin | null;
updates: PluginUpdateMapping | null;
hasLoaderUpdate?: boolean;
@@ -17,6 +18,7 @@ interface PublicDeckyState {
export class DeckyState {
private _plugins: Plugin[] = [];
private _pluginOrder: string[] = [];
private _hiddenPlugins: string[] = [];
private _activePlugin: Plugin | null = null;
private _updates: PluginUpdateMapping | null = null;
private _hasLoaderUpdate: boolean = false;
@@ -29,6 +31,7 @@ export class DeckyState {
return {
plugins: this._plugins,
pluginOrder: this._pluginOrder,
hiddenPlugins: this._hiddenPlugins,
activePlugin: this._activePlugin,
updates: this._updates,
hasLoaderUpdate: this._hasLoaderUpdate,
@@ -52,6 +55,11 @@ export class DeckyState {
this.notifyUpdate();
}
setHiddenPlugins(hiddenPlugins: string[]) {
this._hiddenPlugins = hiddenPlugins;
this.notifyUpdate();
}
setActivePlugin(name: string) {
this._activePlugin = this._plugins.find((plugin) => plugin.name === name) ?? null;
this.notifyUpdate();
@@ -111,11 +119,11 @@ export const DeckyStateContextProvider: FC<Props> = ({ children, deckyState }) =
return () => deckyState.eventBus.removeEventListener('update', onUpdate);
}, []);
const setIsLoaderUpdating = (hasUpdate: boolean) => deckyState.setIsLoaderUpdating(hasUpdate);
const setVersionInfo = (versionInfo: VerInfo) => deckyState.setVersionInfo(versionInfo);
const setActivePlugin = (name: string) => deckyState.setActivePlugin(name);
const closeActivePlugin = () => deckyState.closeActivePlugin();
const setPluginOrder = (pluginOrder: string[]) => deckyState.setPluginOrder(pluginOrder);
const setIsLoaderUpdating = deckyState.setIsLoaderUpdating.bind(deckyState);
const setVersionInfo = deckyState.setVersionInfo.bind(deckyState);
const setActivePlugin = deckyState.setActivePlugin.bind(deckyState);
const closeActivePlugin = deckyState.closeActivePlugin.bind(deckyState);
const setPluginOrder = deckyState.setPluginOrder.bind(deckyState);
return (
<DeckyStateContext.Provider
+11
View File
@@ -8,6 +8,8 @@ import {
staticClasses,
} from 'decky-frontend-lib';
import { VFC, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash } from 'react-icons/fa';
import { Plugin } from '../plugin';
import { useDeckyState } from './DeckyState';
@@ -16,8 +18,10 @@ import { useQuickAccessVisible } from './QuickAccessVisibleState';
import TitleView from './TitleView';
const PluginView: VFC = () => {
const { hiddenPlugins } = useDeckyState();
const { plugins, updates, activePlugin, pluginOrder, setActivePlugin, closeActivePlugin } = useDeckyState();
const visible = useQuickAccessVisible();
const { t } = useTranslation();
const [pluginList, setPluginList] = useState<Plugin[]>(
plugins.sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name)),
@@ -48,6 +52,7 @@ const PluginView: VFC = () => {
<PanelSection>
{pluginList
.filter((p) => p.content)
.filter(({ name }) => !hiddenPlugins.includes(name))
.map(({ name, icon }) => (
<PanelSectionRow key={name}>
<ButtonItem layout="below" onClick={() => setActivePlugin(name)}>
@@ -59,6 +64,12 @@ const PluginView: VFC = () => {
</ButtonItem>
</PanelSectionRow>
))}
{hiddenPlugins.length > 0 && (
<div style={{ display: 'flex', alignItems: 'center', gap: '10px', fontSize: '0.8rem', marginTop: '10px' }}>
<FaEyeSlash />
<div>{t('PluginView.hidden', { count: hiddenPlugins.length })}</div>
</div>
)}
</PanelSection>
</div>
</>
@@ -0,0 +1,30 @@
import { ConfirmModal } from 'decky-frontend-lib';
import { FC } from 'react';
interface PluginUninstallModalProps {
name: string;
title: string;
buttonText: string;
description: string;
closeModal?(): void;
}
const PluginUninstallModal: FC<PluginUninstallModalProps> = ({ name, title, buttonText, description, closeModal }) => {
return (
<ConfirmModal
closeModal={closeModal}
onOK={async () => {
await window.DeckyPluginLoader.callServerMethod('uninstall_plugin', { name });
// 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.hiddenPluginsService.invalidate();
}}
strTitle={title}
strOKButtonText={buttonText}
>
{description}
</ConfirmModal>
);
};
export default PluginUninstallModal;
@@ -4,12 +4,13 @@ import {
DialogControlsSection,
DialogControlsSectionHeader,
Field,
Navigation,
TextField,
Toggle,
} from 'decky-frontend-lib';
import { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaFileArchive, FaLink, FaReact, FaSteamSymbol } from 'react-icons/fa';
import { FaFileArchive, FaLink, FaReact, FaSteamSymbol, FaTerminal } from 'react-icons/fa';
import { setShouldConnectToReactDevTools, setShowValveInternal } from '../../../../developer';
import { installFromURL } from '../../../../store';
@@ -75,6 +76,27 @@ export default function DeveloperSettings() {
</DialogControlsSection>
<DialogControlsSection>
<DialogControlsSectionHeader>{t('SettingsDeveloperIndex.header')}</DialogControlsSectionHeader>
<Field
label={t('SettingsDeveloperIndex.cef_console.label')}
description={<span style={{ whiteSpace: 'pre-line' }}>{t('SettingsDeveloperIndex.cef_console.desc')}</span>}
icon={<FaTerminal style={{ display: 'block' }} />}
>
<DialogButton
onClick={async () => {
let res = await window.DeckyPluginLoader.callServerMethod('get_tab_id', { name: 'SharedJSContext' });
if (res.success) {
Navigation.NavigateToExternalWeb(
'localhost:8080/devtools/inspector.html?ws=localhost:8080/devtools/page/' + res.result,
);
} else {
console.error('Unable to find ID for SharedJSContext tab ', res.result);
Navigation.NavigateToExternalWeb('localhost:8080');
}
}}
>
{t('SettingsDeveloperIndex.cef_console.button')}
</DialogButton>
</Field>
<RemoteDebuggingSettings />
<Field
label={t('SettingsDeveloperIndex.valve_internal.label')}
@@ -21,7 +21,7 @@ const BranchSelect: FunctionComponent<{}> = () => {
t('BranchSelect.update_channel.prerelease'),
t('BranchSelect.update_channel.testing'),
];
const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Prerelease);
const [selectedBranch, setSelectedBranch] = useSetting<UpdateBranch>('branch', UpdateBranch.Stable);
return (
// Returns numerical values from 0 to 2 (with current branch setup as of 8/28/22)
@@ -0,0 +1,34 @@
import { FC } from 'react';
import { useTranslation } from 'react-i18next';
import { FaEyeSlash } from 'react-icons/fa';
interface PluginListLabelProps {
hidden: boolean;
name: string;
version?: string;
}
const PluginListLabel: FC<PluginListLabelProps> = ({ name, hidden, version }) => {
const { t } = useTranslation();
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<div>{version ? `${name} - ${version}` : name}</div>
{hidden && (
<div
style={{
fontSize: '0.8rem',
color: '#dcdedf',
display: 'flex',
alignItems: 'center',
gap: '10px',
}}
>
<FaEyeSlash />
{t('PluginListLabel.hidden')}
</div>
)}
</div>
);
};
export default PluginListLabel;
@@ -22,10 +22,7 @@ import {
} from '../../../../store';
import { useSetting } from '../../../../utils/hooks/useSetting';
import { useDeckyState } from '../../../DeckyState';
function labelToName(pluginLabel: string, pluginVersion?: string): string {
return pluginVersion ? pluginLabel.substring(0, pluginLabel.indexOf(` - ${pluginVersion}`)) : pluginLabel;
}
import PluginListLabel from './PluginListLabel';
async function reinstallPlugin(pluginName: string, currentVersion?: string) {
const serverData = await getPluginList();
@@ -36,29 +33,57 @@ async function reinstallPlugin(pluginName: string, currentVersion?: string) {
}
}
function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
const data = props.entry.data;
type PluginTableData = PluginData & { name: string; hidden: boolean; onHide(): void; onShow(): void };
function PluginInteractables(props: { entry: ReorderableEntry<PluginTableData> }) {
const { t } = useTranslation();
let pluginName = labelToName(props.entry.label, data?.version);
// nothing to display without this data...
if (!props.entry.data) {
return null;
}
const { name, update, version, onHide, onShow, hidden } = props.entry.data;
const showCtxMenu = (e: MouseEvent | GamepadEvent) => {
showContextMenu(
<Menu label={t('PluginListIndex.plugin_actions')}>
<MenuItem onSelected={() => window.DeckyPluginLoader.importPlugin(pluginName, data?.version)}>
<MenuItem
onSelected={() => {
try {
fetch(`http://127.0.0.1:1337/plugins/${name}/reload`, {
method: 'POST',
credentials: 'include',
headers: {
Authentication: window.deckyAuthToken,
},
});
} catch (err) {
console.error('Error Reloading Plugin Backend', err);
}
window.DeckyPluginLoader.importPlugin(name, version);
}}
>
{t('PluginListIndex.reload')}
</MenuItem>
<MenuItem
onSelected={() =>
window.DeckyPluginLoader.uninstallPlugin(
pluginName,
t('PluginLoader.plugin_uninstall.title', { name: pluginName }),
name,
t('PluginLoader.plugin_uninstall.title', { name }),
t('PluginLoader.plugin_uninstall.button'),
t('PluginLoader.plugin_uninstall.desc', { name: pluginName }),
t('PluginLoader.plugin_uninstall.desc', { name }),
)
}
>
{t('PluginListIndex.uninstall')}
</MenuItem>
{hidden ? (
<MenuItem onSelected={onShow}>{t('PluginListIndex.show')}</MenuItem>
) : (
<MenuItem onSelected={onHide}>{t('PluginListIndex.hide')}</MenuItem>
)}
</Menu>,
e.currentTarget ?? window,
);
@@ -66,22 +91,22 @@ function PluginInteractables(props: { entry: ReorderableEntry<PluginData> }) {
return (
<>
{data?.update ? (
{update ? (
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => requestPluginInstall(pluginName, data?.update as StorePluginVersion, InstallType.UPDATE)}
onOKButton={() => requestPluginInstall(pluginName, data?.update as StorePluginVersion, InstallType.UPDATE)}
onClick={() => requestPluginInstall(name, update, InstallType.UPDATE)}
onOKButton={() => requestPluginInstall(name, update, InstallType.UPDATE)}
>
<div style={{ display: 'flex', minWidth: '180px', justifyContent: 'space-between', alignItems: 'center' }}>
{t('PluginListIndex.update_to', { name: data?.update?.name })}
{t('PluginListIndex.update_to', { name: update.name })}
<FaDownload style={{ paddingLeft: '1rem' }} />
</div>
</DialogButton>
) : (
<DialogButton
style={{ height: '40px', minWidth: '60px', marginRight: '10px' }}
onClick={() => reinstallPlugin(pluginName, data?.version)}
onOKButton={() => reinstallPlugin(pluginName, data?.version)}
onClick={() => reinstallPlugin(name, version)}
onOKButton={() => reinstallPlugin(name, version)}
>
<div style={{ display: 'flex', minWidth: '180px', justifyContent: 'space-between', alignItems: 'center' }}>
{t('PluginListIndex.reinstall')}
@@ -114,7 +139,7 @@ type PluginData = {
};
export default function PluginList() {
const { plugins, updates, pluginOrder, setPluginOrder } = useDeckyState();
const { plugins, updates, pluginOrder, setPluginOrder, hiddenPlugins } = useDeckyState();
const [_, setPluginOrderSetting] = useSetting<string[]>(
'pluginOrder',
plugins.map((plugin) => plugin.name),
@@ -125,22 +150,29 @@ export default function PluginList() {
window.DeckyPluginLoader.checkPluginUpdates();
}, []);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginData>[]>([]);
const [pluginEntries, setPluginEntries] = useState<ReorderableEntry<PluginTableData>[]>([]);
const hiddenPluginsService = window.DeckyPluginLoader.hiddenPluginsService;
useEffect(() => {
setPluginEntries(
plugins.map((plugin) => {
plugins.map(({ name, version }) => {
const hidden = hiddenPlugins.includes(name);
return {
label: plugin.version ? `${plugin.name} - ${plugin.version}` : plugin.name,
label: <PluginListLabel name={name} hidden={hidden} version={version} />,
position: pluginOrder.indexOf(name),
data: {
update: updates?.get(plugin.name),
version: plugin.version,
name,
hidden,
version,
update: updates?.get(name),
onHide: () => hiddenPluginsService.update([...hiddenPlugins, name]),
onShow: () => hiddenPluginsService.update(hiddenPlugins.filter((pluginName) => name !== pluginName)),
},
position: pluginOrder.indexOf(plugin.name),
};
}),
);
}, [plugins, updates]);
}, [plugins, updates, hiddenPlugins]);
if (plugins.length === 0) {
return (
@@ -150,8 +182,8 @@ export default function PluginList() {
);
}
function onSave(entries: ReorderableEntry<PluginData>[]) {
const newOrder = entries.map((entry) => labelToName(entry.label, entry?.data?.version));
function onSave(entries: ReorderableEntry<PluginTableData>[]) {
const newOrder = entries.map((entry) => entry.data!.name);
console.log(newOrder);
setPluginOrder(newOrder);
setPluginOrderSetting(newOrder);
@@ -184,7 +216,7 @@ export default function PluginList() {
</DialogButton>
)}
<DialogControlsSection style={{ marginTop: 0 }}>
<ReorderableList<PluginData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
<ReorderableList<PluginTableData> entries={pluginEntries} onSave={onSave} interactables={PluginInteractables} />
</DialogControlsSection>
</DialogBody>
);
+34
View File
@@ -0,0 +1,34 @@
import { DeckyState } from './components/DeckyState';
import { getSetting, setSetting } from './utils/settings';
/**
* A Service class for managing the state and actions related to the hidden plugins feature
*
* It's mostly responsible for sending setting updates to the server and keeping the local state in sync.
*/
export class HiddenPluginsService {
constructor(private deckyState: DeckyState) {}
init() {
getSetting<string[]>('hiddenPlugins', []).then((hiddenPlugins) => {
this.deckyState.setHiddenPlugins(hiddenPlugins);
});
}
/**
* Sends the new hidden plugins list to the server and persists it locally in the decky state
*
* @param hiddenPlugins The new list of hidden plugins
*/
async update(hiddenPlugins: string[]) {
await setSetting('hiddenPlugins', hiddenPlugins);
this.deckyState.setHiddenPlugins(hiddenPlugins);
}
/**
* Refreshes the state of hidden plugins in the local state
*/
async invalidate() {
this.deckyState.setHiddenPlugins(await getSetting('hiddenPlugins', []));
}
}
+7 -16
View File
@@ -1,5 +1,4 @@
import {
ConfirmModal,
ModalRoot,
PanelSection,
PanelSectionRow,
@@ -18,9 +17,11 @@ import LegacyPlugin from './components/LegacyPlugin';
import { deinitFilepickerPatches, initFilepickerPatches } from './components/modals/filepicker/patches';
import MultiplePluginsInstallModal from './components/modals/MultiplePluginsInstallModal';
import PluginInstallModal from './components/modals/PluginInstallModal';
import PluginUninstallModal from './components/modals/PluginUninstallModal';
import NotificationBadge from './components/NotificationBadge';
import PluginView from './components/PluginView';
import WithSuspense from './components/WithSuspense';
import { HiddenPluginsService } from './hidden-plugins-service';
import Logger from './logger';
import { InstallType, Plugin } from './plugin';
import RouterHook from './router-hook';
@@ -45,6 +46,7 @@ class PluginLoader extends Logger {
private routerHook: RouterHook = new RouterHook();
public toaster: Toaster = new Toaster();
private deckyState: DeckyState = new DeckyState();
public hiddenPluginsService = new HiddenPluginsService(this.deckyState);
private reloadLock: boolean = false;
// stores a list of plugin names which requested to be reloaded
@@ -182,21 +184,8 @@ class PluginLoader extends Logger {
);
}
public uninstallPlugin(name: string, title: string, button_text: string, description: string) {
showModal(
<ConfirmModal
onOK={async () => {
await this.callServerMethod('uninstall_plugin', { name });
}}
onCancel={() => {
// do nothing
}}
strTitle={title}
strOKButtonText={button_text}
>
{description}
</ConfirmModal>,
);
public uninstallPlugin(name: string, title: string, buttonText: string, description: string) {
showModal(<PluginUninstallModal name={name} title={title} buttonText={buttonText} description={description} />);
}
public hasPlugin(name: string) {
@@ -220,6 +209,8 @@ class PluginLoader extends Logger {
console.log(pluginOrder);
this.deckyState.setPluginOrder(pluginOrder);
});
this.hiddenPluginsService.init();
}
public deinit() {