Public/Convert-ColorValue.ps1

# Color conversion utility functions

function Convert-HexToRGB {
    <#
    .SYNOPSIS
    Converts hex color codes to RGB values
    
    .DESCRIPTION
    Converts hex color codes (#RRGGBB or 0xRRGGBB) to RGB array
    
    .PARAMETER Hex
    The hex color code to convert
    
    .EXAMPLE
    Convert-HexToRGB "#FF8000"
    Returns: @(255, 128, 0)

    .OUTPUTS
    System.Array
    Returns a 3-element integer array @(R, G, B) with values 0-255.
    Invalid input returns gray @(128, 128, 128).

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    Accepts multiple hex formats: #RRGGBB, 0xRRGGBB, RRGGBB
    Gracefully handles invalid input by defaulting to gray.

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Write-ColorEX
    #>

    [CmdletBinding()]
    [Alias('CHR', 'Hex2RGB')]
    param(
        [Parameter(Mandatory)]
        [string]$Hex
    )
    
    # Remove # or 0x prefix
    $Hex = $Hex -replace '^#|^0x', ''
    
    # Validate hex string
    if ($Hex -notmatch '^[0-9A-Fa-f]{6}$') {
        Write-Warning "Invalid hex color format: $Hex. Expected format: #RRGGBB or RRGGBB"
        return @(128, 128, 128)  # Default to gray
    }
    
    # Convert to RGB
    $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)
}

function Convert-RGBToANSI8 {
    <#
    .SYNOPSIS
    Converts RGB values to closest ANSI 8-bit color
    
    .DESCRIPTION
    Finds the closest ANSI 256-color palette match for RGB values
    
    .PARAMETER RGB
    Array of RGB values [R, G, B]
    
    .EXAMPLE
    Convert-RGBToANSI8 @(255, 128, 0)
    Returns: 208 (closest orange in 256-color palette)

    .OUTPUTS
    System.Int32
    Returns an integer between 0-255 representing the closest ANSI 256-color code.
    Uses 6x6x6 color cube (16-231) for colors and 24-step grayscale ramp (232-255) for grays.

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    Uses intelligent grayscale detection (R≈G≈B within 10) to choose between color cube and grayscale ramp.
    Performance optimized with RGB6LevelLookup table (5-10x faster than conditional logic).

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Convert-HexToRGB

    .LINK
    Write-ColorEX
    #>

    [CmdletBinding()]
    [Alias('CRA8', 'RGB2ANSI8')]
    param(
        [Parameter(Mandatory)]
        [int[]]$RGB
    )
    
    $r = $RGB[0]
    $g = $RGB[1]
    $b = $RGB[2]
    
    # Check if it's grayscale
    if ([Math]::Abs($r - $g) -lt 10 -and [Math]::Abs($g - $b) -lt 10) {
        # Use grayscale ramp (colors 232-255)
        $gray = [Math]::Round(($r + $g + $b) / 3)
        if ($gray -lt 8) {
            return 16  # Black
        } elseif ($gray -gt 248) {
            return 231  # White
        } else {
            # Map to 24 grayscale colors (232-255)
            $grayIndex = [Math]::Round(($gray - 8) / 10)
            return 232 + [Math]::Min(23, $grayIndex)
        }
    }
    
    # Map to 6x6x6 color cube (colors 16-231)
    # Convert RGB to 6-level values (0-5) using lookup table
    if ($null -eq $script:RGB6LevelLookup) {
        # Fallback if lookup table not initialized
        $r6 = if ($r -lt 48) { 0 } elseif ($r -lt 115) { 1 } elseif ($r -lt 155) { 2 } elseif ($r -lt 195) { 3 } elseif ($r -lt 235) { 4 } else { 5 }
        $g6 = if ($g -lt 48) { 0 } elseif ($g -lt 115) { 1 } elseif ($g -lt 155) { 2 } elseif ($g -lt 195) { 3 } elseif ($g -lt 235) { 4 } else { 5 }
        $b6 = if ($b -lt 48) { 0 } elseif ($b -lt 115) { 1 } elseif ($b -lt 155) { 2 } elseif ($b -lt 195) { 3 } elseif ($b -lt 235) { 4 } else { 5 }
    } else {
        # Fast lookup table access
        $r6 = $script:RGB6LevelLookup[$r]
        $g6 = $script:RGB6LevelLookup[$g]
        $b6 = $script:RGB6LevelLookup[$b]
    }

    return 16 + (36 * $r6) + (6 * $g6) + $b6
}

function Convert-RGBToANSI4 {
    <#
    .SYNOPSIS
    Converts RGB values to closest ANSI 4-bit color
    
    .DESCRIPTION
    Finds the closest ANSI 16-color match for RGB values
    
    .PARAMETER RGB
    Array of RGB values [R, G, B]
    
    .EXAMPLE
    Convert-RGBToANSI4 @(255, 128, 0)
    Returns: 33 (Yellow in 16-color palette)

    .OUTPUTS
    System.Int32
    Returns an ANSI 4-bit foreground color code:
    - Normal colors: 30-37 (Black, Red, Green, Yellow, Blue, Magenta, Cyan, White)
    - Bright colors: 90-97 (Bright versions of above)
    Brightness determined by maximum RGB channel value (≥200 is bright).

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    Uses intelligent color matching based on dominant RGB channels and brightness analysis.
    Returns foreground codes only (add 10 for background codes: 40-47, 100-107).

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Convert-HexToRGB

    .LINK
    Write-ColorEX
    #>

    [CmdletBinding()]
    [Alias('CRA4', 'RGB2ANSI4')]
    param(
        [Parameter(Mandatory)]
        [int[]]$RGB
    )
    
    $r = $RGB[0]
    $g = $RGB[1]
    $b = $RGB[2]

    # Find dominant color channel
    $max = [Math]::Max($r, [Math]::Max($g, $b))
    $min = [Math]::Min($r, [Math]::Min($g, $b))

    # Calculate brightness (for grayscale detection)
    $brightness = ($r + $g + $b) / 3

    # Determine if it's a bright or dark color based on max channel value
    # For saturated colors like (255, 0, 0), we want to detect as "bright"
    $isBright = $max -ge 200
    
    # Check for grayscale
    if ($max - $min -lt 30) {
        if ($brightness -lt 64) {
            return 30  # Black
        } elseif ($brightness -lt 128) {
            return 90  # Dark Gray
        } elseif ($brightness -lt 192) {
            return 37  # Gray
        } else {
            return 97  # White
        }
    }
    
    # Determine color based on dominant channels
    if ($r -eq $max) {
        if ($g -gt $b + 30) {
            # Yellow range
            if ($isBright) { return 93 } else { return 33 }
        } elseif ($b -gt $g + 30) {
            # Magenta range
            if ($isBright) { return 95 } else { return 35 }
        } else {
            # Red
            if ($isBright) { return 91 } else { return 31 }
        }
    } elseif ($g -eq $max) {
        if ($r -gt $b + 30) {
            # Yellow range
            if ($isBright) { return 93 } else { return 33 }
        } elseif ($b -gt $r + 30) {
            # Cyan range
            if ($isBright) { return 96 } else { return 36 }
        } else {
            # Green
            if ($isBright) { return 92 } else { return 32 }
        }
    } else {
        # Blue is max
        if ($r -gt $g + 30) {
            # Magenta range
            if ($isBright) { return 95 } else { return 35 }
        } elseif ($g -gt $r + 30) {
            # Cyan range
            if ($isBright) { return 96 } else { return 36 }
        } else {
            # Blue
            if ($isBright) { return 94 } else { return 34 }
        }
    }
}

function Get-ColorTableWithRGB {
    <#
    .SYNOPSIS
    Returns the complete color table with RGB values

    .DESCRIPTION
    Returns a comprehensive hashtable containing 70+ color families with Dark/Normal/Light variants.
    Each color entry maps to all supported color modes for seamless conversion.

    ENTRY FORMAT:
    @(Native, ANSI4FG, ANSI4BG, ANSI8, @(R,G,B))
    - Native: PowerShell ConsoleColor name
    - ANSI4FG: 4-bit foreground code (30-37, 90-97)
    - ANSI4BG: 4-bit background code (40-47, 100-107)
    - ANSI8: 8-bit color code (0-255)
    - RGB: RGB array @(R, G, B)

    COLOR FAMILIES INCLUDED:
    Neutrals, Red, Green, Blue, Yellow, Cyan, Magenta, Orange, Purple, Pink, Brown,
    Teal, Violet, Lime, Slate, Gold, Sky, Coral, Olive, Lavender, Mint, Salmon,
    Indigo, Turquoise, Ruby, Jade, Amber, Steel, Crimson, Emerald, Sapphire, and more.

    .EXAMPLE
    $colors = Get-ColorTableWithRGB
    $orange = $colors['Orange']
    # $orange[0] = 'DarkYellow' (Native PowerShell)
    # $orange[1] = 33 (ANSI 4-bit FG)
    # $orange[2] = 43 (ANSI 4-bit BG)
    # $orange[3] = 208 (ANSI 8-bit)
    # $orange[4] = @(255,165,0) (RGB)

    .EXAMPLE
    $table = Get-ColorTableWithRGB
    $table.Keys | Sort-Object
    # Lists all 70+ available color names

    .OUTPUTS
    System.Collections.Hashtable
    Returns a hashtable where:
    - Keys: Color names (strings)
    - Values: 5-element arrays with Native, ANSI4FG, ANSI4BG, ANSI8, RGB data

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    This table is cached in $script:CachedColorTable during module initialization
    for performance (~1000x faster repeated access).

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Write-ColorEX

    .LINK
    Convert-RGBToANSI8

    .LINK
    Convert-RGBToANSI4
    #>

    [CmdletBinding()]
    [Alias('GCT', 'Get-ColorTable', 'Get-ColourTable')]
    param()
    
    return @{
        # Neutral family
        Black = @('Black', 30, 40, 0, @(0, 0, 0))
        LightBlack = @('DarkGray', 90, 100, 238, @(118, 118, 118))
        DarkGray = @('DarkGray', 90, 100, 8, @(128, 128, 128))
        Gray = @('Gray', 37, 47, 7, @(192, 192, 192))
        LightGray = @('Gray', 37, 47, 253, @(238, 238, 238))
        White = @('White', 97, 107, 15, @(255, 255, 255))
        
        # Red family
        DarkRed = @('DarkRed', 31, 41, 52, @(139, 0, 0))
        Red = @('Red', 31, 41, 1, @(255, 0, 0))
        LightRed = @('Red', 91, 101, 9, @(255, 85, 85))
        
        # Green family
        DarkGreen = @('DarkGreen', 32, 42, 28, @(0, 100, 0))
        Green = @('Green', 32, 42, 2, @(0, 255, 0))
        LightGreen = @('Green', 92, 102, 10, @(85, 255, 85))
        
        # Yellow family
        DarkYellow = @('DarkYellow', 33, 43, 136, @(204, 204, 0))
        Yellow = @('Yellow', 33, 43, 220, @(255, 255, 0))
        LightYellow = @('Yellow', 93, 103, 11, @(255, 255, 85))
        
        # Blue family
        DarkBlue = @('DarkBlue', 34, 44, 19, @(0, 0, 139))
        Blue = @('Blue', 34, 44, 4, @(0, 0, 255))
        LightBlue = @('Blue', 94, 104, 12, @(85, 85, 255))
        
        # Magenta family
        DarkMagenta = @('DarkMagenta', 35, 45, 53, @(139, 0, 139))
        Magenta = @('Magenta', 35, 45, 5, @(255, 0, 255))
        LightMagenta = @('Magenta', 95, 105, 13, @(255, 85, 255))
        
        # Cyan family
        DarkCyan = @('DarkCyan', 36, 46, 30, @(0, 139, 139))
        Cyan = @('Cyan', 36, 46, 6, @(0, 255, 255))
        LightCyan = @('Cyan', 96, 106, 14, @(85, 255, 255))
        
        # Orange family
        DarkOrange = @('DarkYellow', 33, 43, 166, @(255, 140, 0))
        Orange = @('DarkYellow', 33, 43, 208, @(255, 165, 0))
        LightOrange = @('Yellow', 33, 43, 215, @(255, 195, 0))
        
        # Purple family
        DarkPurple = @('DarkMagenta', 35, 45, 54, @(75, 0, 130))
        Purple = @('DarkMagenta', 35, 45, 93, @(128, 0, 128))
        LightPurple = @('Magenta', 35, 45, 135, @(147, 112, 219))
        
        # Pink family
        DarkPink = @('DarkMagenta', 35, 45, 163, @(199, 21, 133))
        Pink = @('Magenta', 35, 45, 205, @(255, 192, 203))
        LightPink = @('Magenta', 95, 105, 218, @(255, 182, 193))
        
        # Brown family
        DarkBrown = @('DarkRed', 31, 41, 88, @(101, 67, 33))
        Brown = @('DarkRed', 31, 41, 130, @(150, 75, 0))
        LightBrown = @('DarkYellow', 33, 43, 173, @(205, 133, 63))
        
        # Teal family
        DarkTeal = @('DarkCyan', 36, 46, 23, @(0, 128, 128))
        Teal = @('DarkCyan', 36, 46, 30, @(0, 150, 150))
        LightTeal = @('Cyan', 36, 46, 80, @(64, 224, 208))
        
        # Violet family
        DarkViolet = @('DarkMagenta', 35, 45, 128, @(148, 0, 211))
        Violet = @('Magenta', 35, 45, 134, @(238, 130, 238))
        LightViolet = @('Magenta', 95, 105, 177, @(200, 162, 200))
        
        # Lime family
        DarkLime = @('DarkGreen', 32, 42, 34, @(50, 205, 50))
        Lime = @('Green', 32, 42, 118, @(0, 255, 0))
        LightLime = @('Green', 92, 102, 119, @(50, 255, 50))
        
        # Slate family
        DarkSlate = @('DarkGray', 90, 100, 238, @(47, 79, 79))
        Slate = @('Gray', 37, 47, 102, @(112, 128, 144))
        LightSlate = @('Gray', 37, 47, 103, @(119, 136, 153))
        
        # Gold family
        DarkGold = @('DarkYellow', 33, 43, 136, @(184, 134, 11))
        Gold = @('Yellow', 33, 43, 178, @(255, 215, 0))
        LightGold = @('Yellow', 93, 103, 185, @(255, 223, 0))
        
        # Sky family
        DarkSky = @('DarkBlue', 34, 44, 24, @(0, 191, 255))
        Sky = @('Blue', 34, 44, 111, @(135, 206, 235))
        LightSky = @('Cyan', 36, 46, 152, @(135, 206, 250))
        
        # Coral family
        DarkCoral = @('DarkRed', 31, 41, 167, @(205, 91, 69))
        Coral = @('Red', 31, 41, 209, @(255, 127, 80))
        LightCoral = @('Red', 91, 101, 210, @(240, 128, 128))
        
        # Olive family
        DarkOlive = @('DarkGreen', 32, 42, 58, @(85, 107, 47))
        Olive = @('DarkYellow', 33, 43, 100, @(128, 128, 0))
        LightOlive = @('DarkYellow', 33, 43, 107, @(170, 170, 0))
        
        # Lavender family
        DarkLavender = @('DarkMagenta', 35, 45, 97, @(100, 100, 150))
        Lavender = @('Magenta', 35, 45, 183, @(230, 230, 250))
        LightLavender = @('Magenta', 95, 105, 189, @(240, 240, 255))
        
        # Mint family
        DarkMint = @('DarkGreen', 32, 42, 29, @(60, 179, 113))
        Mint = @('Green', 32, 42, 121, @(152, 251, 152))
        LightMint = @('Green', 92, 102, 157, @(189, 252, 201))
        
        # Salmon family
        DarkSalmon = @('DarkRed', 31, 41, 173, @(233, 150, 122))
        Salmon = @('Red', 31, 41, 174, @(250, 128, 114))
        LightSalmon = @('Red', 91, 101, 175, @(255, 160, 122))
        
        # Indigo family
        DarkIndigo = @('DarkBlue', 34, 44, 17, @(25, 25, 112))
        Indigo = @('DarkMagenta', 35, 45, 54, @(75, 0, 130))
        LightIndigo = @('Blue', 34, 44, 61, @(102, 102, 153))
        
        # Turquoise family
        DarkTurquoise = @('DarkCyan', 36, 46, 31, @(0, 206, 209))
        Turquoise = @('Cyan', 36, 46, 43, @(64, 224, 208))
        LightTurquoise = @('Cyan', 96, 106, 86, @(175, 238, 238))
        
        # Ruby family
        DarkRuby = @('DarkRed', 31, 41, 52, @(155, 17, 30))
        Ruby = @('Red', 31, 41, 124, @(224, 17, 95))
        LightRuby = @('Red', 91, 101, 161, @(255, 102, 153))
        
        # Jade family
        DarkJade = @('DarkGreen', 32, 42, 22, @(0, 100, 50))
        Jade = @('DarkGreen', 32, 42, 35, @(0, 168, 107))
        LightJade = @('Green', 32, 42, 79, @(64, 216, 143))
        
        # Amber family
        DarkAmber = @('DarkYellow', 33, 43, 130, @(255, 160, 0))
        Amber = @('Yellow', 33, 43, 214, @(255, 191, 0))
        LightAmber = @('Yellow', 93, 103, 221, @(255, 204, 0))
        
        # Steel family
        DarkSteel = @('DarkGray', 90, 100, 60, @(70, 70, 70))
        Steel = @('Gray', 37, 47, 66, @(113, 121, 126))
        LightSteel = @('White', 97, 47, 146, @(176, 196, 222))
        
        # Crimson family
        DarkCrimson = @('DarkRed', 31, 41, 88, @(139, 0, 0))
        Crimson = @('Red', 31, 41, 160, @(220, 20, 60))
        LightCrimson = @('Red', 91, 101, 161, @(248, 48, 88))
        
        # Emerald family
        DarkEmerald = @('DarkGreen', 32, 42, 22, @(0, 100, 0))
        Emerald = @('Green', 32, 42, 36, @(80, 200, 120))
        LightEmerald = @('Green', 92, 102, 85, @(128, 255, 170))
        
        # Sapphire family
        DarkSapphire = @('DarkBlue', 34, 44, 18, @(8, 37, 103))
        Sapphire = @('Blue', 34, 44, 25, @(15, 82, 186))
        LightSapphire = @('Blue', 94, 104, 69, @(100, 149, 237))
        
        # Wine family
        DarkWine = @('DarkRed', 31, 41, 52, @(72, 0, 25))
        Wine = @('DarkRed', 31, 41, 88, @(114, 47, 55))
        LightWine = @('Red', 31, 41, 125, @(179, 97, 115))
        
        # Additional color families
        # Peach family
        DarkPeach = @('DarkYellow', 33, 43, 172, @(255, 164, 96))
        Peach = @('Yellow', 33, 43, 216, @(255, 218, 185))
        LightPeach = @('Yellow', 93, 103, 223, @(255, 239, 213))
        
        # Navy family
        DarkNavy = @('DarkBlue', 34, 44, 17, @(0, 0, 80))
        Navy = @('DarkBlue', 34, 44, 18, @(0, 0, 128))
        LightNavy = @('Blue', 34, 44, 24, @(0, 0, 205))
        
        # Forest family
        DarkForest = @('DarkGreen', 32, 42, 22, @(34, 75, 34))
        Forest = @('DarkGreen', 32, 42, 28, @(34, 139, 34))
        LightForest = @('Green', 32, 42, 34, @(50, 205, 50))
        
        # Rose family
        DarkRose = @('DarkMagenta', 35, 45, 125, @(128, 0, 64))
        Rose = @('Magenta', 35, 45, 168, @(255, 0, 127))
        LightRose = @('Magenta', 95, 105, 211, @(255, 182, 193))
        
        # Plum family
        DarkPlum = @('DarkMagenta', 35, 45, 89, @(102, 51, 153))
        Plum = @('Magenta', 35, 45, 133, @(221, 160, 221))
        LightPlum = @('Magenta', 95, 105, 176, @(238, 174, 238))
        
        # Tan family
        DarkTan = @('DarkYellow', 33, 43, 94, @(139, 90, 43))
        Tan = @('Yellow', 33, 43, 180, @(210, 180, 140))
        LightTan = @('Yellow', 93, 103, 187, @(245, 222, 179))
        
        # Maroon family
        DarkMaroon = @('DarkRed', 31, 41, 52, @(69, 0, 0))
        Maroon = @('DarkRed', 31, 41, 88, @(128, 0, 0))
        LightMaroon = @('Red', 31, 41, 124, @(176, 48, 96))
        
        # Aqua family (alternative cyan)
        DarkAqua = @('DarkCyan', 36, 46, 30, @(0, 128, 128))
        Aqua = @('Cyan', 36, 46, 6, @(0, 255, 255))
        LightAqua = @('Cyan', 96, 106, 14, @(127, 255, 255))
        
        # Chartreuse family
        DarkChartreuse = @('DarkGreen', 32, 42, 64, @(69, 139, 0))
        Chartreuse = @('Green', 32, 42, 118, @(127, 255, 0))
        LightChartreuse = @('Green', 92, 102, 154, @(191, 255, 127))
        
        # Brick family
        DarkBrick = @('DarkRed', 31, 41, 88, @(139, 26, 26))
        Brick = @('DarkRed', 31, 41, 124, @(178, 34, 34))
        LightBrick = @('Red', 31, 41, 167, @(205, 92, 92))
    }
}

