Public/Test-AnsiSupport.ps1
|
function Test-AnsiSupport { <# .SYNOPSIS Detects the terminal's ANSI color support and the styles it shows .DESCRIPTION Test-AnsiSupport reads the environment and the host to answer which ANSI color mode the terminal supports (TrueColor, 256-color, 16-color or none), whether it draws bold as a bold font, and which text styles it shows. Write-ColorEX falls back to the modes it reports. On Windows PowerShell 5.1 in the Windows console host it switches on virtual terminal processing for the session when it is off. DETECTION: - Color support levels: TrueColor (24-bit), ANSI8 (256-color), ANSI4 (16-color), None - Bold font rendering vs color brightening detection - Style support: Italic, Underline, Blink, Faint, CrossedOut, DoubleUnderline, Overline - Terminal-specific detection (Windows Terminal, iTerm2, GNOME Terminal, VS Code, etc.) - Platform detection: Windows, Linux, macOS with specific terminal identification - Environment variables: FORCE_COLOR, NO_COLOR, COLORTERM, TERM (TERM=dumb means none) - On PowerShell 7.2 and later, a host without virtual terminal support means none, since PowerShell removes escape codes from its output TERMINAL-SPECIFIC DETECTION: Windows: Windows Terminal, PowerShell Console (conhost), ConEmu, VS Code, PowerShell ISE, Git Bash macOS: iTerm2, Terminal.app, VS Code Linux: GNOME Terminal, Konsole, xterm, rxvt-unicode, Kitty, and Windows Terminal under WSL WINDOWS CONSOLE: On Windows 10 build 10586 or later, Windows PowerShell 5.1 switches on virtual terminal processing in conhost.exe for the session when it is off, so ANSI escape codes work. .PARAMETER Silent Suppresses all warning messages and terminal limitation notices. Useful when programmatically checking terminal capabilities without user-facing output. Default: $false (warnings enabled) .INPUTS None This function does not accept pipeline input. .OUTPUTS PSCustomObject Returns a custom object with the following properties: ColorSupport (string): - 'TrueColor' : 24-bit RGB color support (16.7 million colors) - 'ANSI8' : 256-color support - 'ANSI4' : 16-color support - 'None' : No ANSI support (PowerShell ISE, old terminals) SupportsBoldFonts (boolean): - $true : Terminal renders true bold fonts - $false : Terminal only brightens colors when Bold SGR is used Details (hashtable): - PowerShellVersion : PowerShell version string - IsConsoleHost : Whether running in ConsoleHost - HasVirtualTerminalProcessing : VT processing enabled (Windows) - HasCompatibleTerminalEnv : Compatible TERM environment variable - IsPSCore : PowerShell 6+ (Core) - OperatingSystem : Platform (Win32NT, Unix) - TerminalType : Detected terminal name - EnableInstructions : How to enable ANSI support - StyleSupport : Hashtable of supported styles - Warnings : Array of limitation warnings .EXAMPLE Test-AnsiSupport Detects ANSI support and displays warnings about terminal limitations. Returns full detection results object. .EXAMPLE $ansi = Test-AnsiSupport -Silent if ($ansi.ColorSupport -eq 'TrueColor') { Write-ColorEX -Text "24-bit colors available!" -Color @(0,255,128) -TrueColor } Silent detection for programmatic use. Checks if TrueColor is available. .EXAMPLE $result = Test-AnsiSupport $result.Details.TerminalType # Output: "Windows Terminal" or "iTerm2" or "GNOME Terminal 3.32+" etc. Get specific terminal type for conditional logic. .EXAMPLE if ((Test-AnsiSupport).SupportsBoldFonts) { Write-ColorEX "Bold text!" -Color Red -Bold } else { Write-ColorEX "Brightened text" -Color Red -Bold } Check if terminal renders true bold fonts or just brightens colors. .NOTES Author: MarkusMcNugen License: MIT Requires: PowerShell 5.1 or later ENVIRONMENT VARIABLES: - FORCE_COLOR : Override detection (0=None, 1=ANSI4, 2=ANSI8, 3=TrueColor) - NO_COLOR : Disable all colors (any value) - TERM : Terminal type identifier - COLORTERM : Color capability ('truecolor', '24bit') - WT_SESSION : Windows Terminal session ID - VTE_VERSION : GNOME Terminal (VTE) version PLATFORM NOTES: Windows - Uses P/Invoke to check/enable Virtual Terminal Processing Linux - Checks TERM, COLORTERM, VTE_VERSION, terminal-specific env vars macOS - Detects iTerm2, Terminal.app via TERM_PROGRAM Importing PSWriteColorEX runs this function once and keeps the result in $script:CachedANSISupport for Write-ColorEX. .LINK https://github.com/MarkusMcNugen/PSWriteColorEX .LINK Write-ColorEX .LINK https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences #> [CmdletBinding()] [Alias('TAS', 'Test-ANSI')] param( [switch]$Silent = $False ) # Initialize collection for results $results = [PSCustomObject]@{ ColorSupport = 'None' # None, ANSI4, ANSI8, TrueColor SupportsBoldFonts = $False # Whether terminal supports true bold fonts (vs color lightening only) Details = @{ PowerShellVersion = $PSVersionTable.PSVersion.ToString() IsConsoleHost = $Host.Name -eq 'ConsoleHost' HasVirtualTerminalProcessing = $False HasCompatibleTerminalEnv = $False IsPSCore = $PSVersionTable.PSVersion.Major -ge 6 OperatingSystem = [System.Environment]::OSVersion.Platform TerminalType = '' EnableInstructions = '' StyleSupport = @{ Bold = $True Italic = $False Underline = $True Faint = $False Blink = $False CrossedOut = $False DoubleUnderline = $False Overline = $False } Warnings = @() } } # Check for forced color support first $forceColor = [Environment]::GetEnvironmentVariable('FORCE_COLOR') If ($forceColor) { Switch ($forceColor) { '0' { $results.ColorSupport = 'None'; Return $results } '1' { $results.ColorSupport = 'ANSI4'; Return $results } '2' { $results.ColorSupport = 'ANSI8'; Return $results } '3' { $results.ColorSupport = 'TrueColor'; $results.SupportsBoldFonts = $True; Return $results } } } If ([Environment]::GetEnvironmentVariable('NO_COLOR')) { $results.ColorSupport = 'None' Return $results } # TERM=dumb names a terminal that interprets no escape codes If ([Environment]::GetEnvironmentVariable('TERM') -eq 'dumb') { $results.ColorSupport = 'None' $results.Details.TerminalType = 'dumb' Return $results } # Check environment variables for color support level $termEnv = [Environment]::GetEnvironmentVariable('TERM') $colorTerm = [Environment]::GetEnvironmentVariable('COLORTERM') $conEmuANSI = [Environment]::GetEnvironmentVariable('ConEmuANSI') $wtSession = [Environment]::GetEnvironmentVariable('WT_SESSION') $termProgram = [Environment]::GetEnvironmentVariable('TERM_PROGRAM') $vte_version = [Environment]::GetEnvironmentVariable('VTE_VERSION') # Determine terminal type and color support level If ($colorTerm -eq 'truecolor' -or $colorTerm -eq '24bit') { $results.ColorSupport = 'TrueColor' $results.Details.HasCompatibleTerminalEnv = $True } ElseIf ($termEnv -like '*256color*' -or $termEnv -like '*256*') { $results.ColorSupport = 'ANSI8' $results.Details.HasCompatibleTerminalEnv = $True } ElseIf (-not [string]::IsNullOrEmpty($termEnv)) { $results.ColorSupport = 'ANSI4' $results.Details.HasCompatibleTerminalEnv = $True } # Platform-specific checks If ($results.Details.OperatingSystem -eq 'Unix') { # Unix/Linux/macOS generally support ANSI $results.Details.HasVirtualTerminalProcessing = $True # Detect specific terminal - macOS If ($termProgram -eq 'Apple_Terminal') { $results.Details.TerminalType = 'macOS Terminal.app' $results.ColorSupport = 'ANSI8' # Terminal.app max is 256 colors $results.SupportsBoldFonts = $False # Terminal.app uses color brightening for bold $results.Details.Warnings += 'macOS Terminal.app does not support TrueColor (24-bit). Maximum 256 colors. For TrueColor support, use iTerm2.' # Terminal.app shows italic and underline $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } ElseIf ($termProgram -eq 'iTerm.app') { $results.Details.TerminalType = 'iTerm2' If ($results.ColorSupport -eq 'None' -or $results.ColorSupport -eq 'ANSI4' -or $results.ColorSupport -eq 'ANSI8') { $results.ColorSupport = 'TrueColor' } $results.SupportsBoldFonts = $True # iTerm2 supports true bold fonts # iTerm2 3.5 and later show these styles $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.StyleSupport.CrossedOut = $True } ElseIf ($termProgram -eq 'vscode') { $results.Details.TerminalType = 'VS Code Integrated Terminal' If ($results.ColorSupport -eq 'None' -or $results.ColorSupport -eq 'ANSI4' -or $results.ColorSupport -eq 'ANSI8') { $results.ColorSupport = 'TrueColor' } $results.SupportsBoldFonts = $True # VS Code terminal (xterm.js) supports bold fonts $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } # Windows Terminal running a WSL shell, which passes WT_SESSION through ElseIf (-not [string]::IsNullOrEmpty($wtSession)) { $results.Details.TerminalType = 'Windows Terminal (WSL)' $results.ColorSupport = 'TrueColor' $results.SupportsBoldFonts = $True $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.StyleSupport.CrossedOut = $True } # Detect VTE-based terminals (GNOME Terminal, Xfce Terminal, etc.) ElseIf (-not [string]::IsNullOrEmpty($vte_version)) { $vteVersionNum = 0 $null = [int]::TryParse($vte_version, [ref]$vteVersionNum) If ($vteVersionNum -ge 5600) { # VTE 0.56+ (GNOME Terminal 3.32+) draws bold as a bold font, without brighter colors $results.Details.TerminalType = 'VTE-based Terminal (GNOME Terminal 3.32+)' $results.SupportsBoldFonts = $True # VTE 0.56+ defaults to true bold fonts $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.StyleSupport.Blink = $True $results.Details.StyleSupport.Overline = $True If ($vteVersionNum -ge 7600) { # VTE 0.76+ (GNOME Terminal 3.52+, Ubuntu 24.04+) $results.Details.StyleSupport.DoubleUnderline = $True } } ElseIf ($vteVersionNum -ge 5200) { # VTE 0.52-0.55 (GNOME Terminal 3.28-3.31) - bold brightens colors by default $results.Details.TerminalType = 'VTE-based Terminal (GNOME Terminal 3.28+)' $results.SupportsBoldFonts = $False # VTE 0.52-0.55 defaults to color brightening $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.StyleSupport.Blink = $True $results.Details.StyleSupport.Overline = $True } Else { $results.Details.TerminalType = 'VTE-based Terminal' $results.SupportsBoldFonts = $False # Older VTE versions brighten colors $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.Warnings += 'Older VTE version detected. Some styles (Blink, Overline) may not be supported. Consider updating to GNOME Terminal 3.28+ (VTE 0.52+).' } If ($results.ColorSupport -eq 'None' -or $results.ColorSupport -eq 'ANSI4' -or $results.ColorSupport -eq 'ANSI8') { $results.ColorSupport = 'TrueColor' } } # Detect Konsole ElseIf ($termEnv -like '*konsole*' -or $env:KONSOLE_VERSION) { $results.Details.TerminalType = 'Konsole' If ($results.ColorSupport -eq 'None' -or $results.ColorSupport -eq 'ANSI4' -or $results.ColorSupport -eq 'ANSI8') { $results.ColorSupport = 'TrueColor' } $results.SupportsBoldFonts = $True # Konsole supports true bold fonts $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.StyleSupport.Overline = $True } # Detect xterm ElseIf ($termEnv -eq 'xterm-256color') { $results.Details.TerminalType = 'xterm-256color' If ($results.ColorSupport -eq 'None') { $results.ColorSupport = 'ANSI8' } $results.SupportsBoldFonts = $False # xterm default couples bright colors with bold $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } ElseIf ($termEnv -like 'xterm*') { $results.Details.TerminalType = 'xterm' If ($results.ColorSupport -eq 'None') { $results.ColorSupport = 'ANSI4' } $results.SupportsBoldFonts = $False # xterm default couples bright colors with bold $results.Details.StyleSupport.Underline = $True } # Detect rxvt ElseIf ($termEnv -like 'rxvt*') { $results.Details.TerminalType = 'rxvt/urxvt' $results.SupportsBoldFonts = $True # urxvt can decouple bold from bright colors If ($termEnv -like '*256*') { $results.ColorSupport = 'ANSI8' $results.Details.StyleSupport.Italic = $True } Else { $results.ColorSupport = 'ANSI4' $results.Details.Warnings += 'rxvt detected. Basic rxvt does not support 256 colors. Consider using rxvt-unicode (urxvt) or a modern terminal.' } $results.Details.StyleSupport.Underline = $True } # Generic Unix terminal Else { If ($termEnv -like '*256*') { $results.Details.TerminalType = 'Unix Terminal (256 color)' If ($results.ColorSupport -eq 'None') { $results.ColorSupport = 'ANSI8' } } ElseIf (-not [string]::IsNullOrEmpty($termEnv)) { $results.Details.TerminalType = "Unix Terminal ($termEnv)" If ($results.ColorSupport -eq 'None') { $results.ColorSupport = 'ANSI4' } } } $results.Details.EnableInstructions = 'ANSI colors are enabled by default on Unix/Linux/macOS. To ensure 256 colors, add "export TERM=xterm-256color" to your shell config file (~/.bashrc, ~/.zshrc, etc.). For TrueColor, add "export COLORTERM=truecolor".' } ElseIf ($results.Details.OperatingSystem -eq 'Win32NT') { # Windows-specific checks $osVersion = [System.Environment]::OSVersion.Version $isWin10Plus = ($osVersion.Major -gt 10) -or ($osVersion.Major -eq 10 -and $osVersion.Build -ge 10586) $supportsTrueColor = ($osVersion.Major -gt 10) -or ($osVersion.Major -eq 10 -and $osVersion.Build -ge 14931) # Windows Terminal detection If (-not [string]::IsNullOrEmpty($wtSession)) { $results.Details.TerminalType = 'Windows Terminal' $results.Details.HasVirtualTerminalProcessing = $True $results.ColorSupport = 'TrueColor' # Windows Terminal shows these styles $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True $results.Details.StyleSupport.CrossedOut = $True If ($results.Details.IsPSCore) { # PowerShell 7 in Windows Terminal: bold is a bold font $results.SupportsBoldFonts = $True $results.Details.StyleSupport.Bold = $True } Else { # PowerShell 5.1 in Windows Terminal: Bold makes colors lighter, not actual bold font $results.SupportsBoldFonts = $False $results.Details.Warnings += 'PowerShell 5.1 in Windows Terminal: Bold style makes colors lighter instead of bolding the font. Use PowerShell 7+ for proper bold support.' } } # ConEmu detection ElseIf (-not [string]::IsNullOrEmpty($conEmuANSI)) { $results.Details.TerminalType = 'ConEmu/Cmder' $results.Details.HasVirtualTerminalProcessing = $True $results.ColorSupport = If ($supportsTrueColor) { 'TrueColor' } Else { 'ANSI8' } $results.SupportsBoldFonts = $True # ConEmu supports bold fonts $results.Details.Warnings += 'ConEmu/Cmder has LIMITED TrueColor support (only in the bottom buffer area, requires scrolling off). For full TrueColor support, use Windows Terminal.' $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } # mintty detection (Git Bash, Cygwin, MSYS2) ElseIf ($termEnv -like '*mintty*' -or $termProgram -eq 'mintty') { $results.Details.TerminalType = 'mintty (Git Bash/Cygwin/MSYS2)' $results.Details.HasVirtualTerminalProcessing = $True $results.ColorSupport = 'TrueColor' # mintty 2.0.1+ supports TrueColor $results.SupportsBoldFonts = $True # mintty supports true bold fonts $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } # VS Code Integrated Terminal ElseIf ($termProgram -eq 'vscode') { $results.Details.TerminalType = 'VS Code Integrated Terminal' $results.Details.HasVirtualTerminalProcessing = $True $results.ColorSupport = 'TrueColor' $results.SupportsBoldFonts = $True # VS Code terminal (xterm.js) supports bold fonts $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } # PowerShell ISE detection (no ANSI support) ElseIf ($Host.Name -eq 'Windows PowerShell ISE Host') { $results.Details.TerminalType = 'PowerShell ISE' $results.Details.HasVirtualTerminalProcessing = $False $results.ColorSupport = 'None' $results.SupportsBoldFonts = $False # ISE has no ANSI support at all $results.Details.Warnings += 'PowerShell ISE does NOT support ANSI escape sequences. Only native PowerShell colors available. Use Windows Terminal or PowerShell 7+ for ANSI support.' } # PowerShell Core/7+ has automatic ANSI support ElseIf ($results.Details.IsPSCore) { $results.Details.HasVirtualTerminalProcessing = $True $results.ColorSupport = If ($supportsTrueColor) { 'TrueColor' } ElseIf ($isWin10Plus) { 'ANSI8' } Else { 'ANSI4' } $results.Details.TerminalType = 'PowerShell Core Console' $results.SupportsBoldFonts = $True # PowerShell 7+ supports true bold fonts If ($supportsTrueColor) { $results.Details.StyleSupport.Italic = $True $results.Details.StyleSupport.Underline = $True } } # Check for console host (ISE doesn't support ANSI) ElseIf (-not $results.Details.IsConsoleHost) { $results.Details.HasVirtualTerminalProcessing = $False $results.ColorSupport = 'None' $results.Details.TerminalType = $Host.Name $results.Details.Warnings += "Host '$($Host.Name)' does not support ANSI escape sequences. Only native PowerShell colors available." } # Use P/Invoke for Windows PowerShell to check/enable console mode ElseIf ($isWin10Plus) { try { # Define P/Invoke signatures if not already defined If (-not ('ConsoleHelper' -as [type])) { Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class ConsoleHelper { [DllImport("kernel32.dll", SetLastError = true)] public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr GetStdHandle(int nStdHandle); } "@ -ErrorAction SilentlyContinue } If ('ConsoleHelper' -as [type]) { # Constants $STDOUT_HANDLE = -11 $ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 # Get console mode $stdoutHandle = [ConsoleHelper]::GetStdHandle($STDOUT_HANDLE) $consoleMode = 0 If ([ConsoleHelper]::GetConsoleMode($stdoutHandle, [ref]$consoleMode)) { $results.Details.HasVirtualTerminalProcessing = ($consoleMode -band $ENABLE_VIRTUAL_TERMINAL_PROCESSING) -ne 0 # Try to enable VT processing if not already enabled If (-not $results.Details.HasVirtualTerminalProcessing) { $newMode = $consoleMode -bor $ENABLE_VIRTUAL_TERMINAL_PROCESSING If ([ConsoleHelper]::SetConsoleMode($stdoutHandle, $newMode)) { $results.Details.HasVirtualTerminalProcessing = $True If (-not $Silent) { Write-Warning 'ANSI support has been enabled for this session only. To enable permanently, run in Admin PowerShell: Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1' } } } If ($results.Details.HasVirtualTerminalProcessing) { $results.Details.TerminalType = 'Windows PowerShell Console (conhost)' $results.ColorSupport = If ($supportsTrueColor) { 'TrueColor' } ElseIf ($isWin10Plus) { 'ANSI8' } Else { 'ANSI4' } $results.Details.StyleSupport.Underline = $True # PowerShell 5.1 conhost doesn't support true bold - it just makes colors lighter If (-not $results.Details.IsPSCore) { $results.SupportsBoldFonts = $False $results.Details.Warnings += 'PowerShell 5.1 conhost does NOT support true bold font rendering. Bold style will make colors lighter instead of actually bolding the text. Use PowerShell 7+ or Windows Terminal for proper bold support.' } Else { # PowerShell 7+ in conhost still doesn't render bold fonts $results.SupportsBoldFonts = $False $results.Details.Warnings += 'Windows Console (conhost) does NOT support true bold font rendering regardless of PowerShell version. Bold will lighten colors. Use Windows Terminal for proper bold support.' } } } } } Catch { # P/Invoke failed, continue with other checks $results.Details.HasVirtualTerminalProcessing = $False } } $results.Details.EnableInstructions = 'To enable ANSI colors permanently on Windows, run in Admin PowerShell: Set-ItemProperty HKCU:\Console VirtualTerminalLevel -Type DWORD 1' # Windows version warnings If (-not $isWin10Plus -and -not $Silent) { Write-Warning 'Windows versions before Windows 10 build 10586 do not support ANSI colors natively. Consider using ConEmu or Windows Terminal.' } } # VT processing with no color level found means the 16 colors If ($results.ColorSupport -eq 'None' -and $results.Details.HasVirtualTerminalProcessing) { $results.ColorSupport = 'ANSI4' } # PowerShell 7.2 and later remove escape codes from the output of a host without virtual terminal support If ($results.ColorSupport -ne 'None' -and -not (Test-ColorHostVirtualTerminal)) { $results.ColorSupport = 'None' $results.Details.HasVirtualTerminalProcessing = $False $results.Details.Warnings += "Host '$($Host.Name)' has no virtual terminal support, so PowerShell removes ANSI escape codes from its output. Only console colors are available." } # Return warnings if not silent If (-not $Silent) { # Display color support warnings If ($results.ColorSupport -eq 'None') { If (-not $results.Details.IsConsoleHost) { Write-Warning 'ANSI not supported: Not running in a compatible console host (e.g., ISE does not support ANSI)' } ElseIf ($results.Details.OperatingSystem -eq 'Win32NT') { Write-Warning "ANSI not supported: Virtual terminal processing is not available. $($results.Details.EnableInstructions)" } } # Display terminal-specific warnings ForEach ($warning in $results.Details.Warnings) { Write-Warning $warning } } # Return the full results object (includes ColorSupport and SupportsBoldFonts) Return $results } |