feat(welcome): SilverOS Welcome first-logon wizard (flavour engine + apply orchestrator + MAUI UI + image bake) #4

Merged
SilverLABS merged 28 commits from feat/welcome-app into main 2026-06-09 10:31:35 +00:00
3 changed files with 20 additions and 12 deletions
Showing only changes of commit a393ded7c6 - Show all commits

View File

@@ -23,7 +23,11 @@
}
else if (_error is not null)
{
<p class="error">@_error</p>
<div class="error-panel">
<p class="error">Couldn't load device profiles. Please reinstall SilverOS.</p>
<p class="error-detail"><small>@_error</small></p>
<button class="btn-secondary" @onclick="LoadFlavours">Retry</button>
</div>
}
else
{
@@ -83,12 +87,17 @@
private bool CanGoNext => _currentStep switch
{
1 => State.Flavour is not null,
2 => _accountStep?.IsValid ?? false,
_ => true
};
protected override Task OnInitializedAsync()
protected override Task OnInitializedAsync() => LoadFlavours();
private Task LoadFlavours()
{
_error = null;
_loading = true;
try
{
_flavours = FlavourLoader.Load(FlavoursDir);

View File

@@ -9,7 +9,7 @@
<input id="username" type="text" placeholder="e.g. alice"
value="@State.Username"
@oninput="OnUsernameInput" />
@if (_errors.TryGetValue("username", out var ue))
@if (_touched.Contains("username") && _errors.TryGetValue("username", out var ue))
{
<span class="field-error">@ue</span>
}
@@ -20,7 +20,7 @@
<input id="password" type="password"
value="@State.Password"
@oninput="OnPasswordInput" />
@if (_errors.TryGetValue("password", out var pe))
@if (_touched.Contains("password") && _errors.TryGetValue("password", out var pe))
{
<span class="field-error">@pe</span>
}
@@ -31,7 +31,7 @@
<input id="adminpassword" type="password"
value="@State.AdminPassword"
@oninput="OnAdminPasswordInput" />
@if (_errors.TryGetValue("adminpassword", out var ae))
@if (_touched.Contains("adminpassword") && _errors.TryGetValue("adminpassword", out var ae))
{
<span class="field-error">@ae</span>
}
@@ -42,7 +42,7 @@
<input id="bitlockerpin" type="password" inputmode="numeric" pattern="[0-9]*"
value="@State.BitLockerPin"
@oninput="OnPinInput" />
@if (_errors.TryGetValue("bitlockerpin", out var be))
@if (_touched.Contains("bitlockerpin") && _errors.TryGetValue("bitlockerpin", out var be))
{
<span class="field-error">@be</span>
}
@@ -51,16 +51,17 @@
@code {
private readonly Dictionary<string, string> _errors = new();
private readonly HashSet<string> _touched = new();
/// <summary>True when all fields are valid. Used by the wizard host to gate Next.</summary>
public bool IsValid { get; private set; }
protected override void OnInitialized() => Validate();
private void OnUsernameInput(ChangeEventArgs e) { State.Username = e.Value?.ToString() ?? ""; Validate(); }
private void OnPasswordInput(ChangeEventArgs e) { State.Password = e.Value?.ToString() ?? ""; Validate(); }
private void OnAdminPasswordInput(ChangeEventArgs e) { State.AdminPassword = e.Value?.ToString() ?? ""; Validate(); }
private void OnPinInput(ChangeEventArgs e) { State.BitLockerPin = e.Value?.ToString() ?? ""; Validate(); }
private void OnUsernameInput(ChangeEventArgs e) { State.Username = e.Value?.ToString() ?? ""; _touched.Add("username"); Validate(); }
private void OnPasswordInput(ChangeEventArgs e) { State.Password = e.Value?.ToString() ?? ""; _touched.Add("password"); Validate(); }
private void OnAdminPasswordInput(ChangeEventArgs e) { State.AdminPassword = e.Value?.ToString() ?? ""; _touched.Add("adminpassword"); Validate(); }
private void OnPinInput(ChangeEventArgs e) { State.BitLockerPin = e.Value?.ToString() ?? ""; _touched.Add("bitlockerpin"); Validate(); }
void Validate()
{

View File

@@ -1,5 +1,3 @@
@inject WizardState State
<div class="step welcome-step">
<div class="welcome-hero">
<h1>Welcome to SilverOS</h1>