diff --git a/README.md b/README.md index 44363da0..3df8a0e4 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ For more information about Decky Loader as well as documentation and development 1. Press the 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. diff --git a/backend/browser.py b/backend/browser.py index 388a01e3..ab71a89d 100644 --- a/backend/browser.py +++ b/backend/browser.py @@ -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) diff --git a/backend/injector.py b/backend/injector.py index b28a0349..e3414fee 100644 --- a/backend/injector.py +++ b/backend/injector.py @@ -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) diff --git a/backend/locales/en-US.json b/backend/locales/en-US.json index a347351b..b5c32957 100644 --- a/backend/locales/en-US.json +++ b/backend/locales/en-US.json @@ -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" }, diff --git a/backend/settings.py b/backend/settings.py index d54ff2b5..c00e6a82 100644 --- a/backend/settings.py +++ b/backend/settings.py @@ -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") diff --git a/frontend/.gitignore b/frontend/.gitignore index 5861baa5..58855fb2 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -2,3 +2,5 @@ node_modules/ .yalc yalc.lock + +stats.html diff --git a/frontend/package.json b/frontend/package.json index 35d98c65..de0fd5a2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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" }, diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 0f91050b..ad7bb42c 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -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'} @@ -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 diff --git a/frontend/rollup.config.js b/frontend/rollup.config.js index 2573b8a0..ac9ff58c 100644 --- a/frontend/rollup.config.js +++ b/frontend/rollup.config.js @@ -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); }, -}); \ No newline at end of file +}); diff --git a/frontend/src/components/DeckyState.tsx b/frontend/src/components/DeckyState.tsx index 67d4b78e..53ef6d2d 100644 --- a/frontend/src/components/DeckyState.tsx +++ b/frontend/src/components/DeckyState.tsx @@ -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 = ({ 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 ( { + const { hiddenPlugins } = useDeckyState(); const { plugins, updates, activePlugin, pluginOrder, setActivePlugin, closeActivePlugin } = useDeckyState(); const visible = useQuickAccessVisible(); + const { t } = useTranslation(); const [pluginList, setPluginList] = useState( plugins.sort((a, b) => pluginOrder.indexOf(a.name) - pluginOrder.indexOf(b.name)), @@ -48,6 +52,7 @@ const PluginView: VFC = () => { {pluginList .filter((p) => p.content) + .filter(({ name }) => !hiddenPlugins.includes(name)) .map(({ name, icon }) => ( setActivePlugin(name)}> @@ -59,6 +64,12 @@ const PluginView: VFC = () => { ))} + {hiddenPlugins.length > 0 && ( +
+ +
{t('PluginView.hidden', { count: hiddenPlugins.length })}
+
+ )}
diff --git a/frontend/src/components/modals/PluginUninstallModal.tsx b/frontend/src/components/modals/PluginUninstallModal.tsx new file mode 100644 index 00000000..e7ecbc99 --- /dev/null +++ b/frontend/src/components/modals/PluginUninstallModal.tsx @@ -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 = ({ name, title, buttonText, description, closeModal }) => { + return ( + { + 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} + + ); +}; + +export default PluginUninstallModal; diff --git a/frontend/src/components/settings/pages/plugin_list/PluginListLabel.tsx b/frontend/src/components/settings/pages/plugin_list/PluginListLabel.tsx new file mode 100644 index 00000000..a49f808f --- /dev/null +++ b/frontend/src/components/settings/pages/plugin_list/PluginListLabel.tsx @@ -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 = ({ name, hidden, version }) => { + const { t } = useTranslation(); + return ( +
+
{version ? `${name} - ${version}` : name}
+ {hidden && ( +
+ + {t('PluginListLabel.hidden')} +
+ )} +
+ ); +}; + +export default PluginListLabel; diff --git a/frontend/src/components/settings/pages/plugin_list/index.tsx b/frontend/src/components/settings/pages/plugin_list/index.tsx index fab8ec2b..09d06d48 100644 --- a/frontend/src/components/settings/pages/plugin_list/index.tsx +++ b/frontend/src/components/settings/pages/plugin_list/index.tsx @@ -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,10 +33,17 @@ async function reinstallPlugin(pluginName: string, currentVersion?: string) { } } -function PluginInteractables(props: { entry: ReorderableEntry }) { - const data = props.entry.data; +type PluginTableData = PluginData & { name: string; hidden: boolean; onHide(): void; onShow(): void }; + +function PluginInteractables(props: { entry: ReorderableEntry }) { 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( @@ -47,7 +51,7 @@ function PluginInteractables(props: { entry: ReorderableEntry }) { { try { - fetch(`http://127.0.0.1:1337/plugins/${pluginName}/reload`, { + fetch(`http://127.0.0.1:1337/plugins/${name}/reload`, { method: 'POST', credentials: 'include', headers: { @@ -58,7 +62,7 @@ function PluginInteractables(props: { entry: ReorderableEntry }) { console.error('Error Reloading Plugin Backend', err); } - window.DeckyPluginLoader.importPlugin(pluginName, data?.version); + window.DeckyPluginLoader.importPlugin(name, version); }} > {t('PluginListIndex.reload')} @@ -66,15 +70,20 @@ function PluginInteractables(props: { entry: ReorderableEntry }) { 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')} + {hidden ? ( + {t('PluginListIndex.show')} + ) : ( + {t('PluginListIndex.hide')} + )} , e.currentTarget ?? window, ); @@ -82,22 +91,22 @@ function PluginInteractables(props: { entry: ReorderableEntry }) { return ( <> - {data?.update ? ( + {update ? ( 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)} >
- {t('PluginListIndex.update_to', { name: data?.update?.name })} + {t('PluginListIndex.update_to', { name: update.name })}
) : ( reinstallPlugin(pluginName, data?.version)} - onOKButton={() => reinstallPlugin(pluginName, data?.version)} + onClick={() => reinstallPlugin(name, version)} + onOKButton={() => reinstallPlugin(name, version)} >
{t('PluginListIndex.reinstall')} @@ -130,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( 'pluginOrder', plugins.map((plugin) => plugin.name), @@ -141,22 +150,29 @@ export default function PluginList() { window.DeckyPluginLoader.checkPluginUpdates(); }, []); - const [pluginEntries, setPluginEntries] = useState[]>([]); + const [pluginEntries, setPluginEntries] = useState[]>([]); + 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: