From 7416c34d7c972d4c12e2a0ad73a2c1f45b119f5d Mon Sep 17 00:00:00 2001 From: flemming-it Date: Sat, 9 May 2026 19:32:13 +0200 Subject: [PATCH] feat(studio): contextual "Add registry token" CTA on install auth wall (v0.46.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hub's v0.10.91 install error message already tells the operator exactly what to do — set FAI_REGISTRY_TOKEN or save to ~/.fai/registry-token via Settings. But that text lives in a FaiErrorBox inside the install dialog. Reading the buried recovery instruction, finding Settings, scrolling to the Registry credentials panel, pasting, then re-clicking the install pill is five hops for what should be one. When at least one item in the install dialog fails with the "registry returned an HTML page" / "no registry token configured" signature, the dialog now surfaces a prominent filled "Add registry token" button next to Close. Click closes the install dialog and opens the Settings dialog directly so the operator pastes + saves without losing context. After Settings dismisses, the operator is back at the flow card and re-clicks the same pill to retry. Detection is signature-based (substring match on the hub's error text) so it activates whether the install was a single- pill click or an "Install all" sequence. Other failure modes (network down, signature invalid, sha mismatch) still show just the Close button — the CTA is specific to the auth-wall case it can actually unblock. Signed-off-by: flemming-it --- lib/l10n/app_de.arb | 1 + lib/l10n/app_en.arb | 1 + lib/l10n/app_localizations.dart | 6 ++++++ lib/l10n/app_localizations_de.dart | 3 +++ lib/l10n/app_localizations_en.dart | 3 +++ lib/pages/flows.dart | 28 ++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 7 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 5923bb9..bbe24f4 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -428,6 +428,7 @@ "failed": { "type": "int" } } }, + "flowsInstallAuthWallButton": "Registry-Token einfügen", "flowsOpenInEditorTooltip": "YAML-Datei im Standard-Editor öffnen", "flowsOpenInEditorFailed": "Konnte Datei nicht öffnen: {error}", "@flowsOpenInEditorFailed": { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9c566b7..3cc7081 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -429,6 +429,7 @@ "failed": { "type": "int" } } }, + "flowsInstallAuthWallButton": "Add registry token", "flowsOpenInEditorTooltip": "Open the YAML file in your default editor", "flowsOpenInEditorFailed": "Could not open file: {error}", "@flowsOpenInEditorFailed": { diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index d22a2a0..3c9c664 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1958,6 +1958,12 @@ abstract class AppLocalizations { /// **'{ok} installed, {failed} failed.'** String flowsInstallDepsAnyFailed(int ok, int failed); + /// No description provided for @flowsInstallAuthWallButton. + /// + /// In en, this message translates to: + /// **'Add registry token'** + String get flowsInstallAuthWallButton; + /// No description provided for @flowsOpenInEditorTooltip. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 569cfce..b60a03f 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1109,6 +1109,9 @@ class AppLocalizationsDe extends AppLocalizations { return '$ok installiert, $failed fehlgeschlagen.'; } + @override + String get flowsInstallAuthWallButton => 'Registry-Token einfügen'; + @override String get flowsOpenInEditorTooltip => 'YAML-Datei im Standard-Editor öffnen'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index ab8a19d..612165f 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1122,6 +1122,9 @@ class AppLocalizationsEn extends AppLocalizations { return '$ok installed, $failed failed.'; } + @override + String get flowsInstallAuthWallButton => 'Add registry token'; + @override String get flowsOpenInEditorTooltip => 'Open the YAML file in your default editor'; diff --git a/lib/pages/flows.dart b/lib/pages/flows.dart index 7dbe938..34829fc 100644 --- a/lib/pages/flows.dart +++ b/lib/pages/flows.dart @@ -940,6 +940,22 @@ class _InstallDependenciesDialogState int get _failedCount => _items.where((i) => i.status == _InstallStatus.failed).length; + /// True iff at least one failure carries the v0.10.91+ HTML-instead- + /// of-bundle signature emitted by `download_to_temp` when the + /// registry redirects an unauthenticated request to a signin page. + /// Used to surface a contextual "Open Settings" button so the + /// operator does not have to read the wall of error text first. + bool get _anyAuthWallFailure => _items.any( + (i) => + i.status == _InstallStatus.failed && _isAuthWallError(i.error), + ); + + static bool _isAuthWallError(String? msg) { + if (msg == null) return false; + return msg.contains('registry returned an HTML page') || + msg.contains('no registry token configured'); + } + @override Widget build(BuildContext context) { final theme = Theme.of(context); @@ -986,6 +1002,18 @@ class _InstallDependenciesDialogState ), ), actions: [ + if (!_running && _anyAuthWallFailure) + FilledButton.icon( + icon: const Icon(Icons.vpn_key_outlined, size: 16), + label: Text(l.flowsInstallAuthWallButton), + onPressed: () async { + // Close install dialog first so the operator returns + // to the flow card on Settings dismiss — then they + // can re-click the pill to retry the install. + Navigator.pop(context, _anySucceeded); + await FaiSettingsDialog.show(context); + }, + ), TextButton( onPressed: _running ? null diff --git a/pubspec.yaml b/pubspec.yaml index bebe178..ceb302e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: fai_studio description: "F∆I Studio — desktop GUI for the F∆I hub" publish_to: 'none' -version: 0.45.0 +version: 0.46.0 environment: sdk: ^3.11.0-200.1.beta