From aff9b398dad85077b3532e5bed23c13119c19aa2 Mon Sep 17 00:00:00 2001 From: librelad Date: Sat, 29 Aug 2026 08:08:47 +0100 Subject: [PATCH] Backup step: Read sits beside the password, like Check beside the folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Read this backup" was a button floating below the form, which reads as a step of its own rather than as the action for the field above it. It is now inline in the password row, styled exactly like the folder's Check — each field carries its own action on the right. Shortened to "Read" so it fits the row without resizing it mid-request, and the status line moved inside that field for the same reason: it is an answer about the password, not about the step. The gate message names the button it is pointing at, so renaming one meant renaming the other. That is the kind of drift nothing catches — the message is only reachable by trying to advance without reading first. Co-Authored-By: Claude Opus 5 --- .../frontend/core/setup/js/setup-wizard.js | 13 +++++-------- scripts/dev/lp-restore-wizard-test | 11 +++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/containers/libreportal/frontend/core/setup/js/setup-wizard.js b/containers/libreportal/frontend/core/setup/js/setup-wizard.js index a5533d4..9baf39a 100755 --- a/containers/libreportal/frontend/core/setup/js/setup-wizard.js +++ b/containers/libreportal/frontend/core/setup/js/setup-wizard.js @@ -457,14 +457,11 @@ class SetupWizard {
- + +
+
- -
- -
-
@@ -1508,7 +1505,7 @@ class SetupWizard { status.innerHTML = `

${this.escapeHtml(e.message || String(e))}

`; } } finally { - if (btn) { btn.disabled = false; btn.textContent = 'Read this backup'; } + if (btn) { btn.disabled = false; btn.textContent = 'Read'; } } } @@ -2377,7 +2374,7 @@ class SetupWizard { const problem = this._restoreSourceProblem(); if (problem) return problem; if (!this.restoreInfo) { - return 'Select "Read this backup" first \u2014 we have to open it before we can show you what is inside.'; + return 'Select \u201cRead\u201d beside the password first \u2014 we have to open the backup before we can show you what is inside.'; } } diff --git a/scripts/dev/lp-restore-wizard-test b/scripts/dev/lp-restore-wizard-test index c6847b8..5c5380b 100755 --- a/scripts/dev/lp-restore-wizard-test +++ b/scripts/dev/lp-restore-wizard-test @@ -198,6 +198,14 @@ read -r -d '' DRIVE <<'JS' // one, and kept telling them after they had. const BACKUP = w.stepNames.indexOf('Backup'); out.passwordLabel = ($('label[for="sw-rs-pass"]') || {}).textContent?.trim().split('\n')[0].trim(); + // The action sits beside the field it acts on, the same way Check does for + // the folder. A button floating below the form reads as a step of its own. + const readBtn = $('#sw-rs-read'); + const readRow = readBtn && readBtn.closest('.setup-input-row'); + out.readSitsInThePasswordRow = !!(readRow && readRow.querySelector('#sw-rs-pass')); + out.readMatchesCheckStyling = !!(readBtn && $('#sw-rs-verify') + && readBtn.className === $('#sw-rs-verify').className); + out.readIsShortEnoughToSitInline = !!readBtn && readBtn.textContent.trim().length <= 8; out.verdictHasNoInstruction = !/enter its password/i.test(($('#sw-rs-verify-result') || {}).textContent || ''); @@ -360,6 +368,9 @@ chk "and the real path is offered" "$(g .offersTheRealPath)" true echo "the step gates rather than instructs" chk "the field is called Backup Password" "$(g .passwordLabel)" "Backup Password" +chk "Read sits beside the password" "$(g .readSitsInThePasswordRow)" true +chk "styled like the folder's Check" "$(g .readMatchesCheckStyling)" true +chk "and short enough to sit inline" "$(g .readIsShortEnoughToSitInline)" true chk "no 'enter its password' copy" "$(g .verdictHasNoInstruction)" true chk "blocked without a password" "$(g .blockedWithoutPassword)" true chk "blocked until the backup is read" "$(g .blockedUntilRead)" true