src/Engine/Startup.ps1
|
# Startup.ps1 — engine half of the startup loader: the deferred background # job and startup-job state. The exported entry point is # src/Public/Invoke-PSMMStartup.ps1. function Get-PSMMStartupJob { $script:PSMM_StartupJob } function Get-PSMMStartupJobTotal { $script:PSMM_JobTotal } # v2 startup report (design-system-v2 §8): the same design tokens as the TUI, # rendered with raw 256-colour escapes because Spectre is NOT loaded at # profile time. Rows: @{ Kind = 'ok'|'skip'|'fail'|'defer'; Name; Ms; Note; # Count (defer only) }. Returns the finished lines; the caller prints them. function Get-PSMMStartupReportLines { [CmdletBinding()] param( [array]$Rows = @(), [int]$TotalMs = 0 ) $reset = Get-PSMMAnsiReset $c = @{} foreach ($t in 'key', 'ok', 'warn', 'err', 'info', 'mute', 'dim') { $c[$t] = Get-PSMMAnsi -Token $t } $mid = [char]0x00B7 $lines = [System.Collections.Generic.List[string]]::new() $loaded = @($Rows | Where-Object Kind -EQ 'ok').Count $skipped = @($Rows | Where-Object Kind -EQ 'skip').Count $failed = @($Rows | Where-Object Kind -EQ 'fail').Count $bg = 0 foreach ($r in @($Rows | Where-Object Kind -EQ 'defer')) { $bg += [Math]::Max(1, [int]$r.Count) } $parts = @() if ($loaded) { $parts += "$loaded loaded" } if ($skipped) { $parts += "$skipped skipped" } if ($failed) { $parts += "$failed failed" } if ($bg) { $parts += "$bg in background" } $parts += "$TotalMs ms" $brand = "$(Get-PSMMAnsi -Token 'brandfg')$(Get-PSMMAnsi -Token 'brandbg' -Background) psmm $reset" $lines.Add("$brand $($c.mute)$($parts -join " $mid ")$reset") if (-not $Rows.Count) { return $lines } $nameW = 4 foreach ($r in $Rows) { $l = [Math]::Min(34, "$($r.Name)".Length); if ($l -gt $nameW) { $nameW = $l } } $maxMs = 1 foreach ($r in $Rows) { if ([int]$r.Ms -gt $maxMs) { $maxMs = [int]$r.Ms } } $okRows = @($Rows | Where-Object { $_.Kind -eq 'ok' -and [int]$_.Ms -gt 0 }) $slowest = if ($okRows.Count -gt 1) { ($okRows | Sort-Object { [int]$_.Ms } -Descending)[0] } else { $null } $anyFail = $false foreach ($r in $Rows) { $nameTxt = "$($r.Name)" if ($nameTxt.Length -gt 34) { $nameTxt = $nameTxt.Substring(0, 33) + [char]0x2026 } $name = $nameTxt.PadRight($nameW) switch ($r.Kind) { 'ok' { $ms = ("{0} ms" -f [int]$r.Ms).PadLeft(8) # proportional bar in eighth-blocks, 10 cells max $units = [double]$r.Ms / $maxMs * 10 $full = [Math]::Floor($units) $frac = [Math]::Round(($units - $full) * 8) $bar = ([string][char]0x2588) * $full if ($frac -gt 0) { $bar += [char][int](0x2590 - $frac) } if (-not $bar) { $bar = [char]0x258F } if ($slowest -and $r -eq $slowest) { $note = "$($c.dim)slowest $([char]0x2014) InstallOnly would free your prompt$reset" $lines.Add("$($c.ok)$([char]0x25CF)$reset $name $($c.mute)$ms$reset $($c.warn)$bar$reset $note") } else { $extra = if ($r.Note) { " $($c.dim)$($r.Note)$reset" } else { '' } $lines.Add("$($c.ok)$([char]0x25CF)$reset $name $($c.mute)$ms$reset $($c.dim)$bar$reset$extra") } } 'skip' { $ms = "$([char]0x2014)".PadLeft(8) $lines.Add("$($c.dim)$([char]0x25CB)$reset $($c.dim)$name$reset $($c.dim)$ms$reset $($c.dim)$($r.Note)$reset") } 'fail' { $anyFail = $true $ms = "$([char]0x2014)".PadLeft(8) $lines.Add("$($c.err)$([char]0x2715)$reset $name $($c.dim)$ms$reset $($c.err)$($r.Note)$reset") } 'defer' { $ms = 'bg'.PadLeft(8) $lines.Add("$($c.info)$([char]0x22EF)$reset $($c.mute)$name$reset $($c.dim)$ms$reset $($c.dim)$($r.Note)$reset") } } } if ($anyFail) { $lines.Add(" $($c.dim)$([char]0x2192) psmm, then $reset$($c.key)i$reset$($c.dim) on the row retries$reset") } $lines } # Start the background job that handles Mode=InstallOnly entries. # The job re-implements the minimal install logic because module functions # are not visible inside a ThreadJob's session. function Start-PSMMDeferredJob { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseUsingScopeModifierInNewRunspaces', '', Justification = '$mods is a scriptblock param supplied via -ArgumentList; $m is its foreach variable. The rule cannot see param bindings.')] [CmdletBinding()] param([Parameter(Mandatory)] $Entries) $payload = @($Entries | ForEach-Object { [pscustomobject]@{ Name = $_.Name; Install = $_.Install; Version = $_.Version } }) $script:PSMM_JobTotal = $payload.Count $script:PSMM_StartupJob = Start-ThreadJob -Name 'PSMM-Startup' -ScriptBlock { param($mods) foreach ($m in $mods) { # one output line per module so the UI can show progress + summary try { $have = Get-Module -ListAvailable -Name $m.Name $psrg = [bool](Get-Command Install-PSResource -ErrorAction SilentlyContinue) $installLatest = { if ($psrg) { if ($m.Version) { Install-PSResource -Name $m.Name -Version $m.Version -Scope CurrentUser -TrustRepository -ErrorAction Stop } else { Install-PSResource -Name $m.Name -Scope CurrentUser -TrustRepository -ErrorAction Stop } } else { Install-Module -Name $m.Name -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop } } switch ($m.Install) { 'CheckOnly' { if (-not $have) { "FAILED $($m.Name): not installed (check-only)" } else { "ok $($m.Name)" } } 'IfMissing' { if (-not $have) { & $installLatest; "installed $($m.Name)" } else { "ok $($m.Name)" } } 'Latest' { if ($m.Version) { & $installLatest } elseif ($have -and (Get-Command Update-PSResource -ErrorAction SilentlyContinue)) { Update-PSResource -Name $m.Name -ErrorAction Stop } else { & $installLatest } "updated $($m.Name)" } } } catch { "FAILED $($m.Name): $($_.Exception.Message)" } } } -ArgumentList (, $payload) $script:PSMM_StartupJob } |