diff --git a/RichPS7/Classes/Color.ps1 b/RichPS7/Classes/Color.ps1 new file mode 100644 index 00000000..bf4d6deb --- /dev/null +++ b/RichPS7/Classes/Color.ps1 @@ -0,0 +1,117 @@ +class RichColor { + [int]$Red + [int]$Green + [int]$Blue + [string]$Name + + # Constructor for RGB + RichColor([int]$r, [int]$g, [int]$b) { + $this.Red = [Math]::Max(0, [Math]::Min(255, $r)) + $this.Green = [Math]::Max(0, [Math]::Min(255, $g)) + $this.Blue = [Math]::Max(0, [Math]::Min(255, $b)) + $this.Name = "" + } + + # Constructor for named color + RichColor([string]$name) { + $this.Name = $name.ToLower() + $rgb = $this.ParseNamedColor($name) + $this.Red = $rgb[0] + $this.Green = $rgb[1] + $this.Blue = $rgb[2] + } + + # Parse named colors + hidden [int[]] ParseNamedColor([string]$name) { + $colors = @{ + 'black' = @(0, 0, 0) + 'red' = @(255, 0, 0) + 'green' = @(0, 255, 0) + 'yellow' = @(255, 255, 0) + 'blue' = @(0, 0, 255) + 'magenta' = @(255, 0, 255) + 'cyan' = @(0, 255, 255) + 'white' = @(255, 255, 255) + 'bright_black' = @(128, 128, 128) + 'bright_red' = @(255, 128, 128) + 'bright_green' = @(128, 255, 128) + 'bright_yellow' = @(255, 255, 128) + 'bright_blue' = @(128, 128, 255) + 'bright_magenta' = @(255, 128, 255) + 'bright_cyan' = @(128, 255, 255) + 'bright_white' = @(255, 255, 255) + 'grey' = @(128, 128, 128) + 'gray' = @(128, 128, 128) + 'orange' = @(255, 165, 0) + 'purple' = @(128, 0, 128) + 'pink' = @(255, 192, 203) + } + + $normalizedName = $name.ToLower().Replace(' ', '_').Replace('-', '_') + if ($colors.ContainsKey($normalizedName)) { + return $colors[$normalizedName] + } + + # Try to parse hex color (#RGB or #RRGGBB) + if ($name -match '^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$') { + $hex = $name.Substring(1) + if ($hex.Length -eq 3) { + $r = [Convert]::ToInt32($hex[0].ToString() * 2, 16) + $g = [Convert]::ToInt32($hex[1].ToString() * 2, 16) + $b = [Convert]::ToInt32($hex[2].ToString() * 2, 16) + } else { + $r = [Convert]::ToInt32($hex.Substring(0, 2), 16) + $g = [Convert]::ToInt32($hex.Substring(2, 2), 16) + $b = [Convert]::ToInt32($hex.Substring(4, 2), 16) + } + return @($r, $g, $b) + } + + # Default to white if unknown + return @(255, 255, 255) + } + + # Get ANSI escape code for foreground color + [string] GetForegroundCode() { + return "`e[38;2;$($this.Red);$($this.Green);$($this.Blue)m" + } + + # Get ANSI escape code for background color + [string] GetBackgroundCode() { + return "`e[48;2;$($this.Red);$($this.Green);$($this.Blue)m" + } + + # Convert to hex string + [string] ToHex() { + return "#{0:X2}{1:X2}{2:X2}" -f $this.Red, $this.Green, $this.Blue + } + + [string] ToString() { + if ($this.Name) { + return $this.Name + } + return $this.ToHex() + } +} + +# Helper function to create color from various inputs +function New-RichColor { + param( + [Parameter(ValueFromPipeline)] + $Color + ) + + if ($Color -is [RichColor]) { + return $Color + } + + if ($Color -is [string]) { + return [RichColor]::new($Color) + } + + if ($Color -is [array] -and $Color.Count -eq 3) { + return [RichColor]::new($Color[0], $Color[1], $Color[2]) + } + + return [RichColor]::new("white") +} diff --git a/RichPS7/Classes/Console.ps1 b/RichPS7/Classes/Console.ps1 new file mode 100644 index 00000000..1421bc96 --- /dev/null +++ b/RichPS7/Classes/Console.ps1 @@ -0,0 +1,188 @@ +class RichConsole { + [int]$Width + [int]$Height + [bool]$ColorSystem + [object]$Theme + + RichConsole() { + $this.UpdateSize() + $this.ColorSystem = $this.DetectColorSupport() + $this.Theme = @{ + 'info' = New-RichColor 'cyan' + 'warning' = New-RichColor 'yellow' + 'error' = New-RichColor 'red' + 'success' = New-RichColor 'green' + } + } + + # Detect terminal size + hidden [void] UpdateSize() { + try { + $this.Width = $Host.UI.RawUI.WindowSize.Width + $this.Height = $Host.UI.RawUI.WindowSize.Height + } catch { + $this.Width = 80 + $this.Height = 24 + } + } + + # Detect if terminal supports colors + hidden [bool] DetectColorSupport() { + # PowerShell 7+ has good ANSI support + if ($PSVersionTable.PSVersion.Major -ge 7) { + return $true + } + + # Check if running in Windows Terminal or other modern terminals + if ($env:WT_SESSION -or $env:TERM_PROGRAM) { + return $true + } + + return $false + } + + # Print text with optional style + [void] Print([string]$text) { + Write-Host $text + } + + [void] Print([string]$text, [RichStyle]$style) { + if ($style) { + Write-Host $style.Apply($text) -NoNewline + Write-Host "" + } else { + Write-Host $text + } + } + + # Print with foreground color + [void] Print([string]$text, [RichColor]$color) { + $style = [RichStyle]::new() + $style.ForegroundColor = $color + $this.Print($text, $style) + } + + # Print without newline + [void] PrintNoNewline([string]$text) { + Write-Host $text -NoNewline + } + + [void] PrintNoNewline([string]$text, [RichStyle]$style) { + if ($style) { + Write-Host $style.Apply($text) -NoNewline + } else { + Write-Host $text -NoNewline + } + } + + # Print a line (rule) + [void] Rule([string]$title) { + $this.Rule($title, $null) + } + + [void] Rule([string]$title, [RichColor]$color) { + $this.UpdateSize() + + if ($title) { + $titleLen = $title.Length + 2 # Add spaces around title + $leftLen = [Math]::Floor(($this.Width - $titleLen) / 2) + $rightLen = $this.Width - $titleLen - $leftLen + + $line = "─" * $leftLen + " $title " + "─" * $rightLen + } else { + $line = "─" * $this.Width + } + + if ($color) { + $style = [RichStyle]::new() + $style.ForegroundColor = $color + $this.Print($line, $style) + } else { + $this.Print($line) + } + } + + # Print styled text with theme + [void] Info([string]$text) { + $style = [RichStyle]::new() + $style.ForegroundColor = $this.Theme['info'] + $this.Print($text, $style) + } + + [void] Warning([string]$text) { + $style = [RichStyle]::new() + $style.ForegroundColor = $this.Theme['warning'] + $this.Print($text, $style) + } + + [void] Error([string]$text) { + $style = [RichStyle]::new() + $style.ForegroundColor = $this.Theme['error'] + $this.Print($text, $style) + } + + [void] Success([string]$text) { + $style = [RichStyle]::new() + $style.ForegroundColor = $this.Theme['success'] + $this.Print($text, $style) + } + + # Clear the screen + [void] Clear() { + Clear-Host + } + + # Print a blank line + [void] NewLine() { + Write-Host "" + } + + [void] NewLine([int]$count) { + for ($i = 0; $i -lt $count; $i++) { + Write-Host "" + } + } +} + +# Create a default console instance +$Global:RichConsole = [RichConsole]::new() + +# Helper function to get the global console +function Get-RichConsole { + return $Global:RichConsole +} + +# Convenience function for printing +function Write-Rich { + param( + [Parameter(Mandatory, Position = 0)] + [string]$Text, + + [Parameter(Position = 1)] + [RichStyle]$Style, + + [RichColor]$Color, + + [switch]$Bold, + [switch]$Italic, + [switch]$Underline, + [switch]$NoNewline + ) + + $console = Get-RichConsole + + # Build style if not provided + if (-not $Style -and ($Color -or $Bold -or $Italic -or $Underline)) { + $Style = [RichStyle]::new() + if ($Color) { $Style.ForegroundColor = $Color } + if ($Bold) { $Style.Bold = $true } + if ($Italic) { $Style.Italic = $true } + if ($Underline) { $Style.Underline = $true } + } + + if ($NoNewline) { + $console.PrintNoNewline($Text, $Style) + } else { + $console.Print($Text, $Style) + } +} diff --git a/RichPS7/Classes/Panel.ps1 b/RichPS7/Classes/Panel.ps1 new file mode 100644 index 00000000..e00c523c --- /dev/null +++ b/RichPS7/Classes/Panel.ps1 @@ -0,0 +1,238 @@ +class RichPanel { + [string]$Content + [string]$Title + [string]$Subtitle + [string]$BoxStyle + [RichStyle]$TitleStyle + [RichStyle]$BorderStyle + [RichStyle]$ContentStyle + [int]$Width + [int]$Padding + [string]$TitleAlign + [hashtable]$BoxChars + + RichPanel([string]$content) { + $this.Content = $content + $this.BoxStyle = "rounded" + $this.Padding = 1 + $this.TitleAlign = "left" + $this.Width = 0 # Auto-width + $this.InitializeBoxChars() + + # Default styles + $this.TitleStyle = [RichStyle]::new() + $this.TitleStyle.Bold = $true + } + + hidden [void] InitializeBoxChars() { + # Reuse the same box drawing characters as Table + $this.BoxChars = @{ + 'rounded' = @{ + 'top_left' = '╭' + 'top_right' = '╮' + 'bottom_left' = '╰' + 'bottom_right' = '╯' + 'horizontal' = '─' + 'vertical' = '│' + } + 'square' = @{ + 'top_left' = '┌' + 'top_right' = '┐' + 'bottom_left' = '└' + 'bottom_right' = '┘' + 'horizontal' = '─' + 'vertical' = '│' + } + 'double' = @{ + 'top_left' = '╔' + 'top_right' = '╗' + 'bottom_left' = '╚' + 'bottom_right' = '╝' + 'horizontal' = '═' + 'vertical' = '║' + } + 'heavy' = @{ + 'top_left' = '┏' + 'top_right' = '┓' + 'bottom_left' = '┗' + 'bottom_right' = '┛' + 'horizontal' = '━' + 'vertical' = '┃' + } + 'simple' = @{ + 'top_left' = '+' + 'top_right' = '+' + 'bottom_left' = '+' + 'bottom_right' = '+' + 'horizontal' = '-' + 'vertical' = '|' + } + } + } + + # Calculate the width of the panel + hidden [int] CalculateWidth() { + if ($this.Width -gt 0) { + return $this.Width + } + + # Calculate based on content + $lines = $this.Content -split "`n" + $maxWidth = 0 + + foreach ($line in $lines) { + if ($line.Length -gt $maxWidth) { + $maxWidth = $line.Length + } + } + + # Add padding + $maxWidth += ($this.Padding * 2) + + # Check title width + if ($this.Title) { + $titleWidth = $this.Title.Length + 4 # Add space for title decoration + if ($titleWidth -gt $maxWidth) { + $maxWidth = $titleWidth + } + } + + # Ensure minimum width + if ($maxWidth -lt 10) { + $maxWidth = 10 + } + + return $maxWidth + } + + # Align text within given width + hidden [string] AlignText([string]$text, [int]$width, [string]$align) { + $len = $text.Length + + if ($len -ge $width) { + return $text.Substring(0, $width) + } + + $padding = $width - $len + + switch ($align) { + 'center' { + $leftPad = [Math]::Floor($padding / 2) + $rightPad = $padding - $leftPad + return (' ' * $leftPad) + $text + (' ' * $rightPad) + } + 'right' { + return (' ' * $padding) + $text + } + default { # left + return $text + (' ' * $padding) + } + } + } + + # Render the panel + [string] Render() { + $output = [System.Text.StringBuilder]::new() + $box = $this.BoxChars[$this.BoxStyle] + $width = $this.CalculateWidth() + $innerWidth = $width - 2 # Subtract borders + + # Top border with title + if ($this.Title) { + $titleText = " $($this.Title) " + if ($this.TitleStyle) { + $titleText = $this.TitleStyle.Apply($titleText) + } + + $titleLen = $this.Title.Length + 2 + $leftLen = switch ($this.TitleAlign) { + 'center' { [Math]::Max(1, [Math]::Floor(($innerWidth - $titleLen) / 2)) } + 'right' { [Math]::Max(1, $innerWidth - $titleLen - 1) } + default { 1 } # left + } + $rightLen = [Math]::Max(1, $innerWidth - $titleLen - $leftLen) + + $topLine = $box.top_left + ($box.horizontal * $leftLen) + $titleText + ($box.horizontal * $rightLen) + $box.top_right + } else { + $topLine = $box.top_left + ($box.horizontal * $innerWidth) + $box.top_right + } + + if ($this.BorderStyle) { + $topLine = $this.BorderStyle.Apply($topLine) + } + [void]$output.AppendLine($topLine) + + # Content lines + $lines = $this.Content -split "`n" + $contentWidth = $innerWidth - ($this.Padding * 2) + + foreach ($line in $lines) { + $paddedLine = (' ' * $this.Padding) + $line.PadRight($contentWidth) + (' ' * $this.Padding) + + if ($this.ContentStyle) { + $paddedLine = $this.ContentStyle.Apply($paddedLine) + } + + $bordered = $box.vertical + $paddedLine + $box.vertical + + if ($this.BorderStyle) { + # Apply border style to the border characters only + $bordered = $this.BorderStyle.Apply($box.vertical) + $paddedLine + $this.BorderStyle.Apply($box.vertical) + } + + [void]$output.AppendLine($bordered) + } + + # Bottom border with subtitle + if ($this.Subtitle) { + $subtitleText = " $($this.Subtitle) " + $subtitleLen = $this.Subtitle.Length + 2 + $leftLen = [Math]::Max(1, [Math]::Floor(($innerWidth - $subtitleLen) / 2)) + $rightLen = [Math]::Max(1, $innerWidth - $subtitleLen - $leftLen) + + $bottomLine = $box.bottom_left + ($box.horizontal * $leftLen) + $subtitleText + ($box.horizontal * $rightLen) + $box.bottom_right + } else { + $bottomLine = $box.bottom_left + ($box.horizontal * $innerWidth) + $box.bottom_right + } + + if ($this.BorderStyle) { + $bottomLine = $this.BorderStyle.Apply($bottomLine) + } + [void]$output.Append($bottomLine) + + return $output.ToString() + } +} + +# Helper function to create and render a panel +function New-RichPanel { + param( + [Parameter(Mandatory, Position = 0)] + [string]$Content, + + [string]$Title, + [string]$Subtitle, + [string]$BoxStyle = "rounded", + [int]$Width = 0, + [int]$Padding = 1, + [string]$TitleAlign = "left", + [RichStyle]$TitleStyle, + [RichStyle]$BorderStyle, + [RichStyle]$ContentStyle + ) + + $panel = [RichPanel]::new($Content) + + if ($Title) { $panel.Title = $Title } + if ($Subtitle) { $panel.Subtitle = $Subtitle } + $panel.BoxStyle = $BoxStyle + $panel.Width = $Width + $panel.Padding = $Padding + $panel.TitleAlign = $TitleAlign + + if ($TitleStyle) { $panel.TitleStyle = $TitleStyle } + if ($BorderStyle) { $panel.BorderStyle = $BorderStyle } + if ($ContentStyle) { $panel.ContentStyle = $ContentStyle } + + return $panel +} diff --git a/RichPS7/Classes/Progress.ps1 b/RichPS7/Classes/Progress.ps1 new file mode 100644 index 00000000..5683ef29 --- /dev/null +++ b/RichPS7/Classes/Progress.ps1 @@ -0,0 +1,213 @@ +class RichProgressBar { + [string]$Description + [int]$Total + [int]$Current + [bool]$IsComplete + [datetime]$StartTime + [RichColor]$CompleteColor + [RichColor]$RemainingColor + [int]$Width + + RichProgressBar([string]$description, [int]$total) { + $this.Description = $description + $this.Total = $total + $this.Current = 0 + $this.IsComplete = $false + $this.StartTime = Get-Date + $this.Width = 40 + $this.CompleteColor = New-RichColor 'green' + $this.RemainingColor = New-RichColor 'bright_black' + } + + # Update progress + [void] Update([int]$value) { + $this.Current = [Math]::Min($value, $this.Total) + $this.IsComplete = ($this.Current -ge $this.Total) + } + + [void] Advance([int]$delta) { + $this.Update($this.Current + $delta) + } + + # Get percentage complete + [double] GetPercentage() { + if ($this.Total -eq 0) { return 100.0 } + return ($this.Current / $this.Total) * 100.0 + } + + # Get elapsed time + [timespan] GetElapsed() { + return (Get-Date) - $this.StartTime + } + + # Estimate remaining time + [timespan] GetEstimatedRemaining() { + if ($this.Current -eq 0) { + return [timespan]::Zero + } + + $elapsed = $this.GetElapsed() + $rate = $this.Current / $elapsed.TotalSeconds + + if ($rate -eq 0) { + return [timespan]::Zero + } + + $remaining = $this.Total - $this.Current + $seconds = $remaining / $rate + + return [timespan]::FromSeconds($seconds) + } + + # Format time span + hidden [string] FormatTimeSpan([timespan]$ts) { + if ($ts.TotalHours -ge 1) { + return "{0:D2}:{1:D2}:{2:D2}" -f [Math]::Floor($ts.TotalHours), $ts.Minutes, $ts.Seconds + } elseif ($ts.TotalMinutes -ge 1) { + return "{0:D2}:{1:D2}" -f $ts.Minutes, $ts.Seconds + } else { + return "{0:D2}s" -f $ts.Seconds + } + } + + # Render the progress bar + [string] Render() { + $percentage = $this.GetPercentage() + $filled = [Math]::Floor($this.Width * ($percentage / 100.0)) + $remaining = $this.Width - $filled + + # Build the bar + $completeStyle = [RichStyle]::new() + $completeStyle.ForegroundColor = $this.CompleteColor + + $remainingStyle = [RichStyle]::new() + $remainingStyle.ForegroundColor = $this.RemainingColor + + $bar = $completeStyle.Apply('█' * $filled) + $remainingStyle.Apply('░' * $remaining) + + # Build the info text + $elapsed = $this.FormatTimeSpan($this.GetElapsed()) + $eta = if ($this.IsComplete) { + "Done" + } else { + $this.FormatTimeSpan($this.GetEstimatedRemaining()) + } + + $percentText = "{0,5:F1}%" -f $percentage + $countText = "$($this.Current)/$($this.Total)" + + # Combine everything + return "$($this.Description.PadRight(30)) [$bar] $percentText $countText ETA: $eta" + } +} + +class RichProgress { + [System.Collections.ArrayList]$Tasks + [bool]$IsLive + [int]$LastLineCount + + RichProgress() { + $this.Tasks = [System.Collections.ArrayList]::new() + $this.IsLive = $false + $this.LastLineCount = 0 + } + + # Add a task + [RichProgressBar] AddTask([string]$description, [int]$total) { + $task = [RichProgressBar]::new($description, $total) + [void]$this.Tasks.Add($task) + return $task + } + + # Start live display + [void] Start() { + $this.IsLive = $true + [Console]::CursorVisible = $false + } + + # Stop live display + [void] Stop() { + $this.IsLive = $false + [Console]::CursorVisible = $true + } + + # Render all tasks + [string] Render() { + $output = [System.Text.StringBuilder]::new() + + foreach ($task in $this.Tasks) { + [void]$output.AppendLine($task.Render()) + } + + return $output.ToString().TrimEnd() + } + + # Update the display + [void] Refresh() { + if (-not $this.IsLive) { return } + + # Move cursor up to overwrite previous output + if ($this.LastLineCount -gt 0) { + for ($i = 0; $i -lt $this.LastLineCount; $i++) { + Write-Host "`e[1A`e[2K" -NoNewline + } + } + + # Render and display + $output = $this.Render() + Write-Host $output + + # Track how many lines we wrote + $this.LastLineCount = ($output -split "`n").Count + } + + # Check if all tasks are complete + [bool] IsComplete() { + foreach ($task in $this.Tasks) { + if (-not $task.IsComplete) { + return $false + } + } + return $true + } +} + +# Helper function to create a progress display +function New-RichProgress { + return [RichProgress]::new() +} + +# Helper function for simple progress tracking +function Invoke-RichProgress { + param( + [Parameter(Mandatory)] + [string]$Description, + + [Parameter(Mandatory)] + [array]$Collection, + + [Parameter(Mandatory)] + [scriptblock]$ScriptBlock + ) + + $progress = [RichProgress]::new() + $task = $progress.AddTask($Description, $Collection.Count) + + $progress.Start() + + try { + foreach ($item in $Collection) { + $progress.Refresh() + + # Execute the script block + & $ScriptBlock $item + + $task.Advance(1) + } + + $progress.Refresh() + Start-Sleep -Milliseconds 500 # Show final state + } finally { + $progress.Stop() + } +} diff --git a/RichPS7/Classes/Style.ps1 b/RichPS7/Classes/Style.ps1 new file mode 100644 index 00000000..fd487d0e --- /dev/null +++ b/RichPS7/Classes/Style.ps1 @@ -0,0 +1,110 @@ +class RichStyle { + [RichColor]$ForegroundColor + [RichColor]$BackgroundColor + [bool]$Bold + [bool]$Italic + [bool]$Underline + [bool]$Strike + [bool]$Dim + + RichStyle() { + $this.Bold = $false + $this.Italic = $false + $this.Underline = $false + $this.Strike = $false + $this.Dim = $false + } + + # Get the complete ANSI code for this style + [string] GetAnsiCode() { + $codes = @() + + # Text attributes + if ($this.Bold) { $codes += "1" } + if ($this.Dim) { $codes += "2" } + if ($this.Italic) { $codes += "3" } + if ($this.Underline) { $codes += "4" } + if ($this.Strike) { $codes += "9" } + + # Build the complete escape sequence + $result = "" + + if ($codes.Count -gt 0) { + $result += "`e[$($codes -join ';')m" + } + + if ($this.ForegroundColor) { + $result += $this.ForegroundColor.GetForegroundCode() + } + + if ($this.BackgroundColor) { + $result += $this.BackgroundColor.GetBackgroundCode() + } + + return $result + } + + # Reset code + static [string] GetResetCode() { + return "`e[0m" + } + + # Apply style to text + [string] Apply([string]$text) { + $code = $this.GetAnsiCode() + if ($code) { + return "$code$text$([RichStyle]::GetResetCode())" + } + return $text + } + + # Combine with another style + [RichStyle] Combine([RichStyle]$other) { + $result = [RichStyle]::new() + + # Copy from this style + $result.ForegroundColor = $this.ForegroundColor + $result.BackgroundColor = $this.BackgroundColor + $result.Bold = $this.Bold + $result.Italic = $this.Italic + $result.Underline = $this.Underline + $result.Strike = $this.Strike + $result.Dim = $this.Dim + + # Override with other style + if ($other.ForegroundColor) { $result.ForegroundColor = $other.ForegroundColor } + if ($other.BackgroundColor) { $result.BackgroundColor = $other.BackgroundColor } + if ($other.Bold) { $result.Bold = $true } + if ($other.Italic) { $result.Italic = $true } + if ($other.Underline) { $result.Underline = $true } + if ($other.Strike) { $result.Strike = $true } + if ($other.Dim) { $result.Dim = $true } + + return $result + } +} + +# Helper function to create styles easily +function New-RichStyle { + param( + [RichColor]$ForegroundColor, + [RichColor]$BackgroundColor, + [switch]$Bold, + [switch]$Italic, + [switch]$Underline, + [switch]$Strike, + [switch]$Dim + ) + + $style = [RichStyle]::new() + + if ($ForegroundColor) { $style.ForegroundColor = $ForegroundColor } + if ($BackgroundColor) { $style.BackgroundColor = $BackgroundColor } + if ($Bold) { $style.Bold = $true } + if ($Italic) { $style.Italic = $true } + if ($Underline) { $style.Underline = $true } + if ($Strike) { $style.Strike = $true } + if ($Dim) { $style.Dim = $true } + + return $style +} diff --git a/RichPS7/Classes/Table.ps1 b/RichPS7/Classes/Table.ps1 new file mode 100644 index 00000000..2a430260 --- /dev/null +++ b/RichPS7/Classes/Table.ps1 @@ -0,0 +1,285 @@ +class RichTable { + [string]$Title + [System.Collections.ArrayList]$Columns + [System.Collections.ArrayList]$Rows + [string]$BoxStyle + [RichStyle]$HeaderStyle + [RichStyle]$RowStyle + [bool]$ShowHeader + [bool]$ShowLines + [hashtable]$BoxChars + + RichTable() { + $this.Columns = [System.Collections.ArrayList]::new() + $this.Rows = [System.Collections.ArrayList]::new() + $this.BoxStyle = "rounded" + $this.ShowHeader = $true + $this.ShowLines = $false + $this.InitializeBoxChars() + + # Default header style + $this.HeaderStyle = [RichStyle]::new() + $this.HeaderStyle.Bold = $true + $this.HeaderStyle.ForegroundColor = New-RichColor 'cyan' + } + + hidden [void] InitializeBoxChars() { + # Box drawing characters for different styles + $this.BoxChars = @{ + 'rounded' = @{ + 'top_left' = '╭' + 'top_right' = '╮' + 'bottom_left' = '╰' + 'bottom_right' = '╯' + 'horizontal' = '─' + 'vertical' = '│' + 'cross' = '┼' + 'top_join' = '┬' + 'bottom_join' = '┴' + 'left_join' = '├' + 'right_join' = '┤' + } + 'square' = @{ + 'top_left' = '┌' + 'top_right' = '┐' + 'bottom_left' = '└' + 'bottom_right' = '┘' + 'horizontal' = '─' + 'vertical' = '│' + 'cross' = '┼' + 'top_join' = '┬' + 'bottom_join' = '┴' + 'left_join' = '├' + 'right_join' = '┤' + } + 'double' = @{ + 'top_left' = '╔' + 'top_right' = '╗' + 'bottom_left' = '╚' + 'bottom_right' = '╝' + 'horizontal' = '═' + 'vertical' = '║' + 'cross' = '╬' + 'top_join' = '╦' + 'bottom_join' = '╩' + 'left_join' = '╠' + 'right_join' = '╣' + } + 'simple' = @{ + 'top_left' = '+' + 'top_right' = '+' + 'bottom_left' = '+' + 'bottom_right' = '+' + 'horizontal' = '-' + 'vertical' = '|' + 'cross' = '+' + 'top_join' = '+' + 'bottom_join' = '+' + 'left_join' = '+' + 'right_join' = '+' + } + } + } + + # Add a column + [void] AddColumn([string]$name) { + $this.AddColumn($name, @{}) + } + + [void] AddColumn([string]$name, [hashtable]$options) { + $column = @{ + 'Name' = $name + 'Width' = if ($options.Width) { $options.Width } else { $null } + 'Align' = if ($options.Align) { $options.Align } else { 'left' } + 'Style' = if ($options.Style) { $options.Style } else { $null } + } + [void]$this.Columns.Add($column) + } + + # Add a row + [void] AddRow([array]$cells) { + [void]$this.Rows.Add($cells) + } + + # Calculate column widths + hidden [array] CalculateWidths() { + $widths = @() + + for ($i = 0; $i -lt $this.Columns.Count; $i++) { + $column = $this.Columns[$i] + + if ($column.Width) { + $widths += $column.Width + } else { + # Calculate based on content + $maxWidth = $column.Name.Length + + foreach ($row in $this.Rows) { + if ($i -lt $row.Count) { + $cellWidth = $row[$i].ToString().Length + if ($cellWidth -gt $maxWidth) { + $maxWidth = $cellWidth + } + } + } + + $widths += $maxWidth + } + } + + return $widths + } + + # Align text within a cell + hidden [string] AlignText([string]$text, [int]$width, [string]$align) { + $text = $text.ToString() + $len = $text.Length + + if ($len -ge $width) { + return $text.Substring(0, $width) + } + + $padding = $width - $len + + switch ($align) { + 'center' { + $leftPad = [Math]::Floor($padding / 2) + $rightPad = $padding - $leftPad + return (' ' * $leftPad) + $text + (' ' * $rightPad) + } + 'right' { + return (' ' * $padding) + $text + } + default { # left + return $text + (' ' * $padding) + } + } + } + + # Render the table + [string] Render() { + $output = [System.Text.StringBuilder]::new() + $widths = $this.CalculateWidths() + $box = $this.BoxChars[$this.BoxStyle] + + # Top border + [void]$output.Append($box.top_left) + for ($i = 0; $i -lt $widths.Count; $i++) { + [void]$output.Append($box.horizontal * ($widths[$i] + 2)) + if ($i -lt $widths.Count - 1) { + [void]$output.Append($box.top_join) + } + } + [void]$output.AppendLine($box.top_right) + + # Title (if present) + if ($this.Title) { + $totalWidth = ($widths | Measure-Object -Sum).Sum + ($widths.Count * 2) + ($widths.Count - 1) + $titleText = $this.AlignText($this.Title, $totalWidth, 'center') + [void]$output.Append($box.vertical) + [void]$output.Append($titleText) + [void]$output.AppendLine($box.vertical) + + # Separator after title + [void]$output.Append($box.left_join) + for ($i = 0; $i -lt $widths.Count; $i++) { + [void]$output.Append($box.horizontal * ($widths[$i] + 2)) + if ($i -lt $widths.Count - 1) { + [void]$output.Append($box.cross) + } + } + [void]$output.AppendLine($box.right_join) + } + + # Header row + if ($this.ShowHeader) { + [void]$output.Append($box.vertical) + for ($i = 0; $i -lt $this.Columns.Count; $i++) { + $column = $this.Columns[$i] + $text = $this.AlignText($column.Name, $widths[$i], $column.Align) + + if ($this.HeaderStyle) { + $text = $this.HeaderStyle.Apply($text) + } + + [void]$output.Append(" $text ") + [void]$output.Append($box.vertical) + } + [void]$output.AppendLine() + + # Separator after header + [void]$output.Append($box.left_join) + for ($i = 0; $i -lt $widths.Count; $i++) { + [void]$output.Append($box.horizontal * ($widths[$i] + 2)) + if ($i -lt $widths.Count - 1) { + [void]$output.Append($box.cross) + } + } + [void]$output.AppendLine($box.right_join) + } + + # Data rows + for ($rowIdx = 0; $rowIdx -lt $this.Rows.Count; $rowIdx++) { + $row = $this.Rows[$rowIdx] + + [void]$output.Append($box.vertical) + for ($i = 0; $i -lt $this.Columns.Count; $i++) { + $column = $this.Columns[$i] + $cellValue = if ($i -lt $row.Count) { $row[$i] } else { "" } + $text = $this.AlignText($cellValue, $widths[$i], $column.Align) + + if ($column.Style) { + $text = $column.Style.Apply($text) + } elseif ($this.RowStyle) { + $text = $this.RowStyle.Apply($text) + } + + [void]$output.Append(" $text ") + [void]$output.Append($box.vertical) + } + [void]$output.AppendLine() + + # Row separator (if ShowLines is true) + if ($this.ShowLines -and $rowIdx -lt $this.Rows.Count - 1) { + [void]$output.Append($box.left_join) + for ($i = 0; $i -lt $widths.Count; $i++) { + [void]$output.Append($box.horizontal * ($widths[$i] + 2)) + if ($i -lt $widths.Count - 1) { + [void]$output.Append($box.cross) + } + } + [void]$output.AppendLine($box.right_join) + } + } + + # Bottom border + [void]$output.Append($box.bottom_left) + for ($i = 0; $i -lt $widths.Count; $i++) { + [void]$output.Append($box.horizontal * ($widths[$i] + 2)) + if ($i -lt $widths.Count - 1) { + [void]$output.Append($box.bottom_join) + } + } + [void]$output.Append($box.bottom_right) + + return $output.ToString() + } +} + +# Helper function to create and render a table +function New-RichTable { + param( + [string]$Title, + [string]$BoxStyle = "rounded", + [switch]$ShowLines, + [switch]$HideHeader + ) + + $table = [RichTable]::new() + if ($Title) { $table.Title = $Title } + $table.BoxStyle = $BoxStyle + $table.ShowLines = $ShowLines.IsPresent + $table.ShowHeader = -not $HideHeader.IsPresent + + return $table +} diff --git a/RichPS7/Examples/01-Colors.ps1 b/RichPS7/Examples/01-Colors.ps1 new file mode 100644 index 00000000..91baac97 --- /dev/null +++ b/RichPS7/Examples/01-Colors.ps1 @@ -0,0 +1,65 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 + +# Example 1: Using Colors with RichPS7 + +# Import the module +Import-Module "$PSScriptRoot/../RichPS7.psd1" -Force + +Write-Host "`n=== RichPS7 Color Examples ===`n" -ForegroundColor Cyan + +# Example 1: Named colors +Write-Rich "This is red text" -Color (New-RichColor 'red') +Write-Rich "This is green text" -Color (New-RichColor 'green') +Write-Rich "This is blue text" -Color (New-RichColor 'blue') +Write-Rich "This is yellow text" -Color (New-RichColor 'yellow') +Write-Rich "This is magenta text" -Color (New-RichColor 'magenta') +Write-Rich "This is cyan text" -Color (New-RichColor 'cyan') + +Write-Host "`n--- Bright Colors ---`n" + +Write-Rich "This is bright red" -Color (New-RichColor 'bright_red') +Write-Rich "This is bright green" -Color (New-RichColor 'bright_green') +Write-Rich "This is bright blue" -Color (New-RichColor 'bright_blue') + +Write-Host "`n--- Hex Colors ---`n" + +Write-Rich "This is orange (#FFA500)" -Color (New-RichColor '#FFA500') +Write-Rich "This is purple (#9B59B6)" -Color (New-RichColor '#9B59B6') +Write-Rich "This is pink (#FF69B4)" -Color (New-RichColor '#FF69B4') + +Write-Host "`n--- RGB Colors ---`n" + +$color1 = [RichColor]::new(255, 100, 50) # Orange-red +Write-Rich "Custom RGB: (255, 100, 50)" -Color $color1 + +$color2 = [RichColor]::new(50, 200, 150) # Teal +Write-Rich "Custom RGB: (50, 200, 150)" -Color $color2 + +Write-Host "`n--- Text Styles ---`n" + +Write-Rich "Bold text" -Bold +Write-Rich "Italic text" -Italic +Write-Rich "Underlined text" -Underline +Write-Rich "Bold + Italic + Underline" -Bold -Italic -Underline + +Write-Host "`n--- Combined Styles ---`n" + +$style1 = New-RichStyle -ForegroundColor (New-RichColor 'cyan') -Bold +Write-Rich "Bold Cyan Text" -Style $style1 + +$style2 = New-RichStyle -ForegroundColor (New-RichColor 'yellow') -BackgroundColor (New-RichColor 'blue') -Bold +Write-Rich "Yellow on Blue Background" -Style $style2 + +$style3 = New-RichStyle -ForegroundColor (New-RichColor '#FF1493') -Italic -Underline +Write-Rich "Hot Pink with Italic and Underline" -Style $style3 + +Write-Host "`n--- Using the Console Object ---`n" + +$console = Get-RichConsole +$console.Info("This is an info message") +$console.Warning("This is a warning message") +$console.Error("This is an error message") +$console.Success("This is a success message") + +Write-Host "" diff --git a/RichPS7/Examples/02-Tables.ps1 b/RichPS7/Examples/02-Tables.ps1 new file mode 100644 index 00000000..329743fd --- /dev/null +++ b/RichPS7/Examples/02-Tables.ps1 @@ -0,0 +1,83 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 + +# Example 2: Using Tables with RichPS7 + +# Import the module +Import-Module "$PSScriptRoot/../RichPS7.psd1" -Force + +Write-Host "`n=== RichPS7 Table Examples ===`n" -ForegroundColor Cyan + +# Example 1: Simple table +Write-Host "--- Simple Table ---`n" + +$table1 = New-RichTable -Title "System Information" +$table1.AddColumn("Property") +$table1.AddColumn("Value") + +$table1.AddRow(@("OS", $PSVersionTable.OS)) +$table1.AddRow(@("PowerShell Version", $PSVersionTable.PSVersion)) +$table1.AddRow(@("Edition", $PSVersionTable.PSEdition)) +$table1.AddRow(@("Platform", $PSVersionTable.Platform)) + +Write-Host $table1.Render() + +# Example 2: Table with different box styles +Write-Host "`n--- Different Box Styles ---`n" + +foreach ($style in @('rounded', 'square', 'double', 'simple')) { + $table = New-RichTable -BoxStyle $style + $table.AddColumn("Style") + $table.AddColumn("Description") + $table.AddRow(@($style, "This table uses the '$style' box style")) + Write-Host $table.Render() + Write-Host "" +} + +# Example 3: Table with alignment +Write-Host "`n--- Table with Alignment ---`n" + +$table2 = New-RichTable -Title "Product Pricing" +$table2.AddColumn("Product", @{ Align = 'left' }) +$table2.AddColumn("Price", @{ Align = 'right' }) +$table2.AddColumn("Quantity", @{ Align = 'center' }) + +$table2.AddRow(@("Widget", "$19.99", "42")) +$table2.AddRow(@("Gadget", "$29.99", "17")) +$table2.AddRow(@("Doohickey", "$9.99", "103")) +$table2.AddRow(@("Thingamajig", "$49.99", "8")) + +Write-Host $table2.Render() + +# Example 4: Table with row lines +Write-Host "`n--- Table with Row Lines ---`n" + +$table3 = New-RichTable -Title "Server Status" -ShowLines +$table3.AddColumn("Server") +$table3.AddColumn("Status") +$table3.AddColumn("Uptime") + +$table3.AddRow(@("web-01", "Running", "45d 12h")) +$table3.AddRow(@("web-02", "Running", "32d 8h")) +$table3.AddRow(@("db-01", "Running", "127d 4h")) +$table3.AddRow(@("cache-01", "Stopped", "0d 0h")) + +Write-Host $table3.Render() + +# Example 5: Process information table +Write-Host "`n--- Process Information ---`n" + +$table4 = New-RichTable -Title "Top Processes by Memory" -BoxStyle "double" +$table4.AddColumn("Process", @{ Align = 'left' }) +$table4.AddColumn("PID", @{ Align = 'right' }) +$table4.AddColumn("Memory (MB)", @{ Align = 'right' }) + +$processes = Get-Process | Sort-Object -Property WS -Descending | Select-Object -First 5 +foreach ($proc in $processes) { + $memMB = [math]::Round($proc.WS / 1MB, 2) + $table4.AddRow(@($proc.ProcessName, $proc.Id, $memMB)) +} + +Write-Host $table4.Render() + +Write-Host "" diff --git a/RichPS7/Examples/03-Panels.ps1 b/RichPS7/Examples/03-Panels.ps1 new file mode 100644 index 00000000..e56d24c1 --- /dev/null +++ b/RichPS7/Examples/03-Panels.ps1 @@ -0,0 +1,93 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 + +# Example 3: Using Panels with RichPS7 + +# Import the module +Import-Module "$PSScriptRoot/../RichPS7.psd1" -Force + +Write-Host "`n=== RichPS7 Panel Examples ===`n" -ForegroundColor Cyan + +# Example 1: Simple panel +Write-Host "--- Simple Panel ---`n" + +$panel1 = New-RichPanel -Content "This is a simple panel with default styling." +Write-Host $panel1.Render() + +# Example 2: Panel with title +Write-Host "`n--- Panel with Title ---`n" + +$panel2 = New-RichPanel -Content "This panel has a title!" -Title "Welcome" +Write-Host $panel2.Render() + +# Example 3: Different box styles +Write-Host "`n--- Different Box Styles ---`n" + +foreach ($style in @('rounded', 'square', 'double', 'heavy')) { + $content = "This panel uses the '$style' box style." + $panel = New-RichPanel -Content $content -Title $style.ToUpper() -BoxStyle $style + Write-Host $panel.Render() + Write-Host "" +} + +# Example 4: Panel with title alignment +Write-Host "`n--- Title Alignment ---`n" + +$panel3 = New-RichPanel -Content "Left-aligned title" -Title "LEFT" -TitleAlign "left" +Write-Host $panel3.Render() +Write-Host "" + +$panel4 = New-RichPanel -Content "Center-aligned title" -Title "CENTER" -TitleAlign "center" +Write-Host $panel4.Render() +Write-Host "" + +$panel5 = New-RichPanel -Content "Right-aligned title" -Title "RIGHT" -TitleAlign "right" +Write-Host $panel5.Render() + +# Example 5: Panel with subtitle +Write-Host "`n--- Panel with Subtitle ---`n" + +$panel6 = New-RichPanel -Content "This panel has both a title and a subtitle." -Title "Main Title" -Subtitle "Subtitle here" +Write-Host $panel6.Render() + +# Example 6: Multi-line content +Write-Host "`n--- Multi-line Content ---`n" + +$content = @" +This panel contains multiple lines of text. +Each line is displayed within the panel. +You can include as many lines as you want! + +This is useful for displaying: +- Instructions +- Status messages +- Help text +- And much more! +"@ + +$panel7 = New-RichPanel -Content $content -Title "Multi-line Panel" -BoxStyle "double" +Write-Host $panel7.Render() + +# Example 7: Styled panels +Write-Host "`n--- Styled Panels ---`n" + +$titleStyle = New-RichStyle -ForegroundColor (New-RichColor 'yellow') -Bold +$borderStyle = New-RichStyle -ForegroundColor (New-RichColor 'cyan') +$contentStyle = New-RichStyle -ForegroundColor (New-RichColor 'green') + +$panel8 = New-RichPanel -Content "This panel has custom styles!" -Title "Styled Panel" -TitleStyle $titleStyle -BorderStyle $borderStyle -ContentStyle $contentStyle +Write-Host $panel8.Render() + +# Example 8: Information panel +Write-Host "`n--- Information Panel ---`n" + +$info = @" +PowerShell Version: $($PSVersionTable.PSVersion) +OS: $($PSVersionTable.OS) +Edition: $($PSVersionTable.PSEdition) +"@ + +$panel9 = New-RichPanel -Content $info -Title "System Information" -BoxStyle "rounded" -Padding 2 +Write-Host $panel9.Render() + +Write-Host "" diff --git a/RichPS7/Examples/04-Progress.ps1 b/RichPS7/Examples/04-Progress.ps1 new file mode 100644 index 00000000..619d5906 --- /dev/null +++ b/RichPS7/Examples/04-Progress.ps1 @@ -0,0 +1,136 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 + +# Example 4: Using Progress Bars with RichPS7 + +# Import the module +Import-Module "$PSScriptRoot/../RichPS7.psd1" -Force + +Write-Host "`n=== RichPS7 Progress Examples ===`n" -ForegroundColor Cyan + +# Example 1: Simple progress with Invoke-RichProgress +Write-Host "--- Simple Progress Example ---`n" + +$items = 1..20 +Invoke-RichProgress -Description "Processing items" -Collection $items -ScriptBlock { + param($item) + # Simulate some work + Start-Sleep -Milliseconds 100 +} + +Write-Host "`n--- Multiple Tasks ---`n" + +# Example 2: Multiple progress bars +$progress = New-RichProgress + +# Add multiple tasks +$task1 = $progress.AddTask("Downloading files", 50) +$task2 = $progress.AddTask("Processing data", 100) +$task3 = $progress.AddTask("Uploading results", 30) + +$progress.Start() + +try { + # Simulate progress on multiple tasks + $random = [Random]::new() + + while (-not $progress.IsComplete()) { + # Randomly advance tasks + if (-not $task1.IsComplete) { + $task1.Advance($random.Next(1, 5)) + } + + if (-not $task2.IsComplete) { + $task2.Advance($random.Next(1, 3)) + } + + if (-not $task3.IsComplete) { + $task3.Advance($random.Next(1, 4)) + } + + # Refresh display + $progress.Refresh() + + # Small delay + Start-Sleep -Milliseconds 50 + } + + # Show final state + $progress.Refresh() + Start-Sleep -Milliseconds 500 + +} finally { + $progress.Stop() +} + +Write-Host "`n--- File Processing Simulation ---`n" + +# Example 3: Simulating file processing +$files = @( + "document1.pdf" + "document2.pdf" + "document3.pdf" + "spreadsheet1.xlsx" + "spreadsheet2.xlsx" + "presentation1.pptx" + "presentation2.pptx" + "image1.jpg" + "image2.jpg" + "image3.jpg" +) + +Invoke-RichProgress -Description "Converting files" -Collection $files -ScriptBlock { + param($file) + # Simulate file processing time based on extension + $delay = switch -Regex ($file) { + '\.pdf$' { 200 } + '\.xlsx$' { 150 } + '\.pptx$' { 180 } + '\.jpg$' { 50 } + default { 100 } + } + Start-Sleep -Milliseconds $delay +} + +Write-Host "`n--- Custom Progress Bar ---`n" + +# Example 4: Single task with manual updates +$progress2 = New-RichProgress +$task = $progress2.AddTask("Installing packages", 10) + +# Customize colors +$task.CompleteColor = New-RichColor 'cyan' +$task.RemainingColor = New-RichColor 'bright_black' + +$progress2.Start() + +try { + $packages = @( + "core-utils" + "network-tools" + "security-suite" + "dev-dependencies" + "runtime-libraries" + "documentation" + "examples" + "tests" + "benchmarks" + "tools" + ) + + foreach ($pkg in $packages) { + $task.Description = "Installing $pkg".PadRight(30) + $progress2.Refresh() + Start-Sleep -Milliseconds ([Random]::new().Next(100, 300)) + $task.Advance(1) + } + + $task.Description = "Installation complete!".PadRight(30) + $progress2.Refresh() + Start-Sleep -Milliseconds 500 + +} finally { + $progress2.Stop() +} + +Write-Host "" diff --git a/RichPS7/Examples/05-Complete-Demo.ps1 b/RichPS7/Examples/05-Complete-Demo.ps1 new file mode 100644 index 00000000..1615975a --- /dev/null +++ b/RichPS7/Examples/05-Complete-Demo.ps1 @@ -0,0 +1,197 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 + +# Example 5: Complete RichPS7 Demo - All Features + +# Import the module +Import-Module "$PSScriptRoot/../RichPS7.psd1" -Force + +$console = Get-RichConsole + +# Clear screen +$console.Clear() + +# Title +$console.NewLine() +$titleStyle = New-RichStyle -ForegroundColor (New-RichColor 'cyan') -Bold +Write-Rich "╔══════════════════════════════════════════════════════════╗" -Style $titleStyle +Write-Rich "║ RichPS7 - Complete Feature Demo ║" -Style $titleStyle +Write-Rich "║ Beautiful Terminal Formatting for PowerShell 7 ║" -Style $titleStyle +Write-Rich "╚══════════════════════════════════════════════════════════╝" -Style $titleStyle +$console.NewLine() + +# Introduction panel +$intro = @" +Welcome to RichPS7! + +This demo showcases all the core features: +• Colors and text styling +• Tables with various formats +• Panels with borders +• Progress bars with ETA +"@ + +$panel = New-RichPanel -Content $intro -Title "Introduction" -BoxStyle "rounded" +Write-Host $panel.Render() + +Start-Sleep -Seconds 2 + +# Rule separator +$console.NewLine() +$console.Rule("FEATURE 1: Colors & Styles", (New-RichColor 'yellow')) +$console.NewLine() + +# Color demonstration +Write-Rich "Bold text example" -Bold +Write-Rich "Italic text example" -Italic +Write-Rich "Underlined text example" -Underline + +$console.NewLine() +Write-Rich "Rainbow colors:" -Bold + +$colors = @('red', 'yellow', 'green', 'cyan', 'blue', 'magenta') +foreach ($color in $colors) { + Write-Rich "● " -Color (New-RichColor $color) -NoNewline +} +$console.NewLine(2) + +Start-Sleep -Seconds 2 + +# Table demonstration +$console.Rule("FEATURE 2: Tables", (New-RichColor 'green')) +$console.NewLine() + +$table = New-RichTable -Title "System Resources" -BoxStyle "double" +$table.AddColumn("Resource", @{ Align = 'left' }) +$table.AddColumn("Usage", @{ Align = 'right' }) +$table.AddColumn("Status", @{ Align = 'center' }) + +# Get actual system info +$cpu = Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue +$cpuUsage = if ($cpu) { [math]::Round($cpu.CounterSamples.CookedValue, 1) } else { "N/A" } + +$memory = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue +if (-not $memory) { + # Try Linux approach + $memInfo = Get-Content /proc/meminfo -ErrorAction SilentlyContinue + if ($memInfo) { + $total = ($memInfo | Select-String "MemTotal" | ForEach-Object { ($_ -split '\s+')[1] }) / 1024 + $available = ($memInfo | Select-String "MemAvailable" | ForEach-Object { ($_ -split '\s+')[1] }) / 1024 + $memUsage = [math]::Round((($total - $available) / $total) * 100, 1) + } else { + $memUsage = "N/A" + } +} else { + $memUsage = [math]::Round((($memory.TotalVisibleMemorySize - $memory.FreePhysicalMemory) / $memory.TotalVisibleMemorySize) * 100, 1) +} + +$diskUsage = "N/A" +try { + $disk = Get-PSDrive -Name C -ErrorAction SilentlyContinue + if ($disk) { + $diskUsage = [math]::Round(($disk.Used / ($disk.Used + $disk.Free)) * 100, 1) + } else { + # Try Linux root + $disk = Get-PSDrive -Name / -ErrorAction SilentlyContinue + if ($disk) { + $diskUsage = [math]::Round(($disk.Used / ($disk.Used + $disk.Free)) * 100, 1) + } + } +} catch { + $diskUsage = "N/A" +} + +$table.AddRow(@("CPU", "$cpuUsage%", "OK")) +$table.AddRow(@("Memory", "$memUsage%", "OK")) +$table.AddRow(@("Disk", "$diskUsage%", "OK")) +$table.AddRow(@("Network", "12.5 Mbps", "OK")) + +Write-Host $table.Render() + +$console.NewLine() +Start-Sleep -Seconds 2 + +# Panel demonstration +$console.Rule("FEATURE 3: Panels", (New-RichColor 'magenta')) +$console.NewLine() + +$infoContent = @" +Current Time: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') +User: $env:USER +PowerShell: $($PSVersionTable.PSVersion) +"@ + +$infoPanel = New-RichPanel -Content $infoContent -Title "Session Info" -BoxStyle "heavy" -TitleAlign "center" +Write-Host $infoPanel.Render() + +$console.NewLine() +Start-Sleep -Seconds 2 + +# Progress demonstration +$console.Rule("FEATURE 4: Progress Bars", (New-RichColor 'cyan')) +$console.NewLine() + +Write-Host "Simulating a download process...`n" + +$progress = New-RichProgress + +$download = $progress.AddTask("Downloading package", 100) +$extract = $progress.AddTask("Extracting files", 50) +$install = $progress.AddTask("Installing", 25) + +$download.CompleteColor = New-RichColor 'green' +$extract.CompleteColor = New-RichColor 'cyan' +$install.CompleteColor = New-RichColor 'yellow' + +$progress.Start() + +try { + # Simulate download + while ($download.Current -lt $download.Total) { + $download.Advance([Random]::new().Next(2, 8)) + $progress.Refresh() + Start-Sleep -Milliseconds 30 + } + + # Simulate extract + while ($extract.Current -lt $extract.Total) { + $extract.Advance([Random]::new().Next(1, 5)) + $progress.Refresh() + Start-Sleep -Milliseconds 50 + } + + # Simulate install + while ($install.Current -lt $install.Total) { + $install.Advance([Random]::new().Next(1, 3)) + $progress.Refresh() + Start-Sleep -Milliseconds 80 + } + + $progress.Refresh() + Start-Sleep -Milliseconds 500 + +} finally { + $progress.Stop() +} + +# Completion message +$console.NewLine() +$console.Rule("Demo Complete!", (New-RichColor 'green')) +$console.NewLine() + +$outro = @" +Thank you for trying RichPS7! + +To get started: + Import-Module RichPS7 + Get-Command -Module RichPS7 + +Check out the Examples directory for more demos. +"@ + +$outroPanel = New-RichPanel -Content $outro -Title "Next Steps" -Subtitle "Happy Scripting!" -BoxStyle "rounded" +Write-Host $outroPanel.Render() + +$console.NewLine() +$console.Success("Demo completed successfully!") +$console.NewLine() diff --git a/RichPS7/QUICKSTART.md b/RichPS7/QUICKSTART.md new file mode 100644 index 00000000..7d4f0556 --- /dev/null +++ b/RichPS7/QUICKSTART.md @@ -0,0 +1,286 @@ +# RichPS7 Quick Start Guide + +Get up and running with RichPS7 in 5 minutes! + +## Installation + +```powershell +# Clone or download, then import +Import-Module ./RichPS7/RichPS7.psd1 +``` + +## 5-Minute Tutorial + +### 1. Print Colored Text (30 seconds) + +```powershell +# Simple colored text +Write-Rich "Success!" -Color (New-RichColor 'green') -Bold + +# Use the console for themed messages +$console = Get-RichConsole +$console.Info("Information message") +$console.Warning("Warning message") +$console.Error("Error message") +$console.Success("Success message") +``` + +### 2. Create a Table (1 minute) + +```powershell +# Create a table +$table = New-RichTable -Title "My First Table" + +# Add columns +$table.AddColumn("Name") +$table.AddColumn("Status") +$table.AddColumn("Count") + +# Add rows +$table.AddRow(@("Item 1", "Active", "42")) +$table.AddRow(@("Item 2", "Inactive", "17")) +$table.AddRow(@("Item 3", "Active", "99")) + +# Render it +Write-Host $table.Render() +``` + +### 3. Create a Panel (1 minute) + +```powershell +# Simple panel +$panel = New-RichPanel -Content "Hello from RichPS7!" -Title "Welcome" +Write-Host $panel.Render() + +# Multi-line panel +$content = @" +This is a panel with multiple lines. +You can include any text you want. +Perfect for highlighting important information! +"@ + +$panel = New-RichPanel -Content $content -Title "Info" -BoxStyle "double" +Write-Host $panel.Render() +``` + +### 4. Show Progress (1 minute) + +```powershell +# Simple progress bar +$items = 1..50 +Invoke-RichProgress -Description "Processing" -Collection $items -ScriptBlock { + param($item) + Start-Sleep -Milliseconds 50 # Your work here +} +``` + +### 5. Combine Everything (1.5 minutes) + +```powershell +# Clear screen and show a complete example +$console = Get-RichConsole +$console.Clear() + +# Title +$console.Rule("System Report", (New-RichColor 'cyan')) +$console.NewLine() + +# Info panel +$info = @" +Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') +PowerShell: $($PSVersionTable.PSVersion) +"@ +$panel = New-RichPanel -Content $info -Title "Report Info" +Write-Host $panel.Render() +$console.NewLine() + +# Data table +$table = New-RichTable -Title "System Processes" -BoxStyle "rounded" +$table.AddColumn("Process") +$table.AddColumn("PID", @{ Align = 'right' }) +$table.AddColumn("Memory (MB)", @{ Align = 'right' }) + +Get-Process | Select-Object -First 5 | ForEach-Object { + $memMB = [math]::Round($_.WS / 1MB, 1) + $table.AddRow(@($_.ProcessName, $_.Id, $memMB)) +} + +Write-Host $table.Render() +$console.NewLine() + +# Success message +$console.Success("Report generated successfully!") +``` + +## Next Steps + +### Explore Examples + +Run the complete demo: + +```powershell +./RichPS7/Examples/05-Complete-Demo.ps1 +``` + +Or explore individual examples: +- `01-Colors.ps1` - Colors and text styling +- `02-Tables.ps1` - Table formatting +- `03-Panels.ps1` - Panel styles +- `04-Progress.ps1` - Progress bars + +### Customize Your Output + +#### Box Styles + +Tables and panels support multiple box styles: + +```powershell +# Try different styles +foreach ($style in @('rounded', 'square', 'double', 'heavy', 'simple')) { + $panel = New-RichPanel -Content "Using $style style" -Title $style -BoxStyle $style + Write-Host $panel.Render() +} +``` + +#### Color Schemes + +Create custom color schemes: + +```powershell +# Define your colors +$primary = New-RichColor '#3498db' # Blue +$success = New-RichColor '#2ecc71' # Green +$warning = New-RichColor '#f39c12' # Orange +$danger = New-RichColor '#e74c3c' # Red + +# Use them +Write-Rich "Primary action" -Color $primary -Bold +Write-Rich "Success!" -Color $success +Write-Rich "Warning!" -Color $warning +Write-Rich "Error!" -Color $danger +``` + +#### Style Presets + +Create reusable styles: + +```powershell +# Define styles +$headerStyle = New-RichStyle -ForegroundColor (New-RichColor 'cyan') -Bold +$errorStyle = New-RichStyle -ForegroundColor (New-RichColor 'red') -Bold -Underline +$codeStyle = New-RichStyle -ForegroundColor (New-RichColor 'green') -BackgroundColor (New-RichColor 'black') + +# Use them +Write-Rich "=== Header ===" -Style $headerStyle +Write-Rich "Error occurred!" -Style $errorStyle +Write-Rich "code snippet" -Style $codeStyle +``` + +### Real-World Use Cases + +#### Script Progress Tracking + +```powershell +function Process-Files { + param([string[]]$Files) + + Invoke-RichProgress -Description "Processing files" -Collection $Files -ScriptBlock { + param($file) + # Process each file + # ... + } +} +``` + +#### Status Reports + +```powershell +function Show-SystemStatus { + $console = Get-RichConsole + + $console.Rule("System Status Report", (New-RichColor 'cyan')) + $console.NewLine() + + # CPU Check + if ($cpuUsage -lt 80) { + $console.Success("CPU: OK ($cpuUsage%)") + } else { + $console.Warning("CPU: High ($cpuUsage%)") + } + + # Memory Check + if ($memUsage -lt 90) { + $console.Success("Memory: OK ($memUsage%)") + } else { + $console.Error("Memory: Critical ($memUsage%)") + } +} +``` + +#### Configuration Display + +```powershell +function Show-Config { + param($Config) + + $table = New-RichTable -Title "Configuration" -BoxStyle "double" + $table.AddColumn("Setting") + $table.AddColumn("Value") + + foreach ($key in $Config.Keys) { + $table.AddRow(@($key, $Config[$key])) + } + + Write-Host $table.Render() +} +``` + +## Tips & Tricks + +1. **Auto-width tables**: Don't specify column widths - they'll auto-size to content +2. **Multi-line panels**: Use here-strings (`@"..."@`) for multi-line panel content +3. **Console theme**: Customize the global console's theme colors +4. **Progress ETA**: Progress bars automatically calculate and display ETA +5. **Style combinations**: You can combine multiple style attributes (bold + italic + color) + +## Getting Help + +```powershell +# List all available functions +Get-Command -Module RichPS7 + +# Get help for a specific function +Get-Help New-RichTable -Full +Get-Help Write-Rich -Examples +``` + +## Troubleshooting + +### Colors not showing? + +Make sure you're using PowerShell 7+ and a modern terminal: + +```powershell +# Check PowerShell version +$PSVersionTable.PSVersion + +# Should be 7.0 or higher +``` + +### Module not loading? + +```powershell +# Try explicit import +Import-Module ./RichPS7/RichPS7.psd1 -Force -Verbose + +# Check for errors +$Error[0] | Format-List -Force +``` + +## Ready to Learn More? + +Check out the full [README.md](README.md) for complete API documentation and advanced features! + +--- + +Happy scripting with RichPS7! 🎨✨ diff --git a/RichPS7/README.md b/RichPS7/README.md new file mode 100644 index 00000000..ce8f1c52 --- /dev/null +++ b/RichPS7/README.md @@ -0,0 +1,384 @@ +# RichPS7 + +**Beautiful terminal formatting for PowerShell 7** + +RichPS7 is a PowerShell 7 port of Python's popular [Rich](https://github.com/Textualize/rich) library. It brings beautiful terminal formatting, colors, tables, panels, and progress bars to your PowerShell scripts. + +![PowerShell Version](https://img.shields.io/badge/PowerShell-7.0+-blue.svg) +![License](https://img.shields.io/badge/license-MIT-green.svg) + +## Features + +- 🎨 **Rich Colors**: RGB, hex codes, and named colors with full ANSI support +- ✨ **Text Styling**: Bold, italic, underline, strikethrough, and dim text +- 📊 **Tables**: Beautiful tables with multiple box styles and alignment options +- 📦 **Panels**: Bordered panels with titles, subtitles, and custom styles +- 📈 **Progress Bars**: Live-updating progress bars with ETA estimation +- 🖥️ **Console**: Powerful console abstraction with themed output +- 🌈 **Cross-platform**: Works on Windows, macOS, and Linux + +## Requirements + +- PowerShell 7.0 or higher +- A terminal with ANSI color support (Windows Terminal, iTerm2, etc.) + +## Installation + +### Option 1: Direct Import + +1. Clone or download this repository +2. Import the module: + +```powershell +Import-Module ./RichPS7/RichPS7.psd1 +``` + +### Option 2: Install to PowerShell Modules Directory + +1. Copy the `RichPS7` folder to your PowerShell modules directory: + +```powershell +# Windows +Copy-Item -Recurse ./RichPS7 "$env:USERPROFILE\Documents\PowerShell\Modules\" + +# macOS/Linux +Copy-Item -Recurse ./RichPS7 "~/.local/share/powershell/Modules/" +``` + +2. Import the module: + +```powershell +Import-Module RichPS7 +``` + +### Option 3: Add to Profile + +Add the import statement to your PowerShell profile for automatic loading: + +```powershell +# Open your profile +notepad $PROFILE + +# Add this line: +Import-Module RichPS7 +``` + +## Quick Start + +```powershell +# Import the module +Import-Module RichPS7 + +# Print colored text +Write-Rich "Hello, World!" -Color (New-RichColor 'cyan') -Bold + +# Create a table +$table = New-RichTable -Title "System Info" +$table.AddColumn("Property") +$table.AddColumn("Value") +$table.AddRow(@("OS", $PSVersionTable.OS)) +$table.AddRow(@("PowerShell", $PSVersionTable.PSVersion)) +Write-Host $table.Render() + +# Create a panel +$panel = New-RichPanel -Content "Important message!" -Title "Alert" -BoxStyle "double" +Write-Host $panel.Render() + +# Show a progress bar +Invoke-RichProgress -Description "Processing" -Collection (1..50) -ScriptBlock { + param($item) + Start-Sleep -Milliseconds 50 +} +``` + +## Usage Guide + +### Colors + +RichPS7 supports multiple ways to specify colors: + +```powershell +# Named colors +$red = New-RichColor 'red' +$blue = New-RichColor 'blue' +$brightGreen = New-RichColor 'bright_green' + +# Hex colors +$orange = New-RichColor '#FFA500' +$purple = New-RichColor '#9B59B6' + +# RGB colors +$custom = [RichColor]::new(255, 128, 64) + +# Use colors +Write-Rich "Colored text!" -Color $red +``` + +**Available named colors:** +- Basic: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white` +- Bright: `bright_black`, `bright_red`, `bright_green`, `bright_yellow`, `bright_blue`, `bright_magenta`, `bright_cyan`, `bright_white` +- Others: `gray`, `grey`, `orange`, `purple`, `pink` + +### Text Styling + +Apply various text styles to your output: + +```powershell +# Individual styles +Write-Rich "Bold text" -Bold +Write-Rich "Italic text" -Italic +Write-Rich "Underlined text" -Underline + +# Combined styles +Write-Rich "Bold and Italic" -Bold -Italic + +# Using style objects +$style = New-RichStyle -ForegroundColor (New-RichColor 'cyan') -Bold -Underline +Write-Rich "Styled text" -Style $style + +# Foreground and background colors +$style = New-RichStyle ` + -ForegroundColor (New-RichColor 'yellow') ` + -BackgroundColor (New-RichColor 'blue') ` + -Bold +Write-Rich "Yellow on blue" -Style $style +``` + +### Console + +The console object provides themed output and utilities: + +```powershell +$console = Get-RichConsole + +# Themed messages +$console.Info("Information message") +$console.Warning("Warning message") +$console.Error("Error message") +$console.Success("Success message") + +# Rules (horizontal lines) +$console.Rule() +$console.Rule("Section Title") +$console.Rule("Colored Rule", (New-RichColor 'cyan')) + +# Utilities +$console.NewLine() +$console.NewLine(3) # Multiple blank lines +$console.Clear() # Clear screen +``` + +### Tables + +Create beautiful tables with various options: + +```powershell +# Basic table +$table = New-RichTable +$table.AddColumn("Name") +$table.AddColumn("Age") +$table.AddRow(@("Alice", "30")) +$table.AddRow(@("Bob", "25")) +Write-Host $table.Render() + +# Table with title and custom box style +$table = New-RichTable -Title "Users" -BoxStyle "double" + +# Columns with alignment +$table.AddColumn("Product", @{ Align = 'left' }) +$table.AddColumn("Price", @{ Align = 'right' }) +$table.AddColumn("Qty", @{ Align = 'center' }) + +# Add rows +$table.AddRow(@("Widget", "$19.99", "42")) +$table.AddRow(@("Gadget", "$29.99", "17")) + +Write-Host $table.Render() + +# Table with row separators +$table = New-RichTable -ShowLines +# ... add columns and rows ... +Write-Host $table.Render() + +# Available box styles: 'rounded', 'square', 'double', 'simple' +``` + +### Panels + +Create bordered panels for highlighting content: + +```powershell +# Simple panel +$panel = New-RichPanel -Content "Hello, World!" +Write-Host $panel.Render() + +# Panel with title +$panel = New-RichPanel -Content "Important info" -Title "Alert" +Write-Host $panel.Render() + +# Panel with subtitle +$panel = New-RichPanel ` + -Content "Content here" ` + -Title "Main Title" ` + -Subtitle "Subtitle" +Write-Host $panel.Render() + +# Multi-line content +$content = @" +Line 1 +Line 2 +Line 3 +"@ +$panel = New-RichPanel -Content $content -Title "Multi-line" +Write-Host $panel.Render() + +# Custom styling +$titleStyle = New-RichStyle -ForegroundColor (New-RichColor 'yellow') -Bold +$borderStyle = New-RichStyle -ForegroundColor (New-RichColor 'cyan') + +$panel = New-RichPanel ` + -Content "Styled panel" ` + -Title "Custom" ` + -TitleStyle $titleStyle ` + -BorderStyle $borderStyle ` + -BoxStyle "double" +Write-Host $panel.Render() + +# Box styles: 'rounded', 'square', 'double', 'heavy', 'simple' +# Title alignment: 'left', 'center', 'right' +``` + +### Progress Bars + +Show progress for long-running operations: + +```powershell +# Simple progress with Invoke-RichProgress +$items = 1..100 +Invoke-RichProgress -Description "Processing items" -Collection $items -ScriptBlock { + param($item) + # Do work here + Start-Sleep -Milliseconds 50 +} + +# Manual progress control +$progress = New-RichProgress +$task = $progress.AddTask("Downloading", 100) + +$progress.Start() +try { + for ($i = 0; $i -lt 100; $i++) { + $task.Advance(1) + $progress.Refresh() + Start-Sleep -Milliseconds 50 + } +} finally { + $progress.Stop() +} + +# Multiple progress bars +$progress = New-RichProgress +$task1 = $progress.AddTask("Task 1", 50) +$task2 = $progress.AddTask("Task 2", 100) +$task3 = $progress.AddTask("Task 3", 75) + +$progress.Start() +try { + while (-not $progress.IsComplete()) { + # Update tasks as needed + if (-not $task1.IsComplete) { $task1.Advance(1) } + if (-not $task2.IsComplete) { $task2.Advance(2) } + if (-not $task3.IsComplete) { $task3.Advance(1) } + + $progress.Refresh() + Start-Sleep -Milliseconds 50 + } +} finally { + $progress.Stop() +} + +# Custom colors +$task.CompleteColor = New-RichColor 'cyan' +$task.RemainingColor = New-RichColor 'bright_black' +``` + +## Examples + +Check out the `Examples` directory for complete demonstrations: + +- `01-Colors.ps1` - Color and styling examples +- `02-Tables.ps1` - Table formatting examples +- `03-Panels.ps1` - Panel examples with various styles +- `04-Progress.ps1` - Progress bar examples +- `05-Complete-Demo.ps1` - Complete feature demonstration + +Run an example: + +```powershell +./RichPS7/Examples/05-Complete-Demo.ps1 +``` + +## API Reference + +### Functions + +- `New-RichColor` - Create a color from name, hex, or RGB +- `New-RichStyle` - Create a text style with colors and attributes +- `Get-RichConsole` - Get the global console instance +- `Write-Rich` - Write styled text to the console +- `New-RichTable` - Create a new table +- `New-RichPanel` - Create a new panel +- `New-RichProgress` - Create a progress tracker +- `Invoke-RichProgress` - Simple progress tracking helper + +### Classes + +- `RichColor` - Color representation with ANSI code generation +- `RichStyle` - Text style with color and attributes +- `RichConsole` - Console output handler +- `RichTable` - Table renderer +- `RichPanel` - Panel renderer +- `RichProgress` - Progress tracker +- `RichProgressBar` - Individual progress bar + +## Comparison with Python Rich + +This PowerShell port includes the core features from Python's Rich library: + +| Feature | Python Rich | RichPS7 | +|---------|-------------|---------| +| Colors (RGB/Hex/Named) | ✅ | ✅ | +| Text Styling | ✅ | ✅ | +| Tables | ✅ | ✅ | +| Panels | ✅ | ✅ | +| Progress Bars | ✅ | ✅ | +| Console | ✅ | ✅ | +| Syntax Highlighting | ✅ | ⏳ (future) | +| Markdown Rendering | ✅ | ⏳ (future) | +| Tracebacks | ✅ | ⏳ (future) | +| Tree Views | ✅ | ⏳ (future) | +| Live Display | ✅ | 🔶 (partial) | + +✅ = Implemented | ⏳ = Planned | 🔶 = Partially implemented + +## Contributing + +Contributions are welcome! This is a community port of the Rich library. + +## License + +MIT License - see LICENSE file for details + +## Credits + +- Original Python Rich library by [Will McGugan](https://github.com/willmcgugan) +- Python Rich: https://github.com/Textualize/rich + +## Links + +- [Python Rich Documentation](https://rich.readthedocs.io/) +- [PowerShell Documentation](https://docs.microsoft.com/powershell/) + +--- + +Made with ❤️ for the PowerShell community diff --git a/RichPS7/RichPS7.psd1 b/RichPS7/RichPS7.psd1 new file mode 100644 index 00000000..954ce586 --- /dev/null +++ b/RichPS7/RichPS7.psd1 @@ -0,0 +1,84 @@ +@{ + # Script module or binary module file associated with this manifest. + RootModule = 'RichPS7.psm1' + + # Version number of this module. + ModuleVersion = '0.1.0' + + # ID used to uniquely identify this module + GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890' + + # Author of this module + Author = 'RichPS7 Contributors' + + # Company or vendor of this module + CompanyName = 'Community' + + # Copyright statement for this module + Copyright = '(c) 2025 RichPS7 Contributors. MIT License.' + + # Description of the functionality provided by this module + Description = 'A PowerShell 7 port of Pythons Rich library - Beautiful terminal formatting with colors, tables, panels, and progress bars. Provides ANSI styling, advanced text formatting, and rich UI components for the terminal.' + + # Minimum version of the PowerShell engine required by this module + PowerShellVersion = '7.0' + + # Functions to export from this module + FunctionsToExport = @( + 'New-RichColor' + 'New-RichStyle' + 'Get-RichConsole' + 'Write-Rich' + 'New-RichTable' + 'New-RichPanel' + 'New-RichProgress' + 'Invoke-RichProgress' + ) + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @('RichConsole') + + # Aliases to export from this module + AliasesToExport = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess + PrivateData = @{ + PSData = @{ + # Tags applied to this module + Tags = @('Terminal', 'Console', 'Formatting', 'ANSI', 'Colors', 'Rich', 'UI', 'Table', 'Progress', 'Panel') + + # A URL to the license for this module. + LicenseUri = 'https://opensource.org/licenses/MIT' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/Textualize/rich' + + # ReleaseNotes of this module + ReleaseNotes = @' +## Version 0.1.0 - Initial Release + +Core Features: +- ANSI color support with RGB, named colors, and hex codes +- Text styling (bold, italic, underline, strike, dim) +- Console output with styled text +- Table rendering with multiple box styles and alignment +- Panel components with titles and borders +- Progress bars with ETA estimation +- Cross-platform support (PowerShell 7+) + +Components: +- RichColor: Color handling and ANSI code generation +- RichStyle: Text styling and formatting +- RichConsole: Main console output interface +- RichTable: Table rendering with borders +- RichPanel: Panel components with decorative borders +- RichProgress: Progress tracking and display + +This is a port of the Python Rich library's core functionality to PowerShell 7. +'@ + } + } +} diff --git a/RichPS7/RichPS7.psm1 b/RichPS7/RichPS7.psm1 new file mode 100644 index 00000000..80f0c596 --- /dev/null +++ b/RichPS7/RichPS7.psm1 @@ -0,0 +1,33 @@ +#Requires -Version 7.0 + +# RichPS7 - A PowerShell 7 port of Python's Rich library +# Beautiful terminal formatting for PowerShell + +# Import classes in order (dependencies first) +. $PSScriptRoot\Classes\Color.ps1 +. $PSScriptRoot\Classes\Style.ps1 +. $PSScriptRoot\Classes\Console.ps1 +. $PSScriptRoot\Classes\Table.ps1 +. $PSScriptRoot\Classes\Panel.ps1 +. $PSScriptRoot\Classes\Progress.ps1 + +# Export functions +Export-ModuleMember -Function @( + 'New-RichColor' + 'New-RichStyle' + 'Get-RichConsole' + 'Write-Rich' + 'New-RichTable' + 'New-RichPanel' + 'New-RichProgress' + 'Invoke-RichProgress' +) + +# Export the global console variable +Export-ModuleMember -Variable 'RichConsole' + +# Module initialization message +Write-Host "RichPS7 module loaded. " -NoNewline +Write-Host "Beautiful terminal formatting for PowerShell 7!" -ForegroundColor Cyan +Write-Host "Get started with: " -NoNewline +Write-Host "Get-Command -Module RichPS7" -ForegroundColor Yellow diff --git a/RichPS7/Tests/Basic-Test.ps1 b/RichPS7/Tests/Basic-Test.ps1 new file mode 100644 index 00000000..d23f1fdb --- /dev/null +++ b/RichPS7/Tests/Basic-Test.ps1 @@ -0,0 +1,161 @@ +#!/usr/bin/env pwsh +#Requires -Version 7.0 + +# Basic functionality test for RichPS7 + +Write-Host "=== RichPS7 Basic Functionality Test ===`n" -ForegroundColor Cyan + +$testsPassed = 0 +$testsFailed = 0 + +function Test-Feature { + param( + [string]$Name, + [scriptblock]$Test + ) + + Write-Host "Testing: $Name ... " -NoNewline + + try { + & $Test + Write-Host "PASS" -ForegroundColor Green + $script:testsPassed++ + return $true + } catch { + Write-Host "FAIL" -ForegroundColor Red + Write-Host " Error: $_" -ForegroundColor Red + $script:testsFailed++ + return $false + } +} + +# Import module +Write-Host "Importing RichPS7 module...`n" +try { + Import-Module "$PSScriptRoot/../RichPS7.psd1" -Force + Write-Host "Module imported successfully!`n" -ForegroundColor Green +} catch { + Write-Host "Failed to import module: $_" -ForegroundColor Red + exit 1 +} + +# Test 1: Color creation +Test-Feature "Color creation (named)" { + $color = New-RichColor 'red' + if (-not $color) { throw "Color creation failed" } + if ($color.Red -ne 255) { throw "Red value incorrect" } +} + +Test-Feature "Color creation (hex)" { + $color = New-RichColor '#FF5733' + if (-not $color) { throw "Hex color creation failed" } +} + +Test-Feature "Color creation (RGB)" { + $color = [RichColor]::new(100, 150, 200) + if ($color.Red -ne 100) { throw "RGB color creation failed" } +} + +# Test 2: Style creation +Test-Feature "Style creation" { + $style = New-RichStyle -Bold -Italic + if (-not $style.Bold) { throw "Bold not set" } + if (-not $style.Italic) { throw "Italic not set" } +} + +Test-Feature "Style with colors" { + $style = New-RichStyle -ForegroundColor (New-RichColor 'cyan') + if (-not $style.ForegroundColor) { throw "Foreground color not set" } +} + +# Test 3: Console +Test-Feature "Console instance" { + $console = Get-RichConsole + if (-not $console) { throw "Console not created" } +} + +# Test 4: Table creation +Test-Feature "Table creation" { + $table = New-RichTable + if (-not $table) { throw "Table creation failed" } +} + +Test-Feature "Table with columns and rows" { + $table = New-RichTable + $table.AddColumn("Col1") + $table.AddColumn("Col2") + $table.AddRow(@("A", "B")) + + if ($table.Columns.Count -ne 2) { throw "Column count incorrect" } + if ($table.Rows.Count -ne 1) { throw "Row count incorrect" } +} + +Test-Feature "Table rendering" { + $table = New-RichTable + $table.AddColumn("Test") + $table.AddRow(@("Value")) + $output = $table.Render() + + if (-not $output) { throw "Table rendering failed" } + if ($output.Length -lt 10) { throw "Table output too short" } +} + +# Test 5: Panel creation +Test-Feature "Panel creation" { + $panel = New-RichPanel -Content "Test" + if (-not $panel) { throw "Panel creation failed" } +} + +Test-Feature "Panel rendering" { + $panel = New-RichPanel -Content "Test content" + $output = $panel.Render() + + if (-not $output) { throw "Panel rendering failed" } + if ($output.Length -lt 10) { throw "Panel output too short" } +} + +Test-Feature "Panel with title" { + $panel = New-RichPanel -Content "Test" -Title "Title" + if ($panel.Title -ne "Title") { throw "Panel title not set" } +} + +# Test 6: Progress +Test-Feature "Progress creation" { + $progress = New-RichProgress + if (-not $progress) { throw "Progress creation failed" } +} + +Test-Feature "Progress task creation" { + $progress = New-RichProgress + $task = $progress.AddTask("Test", 100) + + if (-not $task) { throw "Task creation failed" } + if ($task.Total -ne 100) { throw "Task total incorrect" } +} + +Test-Feature "Progress advancement" { + $progress = New-RichProgress + $task = $progress.AddTask("Test", 100) + $task.Advance(50) + + if ($task.Current -ne 50) { throw "Task advancement failed" } +} + +# Test 7: Write-Rich function +Test-Feature "Write-Rich function exists" { + $command = Get-Command Write-Rich -ErrorAction SilentlyContinue + if (-not $command) { throw "Write-Rich function not found" } +} + +# Summary +Write-Host "`n=== Test Summary ===" -ForegroundColor Cyan +Write-Host "Tests Passed: $testsPassed" -ForegroundColor Green +Write-Host "Tests Failed: $testsFailed" -ForegroundColor $(if ($testsFailed -gt 0) { 'Red' } else { 'Green' }) + +if ($testsFailed -eq 0) { + Write-Host "`nAll tests passed! ✓" -ForegroundColor Green + exit 0 +} else { + Write-Host "`nSome tests failed! ✗" -ForegroundColor Red + exit 1 +}