Examples/Invoke-SystemMonitorDemo.ps1
|
Import-Module "$PSScriptRoot/../Elm.psd1" -Force # --------------------------------------------------------------------------- # System Monitor demo # Live process table, auto-refreshing every 2 seconds via timer subscription. # Demonstrates: timer-driven data polling, tabular rendering, sort/scroll. # # Controls: # UpArrow / K - scroll up # DownArrow / J - scroll down # C - sort by CPU # M - sort by memory # R - force refresh now # Q - quit # --------------------------------------------------------------------------- $script:PAGE_SIZE = 10 # visible process rows function Get-ProcessSnapshot { @(Get-Process -ErrorAction SilentlyContinue | Where-Object { $null -ne $_ } | Select-Object -Property Id, @{ Name = 'Name'; Expression = { [string]$_.Name } }, @{ Name = 'CpuSec'; Expression = { [math]::Round($_.CPU, 1) } }, @{ Name = 'MemMB'; Expression = { [math]::Round($_.WorkingSet64 / 1MB, 1) } }, @{ Name = 'Threads'; Expression = { [int]$_.Threads.Count } }) } function Sort-Processes { param([object[]]$Procs, [string]$SortBy) if ($SortBy -eq 'CPU') { @($Procs | Sort-Object -Property CpuSec -Descending) } else { @($Procs | Sort-Object -Property MemMB -Descending) } } $initFn = { $procs = Get-ProcessSnapshot $sorted = Sort-Processes -Procs $procs -SortBy 'CPU' [PSCustomObject]@{ Model = [PSCustomObject]@{ Processes = $sorted SortBy = 'CPU' ScrollOffset = 0 RefreshedAt = [datetime]::Now.ToString('HH:mm:ss') } Cmd = $null } } $updateFn = { param($msg, $model) switch ($msg) { 'Refresh' { $procs = Get-ProcessSnapshot $sorted = Sort-Processes -Procs $procs -SortBy $model.SortBy $newModel = [PSCustomObject]@{ Processes = $sorted SortBy = $model.SortBy ScrollOffset = [math]::Min($model.ScrollOffset, [math]::Max(0, $sorted.Count - $script:PAGE_SIZE)) RefreshedAt = [datetime]::Now.ToString('HH:mm:ss') } return [PSCustomObject]@{ Model = $newModel; Cmd = $null } } 'SortByCpu' { $sorted = Sort-Processes -Procs $model.Processes -SortBy 'CPU' $newModel = [PSCustomObject]@{ Processes = $sorted SortBy = 'CPU' ScrollOffset = 0 RefreshedAt = $model.RefreshedAt } return [PSCustomObject]@{ Model = $newModel; Cmd = $null } } 'SortByMem' { $sorted = Sort-Processes -Procs $model.Processes -SortBy 'Memory' $newModel = [PSCustomObject]@{ Processes = $sorted SortBy = 'Memory' ScrollOffset = 0 RefreshedAt = $model.RefreshedAt } return [PSCustomObject]@{ Model = $newModel; Cmd = $null } } 'ScrollUp' { $newOffset = [math]::Max(0, $model.ScrollOffset - 1) $newModel = [PSCustomObject]@{ Processes = $model.Processes SortBy = $model.SortBy ScrollOffset = $newOffset RefreshedAt = $model.RefreshedAt } return [PSCustomObject]@{ Model = $newModel; Cmd = $null } } 'ScrollDown' { $maxOffset = [math]::Max(0, $model.Processes.Count - $script:PAGE_SIZE) $newOffset = [math]::Min($maxOffset, $model.ScrollOffset + 1) $newModel = [PSCustomObject]@{ Processes = $model.Processes SortBy = $model.SortBy ScrollOffset = $newOffset RefreshedAt = $model.RefreshedAt } return [PSCustomObject]@{ Model = $newModel; Cmd = $null } } 'Quit' { return [PSCustomObject]@{ Model = $model Cmd = [PSCustomObject]@{ Type = 'Quit' } } } } [PSCustomObject]@{ Model = $model; Cmd = $null } } $viewFn = { param($model) $titleStyle = New-ElmStyle -Foreground 'BrightCyan' -Bold $headerStyle = New-ElmStyle -Foreground 'BrightWhite' -Bold $rowStyle = New-ElmStyle -Foreground 'White' $dimStyle = New-ElmStyle -Foreground 'BrightBlack' $sortStyle = New-ElmStyle -Foreground 'BrightYellow' $hintStyle = New-ElmStyle -Foreground 'BrightBlack' $sepStyle = New-ElmStyle -Foreground 'BrightBlack' # Column widths: PID(6) Name(24) CPU(9) Mem(9) Threads(8) $fmt = '{0,-6} {1,-24} {2,9} {3,9} {4,8}' $header = $fmt -f 'PID', 'NAME', 'CPU(s)', 'MEM(MB)', 'THREADS' $sep = '-' * 60 $cpuLabel = if ($model.SortBy -eq 'CPU') { '[C] CPU *' } else { '[C] CPU ' } $memLabel = if ($model.SortBy -eq 'Memory') { '[M] Mem *' } else { '[M] Mem ' } $children = [System.Collections.Generic.List[object]]::new() $children.Add((New-ElmText -Content "System Monitor $($model.RefreshedAt)" -Style $titleStyle)) $children.Add((New-ElmText -Content "Sort: $cpuLabel $memLabel $($model.Processes.Count) processes" -Style $sortStyle)) $children.Add((New-ElmText -Content $sep -Style $sepStyle)) $children.Add((New-ElmText -Content $header -Style $headerStyle)) $children.Add((New-ElmText -Content $sep -Style $sepStyle)) $procs = @($model.Processes) $end = [math]::Min($model.ScrollOffset + $script:PAGE_SIZE, $procs.Count) - 1 for ($i = $model.ScrollOffset; $i -le $end; $i++) { $p = $procs[$i] $nameStr = [string]$p.Name $name = if ($nameStr.Length -gt 24) { $nameStr.Substring(0, 23) + '~' } else { $nameStr } $line = $fmt -f $p.Id, $name, $p.CpuSec, $p.MemMB, $p.Threads $children.Add((New-ElmText -Content $line -Style $rowStyle)) } $shown = $end - $model.ScrollOffset + 1 $blank = $script:PAGE_SIZE - [math]::Max(0, $shown) for ($i = 0; $i -lt $blank; $i++) { $children.Add((New-ElmText -Content '')) } $children.Add((New-ElmText -Content $sep -Style $sepStyle)) $scrollInfo = "$($model.ScrollOffset + 1)-$($end + 1) of $($procs.Count)" $children.Add((New-ElmText -Content "[Up/K] [Dn/J] scroll [R] refresh [Q] quit $scrollInfo" -Style $hintStyle)) New-ElmBox -Children $children.ToArray() } $subFn = { param($model) @( New-ElmTimerSub -IntervalMs 2000 -Handler { 'Refresh' } New-ElmKeySub -Key 'Q' -Handler { 'Quit' } New-ElmKeySub -Key 'R' -Handler { 'Refresh' } New-ElmKeySub -Key 'C' -Handler { 'SortByCpu' } New-ElmKeySub -Key 'M' -Handler { 'SortByMem' } New-ElmKeySub -Key 'UpArrow' -Handler { 'ScrollUp' } New-ElmKeySub -Key 'DownArrow' -Handler { 'ScrollDown' } New-ElmKeySub -Key 'K' -Handler { 'ScrollUp' } New-ElmKeySub -Key 'J' -Handler { 'ScrollDown' } ) } function Invoke-SystemMonitorDemo { <# .SYNOPSIS Live process monitor in the terminal. .DESCRIPTION Displays a sortable, scrollable table of running processes, auto-refreshing every 2 seconds via a timer subscription. Controls: Up/K scroll up, Down/J scroll down, C sort by CPU, M sort by memory, R force refresh, Q quit. .NOTES Requires the Elm module. Run from Examples: . .\Invoke-SystemMonitorDemo.ps1; Invoke-SystemMonitorDemo #> [CmdletBinding()] param() Start-ElmProgram -InitFn $initFn -UpdateFn $updateFn -ViewFn $viewFn -SubscriptionFn $subFn } Invoke-SystemMonitorDemo |