function Lighten-RGBColor {
    <#
    .SYNOPSIS
    Lightens an RGB color for terminals that don't support true bold fonts

    .DESCRIPTION
    Multiplies RGB values to create a lighter variant, used when Bold style
    is applied in terminals that only brighten colors instead of rendering bold fonts

    .PARAMETER RGB
    Array of RGB values [R, G, B]

    .PARAMETER Factor
    Lightening factor (default 1.4 for 40% lighter)

    .EXAMPLE
    Lighten-RGBColor @(139, 0, 0)
    Returns: @(195, 0, 0) - 40% lighter red

    .EXAMPLE
    Lighten-RGBColor @(50, 50, 50) -Factor 2.0
    Returns: @(100, 100, 100) - Doubled brightness (2x factor)

    .OUTPUTS
    System.Array
    Returns a 3-element integer array @(R, G, B) with lightened values, clamped to 0-255 range.

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    This function is automatically called by Write-ColorEX when Bold styling is applied
    in terminals that don't support true bold fonts (e.g., PowerShell 5.1, conhost.exe).

    Default 1.4 factor provides 40% brighter colors, visually simulating bold effect.

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Write-ColorEX

    .LINK
    Test-AnsiSupport
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [int[]]$RGB,

        [double]$Factor = 1.4
    )

    # Calculate minimum lightening amount (40% of 255 = ~102)
    # This ensures pure black (0,0,0) becomes dark gray instead of staying black
    $minLighten = [Math]::Round(255 * ($Factor - 1.0))

    $r = [Math]::Min(255, [Math]::Max($minLighten, [Math]::Round($RGB[0] * $Factor)))
    $g = [Math]::Min(255, [Math]::Max($minLighten, [Math]::Round($RGB[1] * $Factor)))
    $b = [Math]::Min(255, [Math]::Max($minLighten, [Math]::Round($RGB[2] * $Factor)))

    return @([int]$r, [int]$g, [int]$b)
}

