From f736ec650164279c4bde82ee3624f5afad3c0b7e Mon Sep 17 00:00:00 2001 From: librelad Date: Sat, 29 Aug 2026 08:25:14 +0100 Subject: [PATCH] =?UTF-8?q?Drop=20the=20section=20headings=20=E2=80=94=20t?= =?UTF-8?q?he=20step=20name=20is=20already=20at=20the=20top?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every step's name is in the progress bar. Repeating it as a heading directly beneath was the same word twice with nothing between them: "Step 4 of 8 — Storage" followed immediately by "STORAGE". Eight headings across both branches, all gone. Three of them carried a tooltip worth keeping — what a storage location is, that a backup password cannot be recovered, what a .lpapp is. Those moved to a tooltip beside the step name in the progress bar, which is where the heading actually is now. Steps that never had an explanation show no marker. Also removed the Backup step's "Where is it? Backups live in a repository…" paragraph. The step is called Backup, the first field asks where it lives, and the repository-not-a-file point is what the step tooltip is for. lp-restore-wizard-test asserted the old heading matched the step name; it now asserts there are no headings left anywhere and that the progress bar carries both the name and the explanation. Both browser tests reported "wizard handle missing" when the wizard simply was not on screen — it only renders while setup is incomplete, and a stray .setup_complete from an earlier run hides it. They now say which of the two it is, since the first sends you looking for a JS error that is not there. Co-Authored-By: Claude Opus 5 --- .../frontend/core/setup/js/setup-wizard.js | 37 ++++++++----------- scripts/dev/lp-restore-wizard-test | 30 ++++++++++++--- scripts/dev/lp-storage-step-test | 11 +++++- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/containers/libreportal/frontend/core/setup/js/setup-wizard.js b/containers/libreportal/frontend/core/setup/js/setup-wizard.js index 9baf39a..8e44419 100755 --- a/containers/libreportal/frontend/core/setup/js/setup-wizard.js +++ b/containers/libreportal/frontend/core/setup/js/setup-wizard.js @@ -28,6 +28,18 @@ class SetupWizard { // must not be asked for an install name, domains or an app list — the // backup already answers all three, and asking would invite someone to // contradict what is about to be restored over their answer. + // Tips that used to hang off a section heading. The heading itself is the + // progress bar's step name — repeating it as a title directly beneath was + // the same word twice with nothing between them — so the explanation moves + // to where the name actually is. + this.stepTips = { + Storage: 'Apps normally live on the system disk. If you have another drive, you can register it here and choose per app where its data goes.', + Backups: 'Snapshots of your apps and settings, taken on a schedule. Encrypted, so keep the password somewhere other than this machine \u2014 without it a backup cannot be opened, not even by us.', + Import: "Bring apps over from another LibrePortal using .lpapp files made with 'libreportal app export'. Point at a file or a folder of them, somewhere on this machine.", + Backup: 'Backups live in a repository \u2014 a folder on a disk, or a remote server. Not a single file, so point us at the repository itself.', + Contents: 'What this backup holds, and what it would mean on this machine.', + Rebuild: 'What is about to happen, and in what order.' + }; this.stepNames = ['Start', 'Experience', 'Identity', 'Domains', 'Storage', 'Backups', 'Import', 'Recommended', 'Metrics', 'Backup', 'Contents', 'Rebuild']; this.stepIcons = ['\u{1F9ED}', '\u{1F331}', '\u{1FA90}', '\u{1F6F0}\u{FE0F}', '\u{1F4BE}', '\u{1F6DF}', '\u{1F4E6}', '\u{1F6E1}\u{FE0F}', '\u{1F4CA}', @@ -358,9 +370,6 @@ class SetupWizard { otherwise there is exactly one answer and the step is skipped. -->
-
Storage - ? -
@@ -374,9 +383,6 @@ class SetupWizard { this asks rather than waiting to be found in the Backup page. -->
-
Backups - ? -
Destinations
@@ -390,9 +396,6 @@ class SetupWizard { browser (unlike a backup repository — see first-run-restore.md). -->
-
Import - ? -
Folder or file @@ -408,7 +411,6 @@ class SetupWizard {
-
Recommended Apps

Pre-selected to give you a working install out of the box.

@@ -432,7 +434,6 @@ class SetupWizard { is skipped when the user chose Beginner on step 1. -->
-
Metrics Apps

Optional. Install these to enable per-app "Export metrics to Grafana" later.

@@ -443,11 +444,6 @@ class SetupWizard {
-
Backup
-

- Where is it? Backups live in a repository \u2014 a folder on a - disk, or a remote server. Not a single file. -

@@ -468,8 +464,6 @@ class SetupWizard {
-
Contents
-

What this backup would bring back.

Go back a step and read the backup first.

@@ -479,8 +473,6 @@ class SetupWizard {
-
Rebuild
-

What is about to happen, and in what order.

@@ -2327,7 +2319,10 @@ class SetupWizard { const icon = this.stepIcons[actual]; this.container.querySelector('#sw-progress-fill').style.width = `${pct}%`; this.container.querySelector('#sw-progress-step').innerHTML = - `Step ${this.currentStep + 1} of ${this.totalSteps} ${icon} ${name}`; + `Step ${this.currentStep + 1} of ${this.totalSteps} ${icon} ${name}` + + (this.stepTips[name] + ? ` ?` + : ''); this.container.querySelector('#sw-progress-pct').textContent = `${pct}%`; this.container.querySelector('#sw-back').disabled = this.currentStep === 0; diff --git a/scripts/dev/lp-restore-wizard-test b/scripts/dev/lp-restore-wizard-test index 5c5380b..00205ce 100755 --- a/scripts/dev/lp-restore-wizard-test +++ b/scripts/dev/lp-restore-wizard-test @@ -32,7 +32,16 @@ lp_reachable() { read -r -d '' DRIVE <<'JS' const out = {}; const w = window.setupWizard; - if (!w) return JSON.stringify({ error: 'wizard handle missing' }); + if (!w) { + // Distinguish "the wizard is broken" from "the wizard is not on screen". + // It only renders while setup is incomplete, so a stray .setup_complete — + // which a previous test run can leave behind — hides it entirely, and + // "wizard handle missing" sends you looking for a JS error that is not + // there. + return JSON.stringify({ error: typeof window.SetupWizard === 'function' + ? 'the wizard did not open — setup is already marked complete on this install (remove frontend/data/.setup_complete)' + : 'wizard handle missing' }); + } const fire = (el, ev) => el.dispatchEvent(new Event(ev, { bubbles: true })); const $ = s => document.querySelector(s); const visible = () => w.stepNames.filter((n, i) => w._stepVisible(i)); @@ -56,10 +65,17 @@ read -r -d '' DRIVE <<'JS' out.startAlwaysShown = out.newSteps[0] === 'Start' && out.restoreSteps[0] === 'Start'; // The source form offers every backend, and shows only the chosen one. - // The progress bar said "Source" while the heading said "Where is your - // backup?", which read as two different steps. They have to agree. - const sec = document.querySelector('.setup-step[data-step="9"] .setup-section-title'); - out.titleMatchesStepName = !!sec && sec.textContent.trim() === 'Backup'; + // The step's name lives in the progress bar. Repeating it as a heading + // directly beneath was the same word twice with nothing between them, so + // there are no section headings left in the wizard at all — and what those + // headings explained moved to a tooltip beside the name, which is where the + // name is. + out.noSectionHeadingsAnywhere = document.querySelectorAll('.setup-section-title').length; + w.showStep(1); + const bar = document.querySelector('#sw-progress-step'); + out.progressShowsTheStepName = + (bar.querySelector('.setup-progress-name') || {}).textContent === 'Backup'; + out.progressCarriesTheTip = !!bar.querySelector('.setup-tooltip'); // Fields laid out like the rest of the wizard: label with a tooltip, and an // icon beside the input — not the label-left rows the Storage step uses. out.fieldsHaveIcons = document.querySelectorAll('#sw-rs-fields .setup-field-icon').length > 0; @@ -320,7 +336,9 @@ chk "restore shows no install steps" "$(g .restoreLeaksNewStep)" false chk "new shows no restore steps" "$(g .newLeaksRestoreStep)" false chk "restore step set" "$(g '.restoreSteps | join(",")')" "Start,Backup,Contents,Rebuild" -chk "step name matches its title" "$(g .titleMatchesStepName)" true +chk "no section headings anywhere" "$(g .noSectionHeadingsAnywhere)" 0 +chk "the progress bar names the step" "$(g .progressShowsTheStepName)" true +chk "and carries its explanation" "$(g .progressCarriesTheTip)" true echo "the backup source form" chk "every backend offered" "$(g '.kinds | join(",")')" "local,sftp,rest,s3,b2" diff --git a/scripts/dev/lp-storage-step-test b/scripts/dev/lp-storage-step-test index ee39fb1..e1f258c 100755 --- a/scripts/dev/lp-storage-step-test +++ b/scripts/dev/lp-storage-step-test @@ -43,7 +43,16 @@ lp_reachable() { read -r -d '' DRIVE <<'JS' const out = {}; const w = window.setupWizard; - if (!w) return JSON.stringify({ error: 'wizard handle missing' }); + if (!w) { + // Distinguish "the wizard is broken" from "the wizard is not on screen". + // It only renders while setup is incomplete, so a stray .setup_complete — + // which a previous test run can leave behind — hides it entirely, and + // "wizard handle missing" sends you looking for a JS error that is not + // there. + return JSON.stringify({ error: typeof window.SetupWizard === 'function' + ? 'the wizard did not open — setup is already marked complete on this install (remove frontend/data/.setup_complete)' + : 'wizard handle missing' }); + } const fire = (el, ev) => el.dispatchEvent(new Event(ev, { bubbles: true })); // Looked up by name, never hardcoded. Inserting the Start step moved Storage // from 3 to 4, and a test pinned to the old number silently began validating