From 63b3af4cfc809e7a52237c899d1c893adc6fe0eb Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 19 Aug 2026 02:38:16 +0100 Subject: [PATCH] fix(tools): keep the Tools tab live while a tool runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Locking every tab but Tasks is right for install/restart/backup: those jump to the task log, so Tasks is the one tab you need. A tool run deliberately stays where it was launched and brings its result back to Tools — greying Tools out stranded the user on a tab they could no longer return to. disableTabs() now takes the tab to leave alone, chosen per task type by keepTabFor(). Same rule on the page-load path, which also stops yanking a reload mid-tool-run over to the task log. --- .../apps/core/js/app-tabbed-manager.js | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/containers/libreportal/frontend/components/apps/core/js/app-tabbed-manager.js b/containers/libreportal/frontend/components/apps/core/js/app-tabbed-manager.js index e24d83a..41c4fd5 100755 --- a/containers/libreportal/frontend/components/apps/core/js/app-tabbed-manager.js +++ b/containers/libreportal/frontend/components/apps/core/js/app-tabbed-manager.js @@ -788,8 +788,12 @@ class AppTabbedManager { if (this.currentApp) { const running = this.getRunningTaskForApp(this.currentApp); if (running) { - this.switchTab('tasks'); - this.disableTabs(); + // A tool run is seconds long and surfaces its own result where it was + // launched, so a reload mid-run stays on Tools rather than being thrown + // at the task log like a long install would be. + const keep = this.keepTabFor(running.action); + if (!keep) this.switchTab('tasks'); + this.disableTabs(keep); this.activeTaskId = running.taskId; } } @@ -1012,9 +1016,22 @@ class AppTabbedManager { return 'unknown'; } - // Disable config, services and backup tabs when task is running - disableTabs() { + // Which tab a running task of this type leaves usable, if any. Single source + // of truth for both the live path (disableAppButtons) and the page-load path. + keepTabFor(action) { + return action === 'tool' ? 'tools' : null; + } + + // Disable config, services and backup tabs when task is running. + // + // `keep` names one tab to leave alone — the tab the user is expected to stay + // on for this kind of task. Install/restart/backup and friends jump to Tasks, + // so leaving only Tasks live is right for them; a tool run stays put on Tools + // and brings its result back there, so locking Tools would strand the user on + // a tab they can't return to. + disableTabs(keep = null) { const tabs = ['config', 'services', 'tools', 'backups', 'updater'] + .filter(name => name !== keep) .map(name => document.querySelector(`.main-tab-button[data-tab="${name}"], .tab-button[data-tab="${name}"]`)) .filter(Boolean); @@ -1045,8 +1062,9 @@ class AppTabbedManager { // Disable app buttons during task execution disableAppButtons(appName, action) { - // Also disable config and backup tabs - this.disableTabs(); + // Also disable config and backup tabs — except the one this task reports + // back into (Tools, for a tool run). + this.disableTabs(this.keepTabFor(action)); // Find ALL action buttons in the app content section (config, backup, etc.) // This includes install, uninstall, update, backup, and any other action buttons