Public/Status.ps1
|
$SPINNERS = @{ "dots" = @{ "interval" = 80 "frames" = @("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏") } "dots2" = @{ "interval" = 80 "frames" = @("⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷") } "line" = @{ "interval" = 130 "frames" = @("-", "\", "|", "/") } "arc" = @{ "interval" = 100 "frames" = @("◜", "◠", "◝", "◞", "◡", "◟") } "moon" = @{ "interval" = 80 "frames" = @("🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘") } "bouncingBall" = @{ "interval" = 80 "frames" = @("( ● )", "( ● )", "( ● )", "( ● )", "( ●)", "( ● )", "( ● )", "( ● )") } } function New-RichSpinner { <# .SYNOPSIS Creates a new Rich Spinner object. .DESCRIPTION Initializes a spinner configuration for use with status updates. .PARAMETER Name The name of the spinner type (e.g., "dots", "line", "moon"). Defaults to "dots". .PARAMETER Text The text to display alongside the spinner. .PARAMETER Style The style for the spinner (e.g., "cyan"). .PARAMETER Speed The speed multiplier for the spinner animation. #> [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$Name = "dots", [Parameter(Mandatory = $false)] [string]$Text = "", [Parameter(Mandatory = $false)] [string]$Style = "cyan", [Parameter(Mandatory = $false)] [double]$Speed = 1.0 ) if (-not $SPINNERS.ContainsKey($Name)) { Write-Error "No spinner called '$Name'" return $null } $spinnerDef = $SPINNERS[$Name] return @{ Name = $Name Frames = $spinnerDef.frames Interval = $spinnerDef.interval Text = $Text Style = $Style Speed = $Speed StartTime = [DateTime]::Now } } function Start-RichStatus { <# .SYNOPSIS Runs a script block with a live status spinner. .DESCRIPTION Executes a script block and displays a live animated spinner with status text in the console. .PARAMETER Status The status message to display. .PARAMETER SpinnerName The name of the spinner to use. Defaults to "dots". .PARAMETER SpinnerStyle The style for the spinner. Defaults to "bold cyan". .PARAMETER ScriptBlock The script block to execute while the spinner is active. .EXAMPLE Start-RichStatus -Status "Processing data..." -ScriptBlock { Start-Sleep -Seconds 2 } #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Status, [Parameter(Mandatory = $false)] [string]$SpinnerName = "dots", [Parameter(Mandatory = $false)] [string]$SpinnerStyle = "bold cyan", [Parameter(Mandatory = $true)] [scriptblock]$ScriptBlock ) $spinner = New-RichSpinner -Name $SpinnerName -Text $Status -Style $SpinnerStyle # We'll use a simple loop with a timeout for the scriptblock if possible, # but PowerShell doesn't easily support "running a scriptblock and updating UI" # without threads or jobs. # For this port, we'll implement a "Live" update approach using a background thread # to handle the spinner animation while the main thread runs the scriptblock. $runspace = [runspacefactory]::CreateRunspace() $runspace.Open() $powershell = [powershell]::Create().AddScript({ param($spinner, $status, $modulePath) # Ensure UTF8 output for Unicode spinners [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 # Import the module components needed for styling # We'll just use raw ANSI for simplicity in the thread to avoid complex imports function Get-SimpleAnsi { param($style) $esc = [char]27 if ($style -match "bold") { $res += "$esc[1m" } if ($style -match "cyan") { $res += "$esc[36m" } if ($style -match "green") { $res += "$esc[32m" } if ($style -match "magenta") { $res += "$esc[35m" } if ($style -match "red") { $res += "$esc[31m" } if ($style -match "yellow") { $res += "$esc[33m" } if ($style -match "blue") { $res += "$esc[34m" } return $res } $frames = $spinner.Frames $styleCode = Get-SimpleAnsi -style $spinner.Style $reset = "$([char]27)[0m" $i = 0 # Hide cursor [Console]::Write("$([char]27)[?25l") try { while ($true) { $frame = $frames[$i % $frames.Count] # Simple status rendering (no markup support in thread for now to keep it fast) $cleanStatus = $status -replace "\[.*?\]", "" [Console]::Write("`r$([char]27)[K$styleCode$frame$reset $cleanStatus") $i++ [System.Threading.Thread]::Sleep([int]($spinner.Interval / $spinner.Speed)) } } catch {} finally { [Console]::Write("`r$([char]27)[K$([char]27)[?25h") } }).AddArgument($spinner).AddArgument($Status).AddArgument($PSScriptRoot) $powershell.Runspace = $runspace $handle = $powershell.BeginInvoke() try { # Run the actual work in the main thread $result = &$ScriptBlock return $result } finally { $powershell.Stop() $runspace.Close() $powershell.Dispose() $runspace.Dispose() # Ensure cursor is back and line is clean Write-Host -NoNewline "`r$([char]27)[K$([char]27)[?25h" } } |