@using SilverOS.Welcome.Core.Apps @using SilverOS.Welcome.Core.Preconfig @inject IApplyService ApplyService @inject IAppCatalog AppCatalog @inject IPreconfigStore PreconfigStore @inject WizardState State

Applying Configuration

Your SilverOS is being configured. This may take a few minutes.

@if (_errorMessage is not null) {

Configuration failed

@SanitiseForDisplay(_errorMessage)

} else if (!_running && !_complete) {

Ready to apply your selections. Click Start to begin.

} else {
@if (!_complete) { }
@_stageLabel
@(_percent)%
@if (!_complete) {

Installing your apps and applying security hardening — this can take several minutes. Please leave the device powered on.

}
@if (_complete) {

Configuration complete.

} }
@code { [Parameter] public EventCallback OnComplete { get; set; } [Parameter] public EventCallback OnRunningChanged { get; set; } [Parameter] public bool AutoStart { get; set; } private bool _running; private bool _autoStarted; private bool _complete; private int _percent; private string _stageLabel = "Preparing…"; private string? _errorMessage; private const int ErrorDisplayMaxLength = 200; /// /// Strips newlines and caps length so a multi-line or huge error message /// cannot dump raw output into the UI. /// private static string SanitiseForDisplay(string message) { var single = message.ReplaceLineEndings(" ").Trim(); return single.Length <= ErrorDisplayMaxLength ? single : single[..ErrorDisplayMaxLength] + "…"; } protected override async Task OnAfterRenderAsync(bool firstRender) { // First-run auto-apply: when the host jumps straight to this step with AutoStart, // kick off the same apply the Start button would, exactly once. The manual path // (AutoStart=false) is untouched. if (firstRender && AutoStart && !_autoStarted) { _autoStarted = true; await StartAsync(); } } public async Task StartAsync() { // Re-entrancy guard: prevent a second overlapping apply if already running // (e.g. rapid double-click on Retry). if (_running) return; _running = true; _complete = false; _errorMessage = null; _percent = 0; _stageLabel = "Preparing…"; StateHasChanged(); await OnRunningChanged.InvokeAsync(true); var apps = AppCatalog.Load(Path.Combine(AppContext.BaseDirectory, "apps")) .All.Where(a => State.SelectedApps.Contains(a.Id)).ToList(); // D1: Apply is now apps+bitlocker only (account via Setup, hardening via SetupComplete). // D2 owns the full UI rewire (run-mode / preseed); this passes the 3-arg request from // existing State fields so the app keeps compiling. var req = new ApplyRequest( Flavour: State.Flavour!, BitLockerPin: State.BitLockerPin, Apps: apps); var progress = new Progress(p => { _ = InvokeAsync(() => { _percent = p.Percent; _stageLabel = p.Stage; StateHasChanged(); }); }); try { await ApplyService.RunAsync(req, progress); _complete = true; _running = false; _percent = 100; // Apply succeeded: wipe the BitLocker pin from the preconfig and stamp the // configured marker so the next launch opens toolbox-home instead of re-applying. PreconfigStore.ClearPin(); PreconfigStore.MarkConfigured(); StateHasChanged(); await OnRunningChanged.InvokeAsync(false); await OnComplete.InvokeAsync(); } catch (Exception ex) { _running = false; _errorMessage = ex.Message; StateHasChanged(); await OnRunningChanged.InvokeAsync(false); } } }