function Lighten-ANSI8Color {
    <#
    .SYNOPSIS
    Lightens an ANSI8 (256-color) code for terminals that don't support true bold fonts

    .DESCRIPTION
    Algorithmically lightens ANSI 256-color codes when Bold style is applied in terminals
    that only brighten colors instead of rendering bold fonts. Works across all three ANSI8
    color ranges: standard colors (0-15), RGB cube (16-231), and grayscale ramp (232-255).

    This provides a more robust lightening solution compared to color-name-based shifting,
    as it works with direct ANSI8 codes, named colors, and can lighten colors beyond their
    predefined family ranges.

    .PARAMETER ANSI8Code
    ANSI 256-color code to lighten (0-255)

    .PARAMETER Factor
    Lightening factor (default 1.4 for 40% lighter, matching TrueColor behavior)

    .EXAMPLE
    Lighten-ANSI8Color 196
    Returns: 9 (brightened red)
    # ANSI8 code 196 is bright red in RGB cube, lightened to bright red variant

    .EXAMPLE
    Lighten-ANSI8Color 240 -Factor 1.4
    Returns: 246 (lighter gray)
    # Grayscale ramp: moves up ~6 steps for 40% brightness increase

    .EXAMPLE
    Lighten-ANSI8Color 4
    Returns: 12 (bright blue)
    # Standard colors: 4 (dark blue) → 12 (bright blue)

    .OUTPUTS
    System.Int32
    Returns an integer between 0-255 representing the lightened ANSI 256-color code.

    Processing varies by input range:
    - Standard colors (0-15): Maps dark (0-7) to bright (8-15), or converts to RGB cube
    - RGB cube (16-231): Converts to RGB, lightens mathematically, converts back
    - Grayscale ramp (232-255): Increments by ~25% brightness steps

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    This function is automatically called by Write-ColorEX when Bold styling is applied
    in ANSI8 color mode on terminals without true bold font support.

    Benefits over color-name-based lightening:
    - Works with direct ANSI8 codes: Write-ColorEX -Text "Test" -Color 196 -Bold -ANSI8
    - Works with named colors (converted to ANSI8 first)
    - Can lighten "Light*" colors beyond their predefined family
    - Consistent with TrueColor lightening (same 1.4 factor)
    - No need to maintain color family mappings

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Lighten-RGBColor

    .LINK
    Convert-RGBToANSI8

    .LINK
    Write-ColorEX
    #>

    [CmdletBinding()]
    [Alias('LA8', 'Lighten-ANSI8')]
    param(
        [Parameter(Mandatory)]
        [ValidateRange(0, 255)]
        [int]$ANSI8Code,

        [double]$Factor = 1.4
    )

    # Standard colors (0-15): Map to brighter variants or RGB cube
    if ($ANSI8Code -le 15) {
        # Map dark colors (0-7) to bright variants (8-15)
        if ($ANSI8Code -le 7) {
            return $ANSI8Code + 8
        }
        # Already bright (8-15), convert to RGB, lighten, and convert back
        # Standard ANSI color RGB mappings
        $standardRGB = @(
            @(0, 0, 0),       # 0: Black
            @(128, 0, 0),     # 1: Dark Red (Maroon)
            @(0, 128, 0),     # 2: Dark Green
            @(128, 128, 0),   # 3: Dark Yellow (Olive)
            @(0, 0, 128),     # 4: Dark Blue (Navy)
            @(128, 0, 128),   # 5: Dark Magenta (Purple)
            @(0, 128, 128),   # 6: Dark Cyan (Teal)
            @(192, 192, 192), # 7: Light Gray
            @(128, 128, 128), # 8: Dark Gray
            @(255, 0, 0),     # 9: Red
            @(0, 255, 0),     # 10: Green
            @(255, 255, 0),   # 11: Yellow
            @(0, 0, 255),     # 12: Blue
            @(255, 0, 255),   # 13: Magenta
            @(0, 255, 255),   # 14: Cyan
            @(255, 255, 255)  # 15: White
        )

        $rgb = $standardRGB[$ANSI8Code]
        $lightenedRGB = Lighten-RGBColor -RGB $rgb -Factor $Factor
        return Convert-RGBToANSI8 -RGB $lightenedRGB
    }

    # Grayscale ramp (232-255): Increment by steps
    if ($ANSI8Code -ge 232) {
        $grayLevel = $ANSI8Code - 232  # 0-23
        # Add approximately 25% brightness (~6 steps out of 24)
        $increment = [Math]::Max(1, [Math]::Round(($Factor - 1.0) * 10))
        $newLevel = [Math]::Min(23, $grayLevel + $increment)
        return 232 + $newLevel
    }

    # RGB cube (16-231): Convert to RGB, lighten, convert back
    # ANSI8 color = 16 + (36 × r) + (6 × g) + b
    # where r,g,b are 0-5 (mapped to 0,95,135,175,215,255 in RGB)

    $index = $ANSI8Code - 16
    $r6 = [Math]::Floor($index / 36)
    $g6 = [Math]::Floor(($index % 36) / 6)
    $b6 = $index % 6

    # Convert 0-5 to RGB 0-255
    $rgbLevels = @(0, 95, 135, 175, 215, 255)
    $rgb = @(
        $rgbLevels[$r6],
        $rgbLevels[$g6],
        $rgbLevels[$b6]
    )

    # Lighten RGB values
    $lightenedRGB = Lighten-RGBColor -RGB $rgb -Factor $Factor

    # Convert back to ANSI8
    return Convert-RGBToANSI8 -RGB $lightenedRGB
}

