mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-30 06:59:13 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8dfe5a87d | |||
| d0b7d1a4a6 | |||
| 9a05c228a0 | |||
| 00d9b03322 | |||
| 47bc910a84 | |||
| 1c6270ccd6 | |||
| 8f17f2b0fe | |||
| 81b601c0e7 | |||
| 83e8c89c97 | |||
| ca107feb25 | |||
| e5277190ed | |||
| 2e8e0fc7c1 | |||
| 8049417e03 | |||
| f4c0a8b5aa | |||
| d3584a9931 | |||
| b27b625921 |
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
@@ -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."
|
||||
},
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,3 +2,5 @@ node_modules/
|
||||
|
||||
.yalc
|
||||
yalc.lock
|
||||
|
||||
stats.html
|
||||
|
||||
@@ -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",
|
||||
|
||||
Generated
+98
-4
@@ -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
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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', []));
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user