private/StatusPanel.ps1
|
# FR-024: fixed, live activity panel pinned to the bottom of the console, using native # cursor positioning (no external TUI dependency - research.md). Shown during both dry-run # scanning and apply. # # ponytail: fixed-row overwrite of a single reserved block, not a real scrolling split-pane. # Callers must bracket a scan/apply block with Initialize-OctaActivityPanel ... Write-OctaActivity # (repeatable) ... Complete-OctaActivityPanel, and must not Write-Host anything else in between - # upgrade to a proper scroll region if a future screen needs to interleave the two. $script:OctaActivityLog = [System.Collections.Generic.List[string]]::new() $script:OctaActivityPanelEnabled = $false $script:OctaActivityPanelRow = -1 $script:OctaActivityPanelLines = 4 # divider + header + 2 most-recent events function Test-OctaInteractiveConsole { try { return ($null -ne $Host.UI.RawUI -and -not [Console]::IsOutputRedirected) } catch { return $false } } function Initialize-OctaActivityPanel { [CmdletBinding()] param() $script:OctaActivityLog.Clear() $script:OctaActivityPanelEnabled = Test-OctaInteractiveConsole if (-not $script:OctaActivityPanelEnabled) { return } for ($i = 0; $i -lt $script:OctaActivityPanelLines; $i++) { Write-Host '' } $script:OctaActivityPanelRow = $Host.UI.RawUI.CursorPosition.Y - $script:OctaActivityPanelLines } function Write-OctaActivity { [CmdletBinding()] param([Parameter(Mandatory)][string]$Message) if (-not $script:OctaActivityPanelEnabled) { # Non-interactive / redirected-output fallback: plain scrolling line, same content. Write-Host $Message return } $script:OctaActivityLog.Add($Message) if ($script:OctaActivityLog.Count -gt 2) { $script:OctaActivityLog.RemoveAt(0) } $savedCursor = $Host.UI.RawUI.CursorPosition $width = $Host.UI.RawUI.WindowSize.Width $lines = @(('-' * [Math]::Min(64, $width)), 'Actividad en vivo / Live activity:') + $script:OctaActivityLog while ($lines.Count -lt $script:OctaActivityPanelLines) { $lines += '' } for ($i = 0; $i -lt $script:OctaActivityPanelLines; $i++) { $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $script:OctaActivityPanelRow + $i) $text = $lines[$i] if ($text.Length -gt $width) { $text = $text.Substring(0, $width) } Write-Host $text.PadRight($width) -NoNewline } $Host.UI.RawUI.CursorPosition = $savedCursor } function Complete-OctaActivityPanel { <# Moves the cursor past the reserved panel rows so anything printed afterwards (the dry-run preview list, a confirmation prompt, etc.) starts on a fresh line below it. #> [CmdletBinding()] param() if (-not $script:OctaActivityPanelEnabled) { return } $Host.UI.RawUI.CursorPosition = [System.Management.Automation.Host.Coordinates]::new(0, $script:OctaActivityPanelRow + $script:OctaActivityPanelLines) } |