fix(setup): wizard errors copyable above the dialog, CLI skew explained, exec transparency
Some checks failed
Security / Security check (push) Failing after 2s
Some checks failed
Security / Security check (push) Failing after 2s
Field test of the setup wizard surfaced three trust breaks in one run: an unexplained macOS Documents permission prompt, a perceived crash, and an error message whose copy button could not be reached. Root causes and fixes: - chain init failures were shown as a SnackBar, which lands BEHIND the wizard's modal barrier: dimmed, clipped, copy unreachable — and the click aimed at it hit the barrier, dismissing the whole wizard with all answers (the perceived crash). Errors now open a modal dialog ABOVE the wizard via showChainErrorDialog with a copyable detail block, and the wizard is no longer barrier-dismissible. - When the resolved chain binary is older than Studio and rejects --plan-json, the wizard now explains the version skew in plain language (binary path + update path) instead of leaking a raw clap usage error. A missing binary gets its own localized story. - Step 3 announces which chain binary the preview will execute; when that binary physically lives (symlinks resolved) in a TCC-protected folder, the wizard pre-explains the macOS folder prompt. Supporting changes: FriendlyError passes through friendlyError() unchanged so call sites can ship precise localized stories through the shared presentation; SystemActions gains resolvedChainBinary() plus run/resolve test seams; ChainErrorBox hugs its content instead of filling an unbounded dialog; the wizard's answers file is written synchronously (the async dart:io variants never complete under the widget-test fake-async zone). Verified: flutter analyze clean, 53 tests green (6 new wizard error- path tests incl. clipboard round-trip), plus a live GUI walk on macOS in dark + light with a stale binary (skew dialog, copy verified via clipboard) and with the real binary (TCC pre-explanation with the resolved path, full plan preview). Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
4ceb5bb567
commit
c6da5025ce
11 changed files with 524 additions and 10 deletions
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Sentinel returned in `_runFai(...).stderr` when no `fai`
|
||||
|
|
@ -73,6 +74,28 @@ class SystemActions {
|
|||
/// binary / read the install guide" recovery path.
|
||||
static bool chainBinaryExists() => _faiExecutable() != null;
|
||||
|
||||
/// Test seam: when set, [_runFai] returns this function's result
|
||||
/// instead of spawning a real process, and [resolvedChainBinary] /
|
||||
/// [chainBinaryExists] answer from [debugResolveOverride]. Lets
|
||||
/// widget tests drive the CLI error paths deterministically.
|
||||
@visibleForTesting
|
||||
static Future<({bool ok, String stdout, String stderr})> Function(
|
||||
List<String> args,
|
||||
)?
|
||||
debugRunFaiOverride;
|
||||
|
||||
/// Test seam companion to [debugRunFaiOverride]: overrides binary
|
||||
/// resolution (may return null to simulate "no binary found").
|
||||
@visibleForTesting
|
||||
static String? Function()? debugResolveOverride;
|
||||
|
||||
/// Absolute path of the `chain` binary Studio would execute right
|
||||
/// now, or null when none can be located. Public so surfaces that
|
||||
/// are about to spawn the binary (the guided-setup wizard) can say
|
||||
/// WHAT they will run — before macOS asks the operator for folder
|
||||
/// permission because of where that binary happens to live.
|
||||
static String? resolvedChainBinary() => _faiExecutable();
|
||||
|
||||
/// Ask the OS to open [path] in the default handler. On macOS
|
||||
/// this opens text files in TextEdit, configs in the registered
|
||||
/// editor, etc. Returns true on a clean spawn (process exited
|
||||
|
|
@ -219,6 +242,8 @@ class SystemActions {
|
|||
static Future<({bool ok, String stdout, String stderr})> _runFai(
|
||||
List<String> args,
|
||||
) async {
|
||||
final runOverride = debugRunFaiOverride;
|
||||
if (runOverride != null) return runOverride(args);
|
||||
final exe = _faiExecutable();
|
||||
if (exe == null) {
|
||||
// Sentinel, not a user-facing string. Callers detect this
|
||||
|
|
@ -249,6 +274,8 @@ class SystemActions {
|
|||
/// the file picker), $CHAIN_BIN, PATH, fallback to the canonical
|
||||
/// install location under the user's home dir.
|
||||
static String? _faiExecutable() {
|
||||
final resolveOverride = debugResolveOverride;
|
||||
if (resolveOverride != null) return resolveOverride();
|
||||
final override = _faiBinaryOverride;
|
||||
if (override != null &&
|
||||
override.isNotEmpty &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue