Examples/Invoke-StopwatchDemo.ps1
|
Import-Module "$PSScriptRoot/../Elm.psd1" -Force # --------------------------------------------------------------------------- # Stopwatch demo # Start/stop with Space, record lap splits with L, reset with R. # Demonstrates: tick-driven state updates, time formatting, variable-length lists # --------------------------------------------------------------------------- function Format-ElapsedTime { param([long]$Ms) $totalSec = [Math]::Floor($Ms / 1000) $minutes = [Math]::Floor($totalSec / 60) $seconds = $totalSec % 60 $centis = [int][Math]::Floor(($Ms % 1000) / 10) '{0:D2}:{1:D2}.{2:D2}' -f [int]$minutes, [int]$seconds, $centis } function Get-NowMs { [long]([DateTime]::UtcNow - [DateTime]::new(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc)).TotalMilliseconds } $init = { [PSCustomObject]@{ Model = [PSCustomObject]@{ Running = $false StartedAtMs = 0L AccumulatedMs = 0L Laps = @() } Cmd = $null } } $update = { param($msg, $model) switch ($msg.Key) { 'Tick' { if (-not $model.Running) { return [PSCustomObject]@{ Model = $model; Cmd = $null } } $nowMs = Get-NowMs $newAccum = [long]$model.AccumulatedMs + ($nowMs - [long]$model.StartedAtMs) return [PSCustomObject]@{ Model = [PSCustomObject]@{ Running = $true StartedAtMs = $nowMs AccumulatedMs = $newAccum Laps = $model.Laps } Cmd = $null } } 'Spacebar' { if ($model.Running) { $nowMs = Get-NowMs $finalAccum = [long]$model.AccumulatedMs + ($nowMs - [long]$model.StartedAtMs) return [PSCustomObject]@{ Model = [PSCustomObject]@{ Running = $false StartedAtMs = 0L AccumulatedMs = $finalAccum Laps = $model.Laps } Cmd = $null } } else { return [PSCustomObject]@{ Model = [PSCustomObject]@{ Running = $true StartedAtMs = (Get-NowMs) AccumulatedMs = [long]$model.AccumulatedMs Laps = $model.Laps } Cmd = $null } } } 'L' { if (-not $model.Running) { return [PSCustomObject]@{ Model = $model; Cmd = $null } } $nowMs = Get-NowMs $lapTotal = [long]$model.AccumulatedMs + ($nowMs - [long]$model.StartedAtMs) $newLaps = @($model.Laps) + @($lapTotal) return [PSCustomObject]@{ Model = [PSCustomObject]@{ Running = $true StartedAtMs = $nowMs AccumulatedMs = $lapTotal Laps = $newLaps } Cmd = $null } } 'R' { return [PSCustomObject]@{ Model = [PSCustomObject]@{ Running = $false StartedAtMs = 0L AccumulatedMs = 0L Laps = @() } Cmd = $null } } 'Q' { return [PSCustomObject]@{ Model = $model Cmd = [PSCustomObject]@{ Type = 'Quit' } } } } [PSCustomObject]@{ Model = $model; Cmd = $null } } $view = { param($model) $titleStyle = New-ElmStyle -Foreground 'BrightCyan' -Bold $timeStyle = New-ElmStyle -Foreground 'BrightWhite' -Bold $runStyle = New-ElmStyle -Foreground 'BrightGreen' $pauseStyle = New-ElmStyle -Foreground 'BrightYellow' $lapStyle = New-ElmStyle -Foreground 'BrightBlack' $hintStyle = New-ElmStyle -Foreground 'BrightBlack' $boxStyle = New-ElmStyle -Border 'Rounded' -Padding @(0, 2) $displayTime = Format-ElapsedTime -Ms $model.AccumulatedMs $statusNode = if ($model.Running) { New-ElmText -Content '[+] Running' -Style $runStyle } else { New-ElmText -Content '[i] Paused ' -Style $pauseStyle } $children = @( New-ElmText -Content 'Stopwatch' -Style $titleStyle New-ElmText -Content '' New-ElmText -Content $displayTime -Style $timeStyle $statusNode New-ElmText -Content '' ) $laps = @($model.Laps) if ($laps.Count -gt 0) { $children += New-ElmText -Content 'Laps:' -Style $lapStyle $lapStart = [Math]::Max(0, $laps.Count - 8) for ($i = $lapStart; $i -lt $laps.Count; $i++) { $total = $laps[$i] $lapTime = if ($i -gt 0) { $total - $laps[$i - 1] } else { $total } $children += New-ElmText -Content " $($i + 1). $(Format-ElapsedTime -Ms $total) (lap: $(Format-ElapsedTime -Ms $lapTime))" -Style $lapStyle } $children += New-ElmText -Content '' } $children += New-ElmText -Content '[Space] start/stop [L] lap [R] reset [Q] quit' -Style $hintStyle New-ElmBox -Style $boxStyle -Children $children } Start-ElmProgram -InitFn $init -UpdateFn $update -ViewFn $view -TickMs 100 |