src/UI/68-Tasks.ps1
|
# 68-Tasks.ps1 — background-task screen (#25) + the completion handler the # grid loop calls, + background Update-Help (#35). # React to freshly finished tasks: refresh state, surface a status line. # Called from the grid loop on every wake-up. function script:Receive-PSMMUITask { Update-PSMMTask $ui = $script:PSMM_UI foreach ($t in @(Get-PSMMTask | Where-Object { $_.Done -and -not $_.Seen })) { switch ($t.Kind) { 'unmanagedscan' { # no status line here: Build-PSMMGrid renders a standing # notice for unshown unmanaged modules - a status too would # print the same text twice $ui.Unmanaged = @($t.Output) } 'install' { $names = @($t.Data) if ($names.Count) { Update-PSMMAvailable -Entries $ui.Entries -Name $names Update-PSMMLoaded -Entries $ui.Entries } $fails = @($t.Output | Where-Object { "$_" -like 'FAILED *' }) $ui.Status = if ($fails.Count) { "[orange1]install/update: $($fails.Count) of $($names.Count) failed (t=details)[/]" } else { "[green3]install/update done: $($names -join ', ')[/]" } } 'updatecheck' { $found = 0 $byName = @{} foreach ($o in $t.Output) { if ($o.Name) { $byName[$o.Name] = "$($o.Latest)" } } foreach ($e in $ui.Entries) { if (-not $byName.ContainsKey($e.Name)) { continue } $e.LatestVersion = $byName[$e.Name] $e.UpdateAvailable = $false if ($e.PinnedExact) { continue } if ($e.InstalledVersion) { $lv = $byName[$e.Name] -replace '-.*$', '' try { $e.UpdateAvailable = ([version]$lv -gt [version]"$($e.InstalledVersion)") } catch { } } if ($e.UpdateAvailable) { $found++ } } $ui.Status = if ($found) { "[orange1]$found update(s) available (^ in the Ver column)[/]" } else { '[green3]everything up to date[/]' } } 'updatehelp' { $ui.Status = if ($t.Failed) { '[orange1]Update-Help finished with errors (t=details)[/]' } else { '[green3]Update-Help done[/]' } } default { $ui.Status = if ($t.Failed) { "[orange1]task '$(ConvertTo-PSMMSafe $t.Label)' failed (t=details)[/]" } else { "[green3]task '$(ConvertTo-PSMMSafe $t.Label)' done[/]" } } } $t.Seen = $true } } # Kick off Update-Help for all installed modules in the background (#35). function script:Start-PSMMUpdateHelpTask { $running = @(Get-PSMMTask | Where-Object { $_.Kind -eq 'updatehelp' -and -not $_.Done }) if ($running.Count) { $script:PSMM_UI.Status = '[orange1]Update-Help is already running[/]'; return } $null = Start-PSMMTask -Label 'Update-Help (all modules)' -Kind 'updatehelp' -ScriptBlock { try { Update-Help -Scope CurrentUser -Force -ErrorAction SilentlyContinue -ErrorVariable errs 3>$null foreach ($e in @($errs)) { "note: $($e.Exception.Message)" } 'help update finished' } catch { "FAILED: $($_.Exception.Message)" } } $script:PSMM_UI.Status = "[$script:PSMM_ColAccent]Update-Help started in the background[/]" } function script:Build-PSMMTasksView { param( [Parameter(Mandatory)] $State, [Parameter(Mandatory)] $Tasks, [string]$StatusMarkup ) $n = $Tasks.Count $win = Get-PSMMWinSize $vp = Get-PSMMViewport -State $State -Count $n -Rows ($win.Height - 10) $T = [Spectre.Console.Table]::new() $T.Border = [Spectre.Console.TableBorder]::Rounded foreach ($h in ' ', 'Task', 'State', 'Started', 'Output') { [void][Spectre.Console.TableExtensions]::AddColumn($T, $h) } for ($i = $vp.First; $i -le $vp.Last; $i++) { # NB: $task, not $t - PowerShell variables are case-insensitive and $T # is the table right above. $task = $Tasks[$i] $nm = ConvertTo-PSMMSafe (Get-PSMMTrunc $task.Label 44) if ($i -eq $State.Cursor) { $nm = "[$script:PSMM_ColAccent]$nm[/]" } $state = if (-not $task.Done) { "[deepskyblue1]running[/]" } elseif ($task.Failed) { '[indianred1]failed[/]' } else { '[green3]done[/]' } [void][Spectre.Console.TableExtensions]::AddRow($T, [string[]]@( $(if ($i -eq $State.Cursor) { "[$script:PSMM_ColAccent]>[/]" } else { ' ' }), $nm, $state, $task.StartedAt.ToString('HH:mm:ss'), "$($task.Output.Count) line(s)")) } $pos = Get-PSMMPositionMarkup -State $State -Count $n -Viewport $vp $items = [System.Collections.Generic.List[Spectre.Console.Rendering.IRenderable]]::new() $items.Add([Spectre.Console.Markup]::new("[$script:PSMM_ColAccent]Background tasks[/]$pos")) $items.Add($T) $items.Add([Spectre.Console.Markup]::new((Get-PSMMHint -Pairs @('up/dn=move', 'enter=view output', 'u=run Update-Help', 'c=clear finished', '?=help', 'esc=back', 'Ctrl+Q=quit')))) if ($StatusMarkup) { $items.Add([Spectre.Console.Markup]::new($StatusMarkup)) } [Spectre.Console.Rows]::new($items) } function script:Show-PSMMTasks { $ui = $script:PSMM_UI $status = '' while ($true) { if ($ui.HardQuit) { return } Update-PSMMTask $tasks = @(Get-PSMMTask) $st = New-PSMMListState $st.Status = $status $status = '' $action = @{ Name = $null } if (-not $tasks.Count) { Clear-PSMMScreen Write-PSMMLine "[$script:PSMM_ColAccent]Background tasks[/]" Write-PSMMLine '[grey66]No tasks yet. u starts a background Update-Help; installs/updates/scans appear here too.[/]' Write-PSMMLine (Get-PSMMHint -Pairs @('u=run Update-Help', 'esc=back')) $k = [Console]::ReadKey($true) if (Test-PSMMHardQuitKey $k) { $ui.HardQuit = $true; return } if ($k.Key -eq [ConsoleKey]::U) { Start-PSMMUpdateHelpTask; continue } return } Clear-PSMMScreen Invoke-PSMMLive -Body { param($ctx) while ($true) { if ($script:PSMM_UI.HardQuit) { return } Update-PSMMTask $tasks = @(Get-PSMMTask) $ctx.UpdateTarget((Build-PSMMTasksView -State $st -Tasks $tasks -StatusMarkup $st.Status)) $ctx.Refresh() $k = Read-PSMMKeyResize if ($null -eq $k) { continue } if (Test-PSMMHardQuitKey $k) { $script:PSMM_UI.HardQuit = $true; return } $st.Status = '' if (Invoke-PSMMListNav -State $st -KeyInfo $k -Count $tasks.Count) { continue } switch ($k.Key) { ([ConsoleKey]::Enter) { $action.Name = 'view'; return } ([ConsoleKey]::U) { Start-PSMMUpdateHelpTask; continue } ([ConsoleKey]::C) { Clear-PSMMTask; $st.Cursor = 0; $st.Status = '[green3]finished tasks cleared[/]'; continue } ([ConsoleKey]::Escape) { $action.Name = 'back'; return } default { if ($k.KeyChar -eq '?') { $action.Name = 'help'; return } } } } } if ($ui.HardQuit) { return } switch ($action.Name) { 'back' { return } 'help' { Show-PSMMHelpScreen -Topic 'tasks' } 'view' { $tasks = @(Get-PSMMTask) if ($tasks.Count -and $st.Cursor -lt $tasks.Count) { $t = $tasks[$st.Cursor] $lines = @("task: $($t.Label)", "state: $(if (-not $t.Done) { 'running' } elseif ($t.Failed) { 'failed' } else { 'done' })", '') + @($t.Output | ForEach-Object { "$_" }) Show-PSMMPager -Lines $lines -TitleMarkup "[$script:PSMM_ColAccent]Task output[/]" } } } } } |