function Lighten-ColorName {
    <#
    .SYNOPSIS
    Lightens a color name for terminals that don't support true bold fonts

    .DESCRIPTION
    Shifts color names from Dark→Normal→Light variants for ANSI4/ANSI8 modes
    when Bold style is applied in terminals that only brighten colors

    .PARAMETER ColorName
    Color name to lighten (e.g., "DarkRed", "Red")

    .EXAMPLE
    Lighten-ColorName "DarkRed"
    Returns: "Red"

    .EXAMPLE
    Lighten-ColorName "Red"
    Returns: "LightRed"

    .EXAMPLE
    Lighten-ColorName "LightGreen"
    Returns: "LightGreen" (already at lightest, no change)

    .OUTPUTS
    System.String
    Returns the lightened color name using the following rules:
    - Dark* → Remove "Dark" prefix (e.g., DarkRed → Red)
    - Normal → Add "Light" prefix (e.g., Red → LightRed)
    - Light* → Return unchanged (already lightest)

    .NOTES
    Author: MarkusMcNugen
    License: MIT
    Requires: PowerShell 5.1 or later

    This function is automatically called by Write-ColorEX when Bold styling is applied
    in ANSI4/ANSI8 color modes on terminals without true bold font support.

    Works with all color families in the PSWriteColorEX color table.

    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX

    .LINK
    Lighten-RGBColor

    .LINK
    Write-ColorEX

    .LINK
    Get-ColorTableWithRGB
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ColorName
    )

    # If already Light*, return as-is (can't lighten further)
    if ($ColorName -like 'Light*') {
        return $ColorName
    }

    # If Dark*, remove Dark prefix to get normal variant
    if ($ColorName -like 'Dark*') {
        return $ColorName -replace '^Dark', ''
    }

    # Otherwise, add Light prefix
    return "Light$ColorName"
}