Private/AnsiPalette.ps1
|
# Catppuccin theme definitions (RGB triplets) # Reference: https://catppuccin.com/palette $script:CatppuccinThemes = @{ Mocha = @{ Text = @(205, 214, 244) # #CDD6F4 Subtext = @(166, 173, 200) # #A6ADC8 Dim = @(127, 132, 156) # #7F849C Blue = @(166, 227, 161) # #A6E3A1 (green primary in this app) Green = @(166, 227, 161) # #A6E3A1 Red = @(243, 139, 168) # #F38BA8 Yellow = @(249, 226, 175) # #F9E2AF Mauve = @(137, 180, 250) # #89B4FA (blue accent) Teal = @(148, 226, 213) # #94E2D5 Peach = @(250, 179, 135) # #FAB387 Cyan = @(137, 220, 235) # #89DCEB Orange = @(250, 179, 135) # #FAB387 White = @(205, 214, 244) # #CDD6F4 Grey = @(127, 132, 156) # #7F849C Surface = @(49, 50, 68) # #313244 SurfaceFg = @(69, 71, 90) # #45475A BgSelect = @(69, 71, 90) # #45475A } Macchiato = @{ Text = @(202, 211, 245) # #CAD3F5 Subtext = @(165, 173, 206) # #A5ADCE Dim = @(128, 135, 162) # #8087A2 Blue = @(166, 218, 149) # #A6DA95 Green = @(166, 218, 149) # #A6DA95 Red = @(237, 135, 150) # #ED8796 Yellow = @(238, 212, 159) # #EED49F Mauve = @(138, 173, 244) # #8AADF4 Teal = @(139, 213, 202) # #8BD5CA Peach = @(245, 169, 127) # #F5A97F Cyan = @(145, 215, 227) # #91D7E3 Orange = @(245, 169, 127) # #F5A97F White = @(202, 211, 245) # #CAD3F5 Grey = @(128, 135, 162) # #8087A2 Surface = @(54, 58, 79) # #363A4F SurfaceFg = @(73, 77, 100) # #494D64 BgSelect = @(73, 77, 100) # #494D64 } Frappe = @{ Text = @(198, 208, 245) # #C6D0F5 Subtext = @(165, 173, 206) # #A5ADCE Dim = @(131, 139, 167) # #838BA7 Blue = @(166, 209, 137) # #A6D189 Green = @(166, 209, 137) # #A6D189 Red = @(231, 130, 132) # #E78284 Yellow = @(229, 200, 144) # #E5C890 Mauve = @(140, 170, 238) # #8CAAEE Teal = @(129, 200, 190) # #81C8BE Peach = @(239, 159, 118) # #EF9F76 Cyan = @(153, 209, 219) # #99D1DB Orange = @(239, 159, 118) # #EF9F76 White = @(198, 208, 245) # #C6D0F5 Grey = @(131, 139, 167) # #838BA7 Surface = @(65, 69, 89) # #414559 SurfaceFg = @(81, 87, 109) # #51576D BgSelect = @(81, 87, 109) # #51576D } Latte = @{ Text = @(76, 79, 105) # #4C4F69 Subtext = @(92, 95, 119) # #5C5F77 Dim = @(124, 127, 147) # #7C7F93 Blue = @(64, 160, 43) # #40A02B Green = @(64, 160, 43) # #40A02B Red = @(210, 15, 57) # #D20F39 Yellow = @(223, 142, 29) # #DF8E1D Mauve = @(30, 102, 245) # #1E66F5 Teal = @(23, 146, 153) # #179299 Peach = @(254, 100, 11) # #FE640B Cyan = @(4, 165, 229) # #04A5E5 Orange = @(254, 100, 11) # #FE640B White = @(76, 79, 105) # #4C4F69 Grey = @(124, 127, 147) # #7C7F93 Surface = @(204, 208, 218) # #CCD0DA SurfaceFg = @(172, 176, 190) # #ACB0BE BgSelect = @(172, 176, 190) # #ACB0BE } } function Get-InTUIColorPalette { <# .SYNOPSIS Returns the active Catppuccin theme palette as ANSI 24-bit escape strings. #> if ($PSVersionTable.PSVersion.Major -lt 7) { # Graceful degradation: return empty strings on older PS $empty = @{} foreach ($key in @('Text','Subtext','Dim','Blue','Green','Red','Yellow','Mauve', 'Teal','Peach','Surface','SurfaceFg','BgSelect','Cyan','Orange','White', 'Grey','Bold','Italic','DimStyle','Reset')) { $empty[$key] = '' } return $empty } $themeName = if ($script:InTUIConfig -and $script:InTUIConfig.Theme) { $script:InTUIConfig.Theme } else { 'Mocha' } $theme = $script:CatppuccinThemes[$themeName] if (-not $theme) { $theme = $script:CatppuccinThemes['Mocha'] } $e = [char]0x1B $palette = @{ Bold = "$e[1m" Italic = "$e[3m" DimStyle = "$e[2m" Reset = "$e[0m" } foreach ($key in @('Text','Subtext','Dim','Blue','Green','Red','Yellow','Mauve', 'Teal','Peach','Cyan','Orange','White','Grey')) { $rgb = $theme[$key] $palette[$key] = "$e[38;2;$($rgb[0]);$($rgb[1]);$($rgb[2])m" } # Background colors $rgb = $theme['Surface'] $palette['Surface'] = "$e[48;2;$($rgb[0]);$($rgb[1]);$($rgb[2])m" $rgb = $theme['SurfaceFg'] $palette['SurfaceFg'] = "$e[38;2;$($rgb[0]);$($rgb[1]);$($rgb[2])m" $rgb = $theme['BgSelect'] $palette['BgSelect'] = "$e[48;2;$($rgb[0]);$($rgb[1]);$($rgb[2])m" return $palette } function ConvertFrom-InTUIMarkup { <# .SYNOPSIS Converts [color]text[/] Spectre-style markup to ANSI escape codes. .DESCRIPTION Stack-based parser that handles nested tags correctly. Supports: blue, green, red, yellow, cyan, grey, white, bold, dim, DeepSkyBlue1, DarkOrange, orange1, Cyan1, steelblue1_1, compound tags like [bold white], [white bold], [grey dim]. [/] pops the current style and restores the parent. #> [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyString()] [string]$Text ) if ([string]::IsNullOrEmpty($Text)) { return $Text } $palette = Get-InTUIColorPalette if (-not $palette.Reset) { return Strip-InTUIMarkup -Text $Text } $reset = $palette.Reset $colorMap = @{ 'blue' = $palette.Blue 'green' = $palette.Green 'red' = $palette.Red 'yellow' = $palette.Yellow 'cyan' = $palette.Cyan 'cyan1' = $palette.Cyan 'grey' = $palette.Grey 'gray' = $palette.Grey 'white' = $palette.White 'bold' = $palette.Bold 'dim' = $palette.DimStyle 'italic' = $palette.Italic 'deepskyblue1' = $palette.Blue 'darkorange' = $palette.Peach 'orange1' = $palette.Peach 'steelblue1_1' = $palette.Blue 'orange' = $palette.Peach 'mauve' = $palette.Mauve 'teal' = $palette.Teal 'peach' = $palette.Peach } $styleStack = [System.Collections.Generic.Stack[string]]::new() $buf = [System.Text.StringBuilder]::new($Text.Length * 2) $len = $Text.Length $pos = 0 while ($pos -lt $len) { $ch = $Text[$pos] # Escaped [[ → literal [ if ($ch -eq '[' -and ($pos + 1) -lt $len -and $Text[$pos + 1] -eq '[') { $buf.Append('[') | Out-Null $pos += 2 continue } # Tag or close marker if ($ch -eq '[') { $close = $Text.IndexOf(']', $pos + 1) if ($close -eq -1) { $buf.Append($ch) | Out-Null $pos++ continue } $tag = $Text.Substring($pos + 1, $close - $pos - 1) if ($tag -eq '/') { # Pop style, emit reset, re-apply parent if any if ($styleStack.Count -gt 0) { $styleStack.Pop() | Out-Null } $buf.Append($reset) | Out-Null if ($styleStack.Count -gt 0) { $buf.Append($styleStack.Peek()) | Out-Null } } else { $parts = $tag.Trim() -split '\s+' $ansi = '' foreach ($part in $parts) { $key = $part.ToLower() if ($colorMap.ContainsKey($key)) { $ansi += $colorMap[$key] } } if ($ansi) { $styleStack.Push($ansi) | Out-Null $buf.Append($ansi) | Out-Null } } $pos = $close + 1 continue } # Escaped ]] → literal ] if ($ch -eq ']' -and ($pos + 1) -lt $len -and $Text[$pos + 1] -eq ']') { $buf.Append(']') | Out-Null $pos += 2 continue } $buf.Append($ch) | Out-Null $pos++ } if ($styleStack.Count -gt 0) { $buf.Append($reset) | Out-Null } return $buf.ToString() } function Strip-InTUIMarkup { <# .SYNOPSIS Removes all [color]...[/] markup tags, returning plain text. #> [CmdletBinding()] param( [Parameter(Mandatory)] [AllowEmptyString()] [string]$Text ) if ([string]::IsNullOrEmpty($Text)) { return $Text } # Handle escaped brackets $result = $Text -replace '\[\[', "`0LBRACKET`0" $result = $result -replace '\]\]', "`0RBRACKET`0" # Remove [tag]...[/] keeping inner content - innermost first, loop until stable do { $prev = $result $result = [regex]::Replace($result, '\[([^\]\/]+)\]([^\[]*?)\[/\]', '$2') } while ($result -ne $prev) # Restore escaped brackets $result = $result -replace "`0LBRACKET`0", '[' $result = $result -replace "`0RBRACKET`0", ']' return $result } function Write-InTUIText { <# .SYNOPSIS Converts markup to ANSI and writes to host. Replaces Write-SpectreHost. #> [CmdletBinding()] param( [Parameter(Position = 0)] [AllowEmptyString()] [string]$Text = '', [Parameter()] [switch]$NoNewline ) $converted = ConvertFrom-InTUIMarkup -Text $Text if ($NoNewline) { Write-Host $converted -NoNewline } else { Write-Host $converted } } |