Private/New-GradientColorArray.ps1

function New-GradientColorArray {
    <#
    .SYNOPSIS
    Generates a gradient color array for character-by-character coloring
 
    .DESCRIPTION
    Interpolates colors evenly between 2 or more waypoint colors, one color per step,
    as RGB arrays (TrueColor) or ANSI 256-color codes (ANSI8).
 
    .PARAMETER Colors
    Array of gradient waypoints (minimum 2 colors).
    Accepts color names, hex codes, or RGB arrays.
 
    .PARAMETER Steps
    Number of colors to generate (typically total character count)
 
    .PARAMETER Mode
    Color mode: 'TrueColor' returns RGB arrays, 'ANSI8' returns ANSI 256-color codes
 
    .PARAMETER Space
    The space colors are blended in: 'OKLab', where equal steps look equally far apart and the
    brightness stays even, or 'RGB', each channel on its own. The first and last step of each
    stretch between two waypoints are the waypoints themselves.
 
    .EXAMPLE
    New-GradientColorArray -Colors @("Red","Blue") -Steps 10 -Mode TrueColor
    Returns 10 RGB arrays interpolated from Red to Blue
 
    .EXAMPLE
    New-GradientColorArray -Colors @("#FF0000","#00FF00","#0000FF") -Steps 20 -Mode ANSI8
    Returns 20 ANSI8 codes for a red-green-blue gradient
 
    .OUTPUTS
    System.Array
    Returns an array of color values matching the Steps count:
    - TrueColor mode: Array of RGB arrays @(R,G,B)
    - ANSI8 mode: Array of ANSI 256-color codes (integers 0-255)
 
    .NOTES
    Author: Mark Newton
    License: MIT
    Requires: PowerShell 5.1 or later
 
    Write-ColorEX calls this private function for -Gradient and -BackGroundGradient. Each step's
    color is a linear interpolation between the two waypoints around it, in OKLab or in RGB. A
    color name not in the color table and an invalid hex code are gray. The result of each set of
    waypoints, steps, mode and space is kept for the session, so a gradient written again costs a
    lookup.
 
    .LINK
    https://github.com/MarkusMcNugen/PSWriteColorEX
 
    .LINK
    Write-ColorEX
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object[]]$Colors,

        [Parameter(Mandatory)]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Steps,

        [Parameter(Mandatory)]
        [ValidateSet('TrueColor', 'ANSI8')]
        [string]$Mode,

        [ValidateSet('OKLab', 'RGB')]
        [string]$Space = 'OKLab'
    )

    # Validate colors count
    if ($Colors.Count -lt 2) {
        throw "Gradient requires at least 2 colors (received $($Colors.Count))"
    }

    # Every waypoint as RGB
    $rgbColors = [System.Collections.Generic.List[array]]::new($Colors.Count)

    if ($null -eq $script:CachedColorTable) {
        $script:CachedColorTable = Get-ColorTableWithRGB
    }

    foreach ($color in $Colors) {
        if ($color -is [array] -and $color.Count -eq 3) {
            $null = $rgbColors.Add(@(
                [Math]::Max(0, [Math]::Min(255, [int]$color[0])),
                [Math]::Max(0, [Math]::Min(255, [int]$color[1])),
                [Math]::Max(0, [Math]::Min(255, [int]$color[2]))
            ))
        } elseif ($color -is [string] -and $color -match '^#|^0x') {
            $null = $rgbColors.Add((Convert-HexToRGB -Hex $color))
        } else {
            $colorEntry = $script:CachedColorTable[$color]
            if ($colorEntry) {
                $null = $rgbColors.Add($colorEntry[4])
            } else {
                $null = $rgbColors.Add(@(128, 128, 128))
            }
        }
    }

    $cacheKey = '{0}|{1}|{2}|{3}' -f $Mode, $Space, $Steps, (($rgbColors | ForEach-Object { $_ -join ',' }) -join ';')
    $cached = $script:GradientCache[$cacheKey]
    if ($null -ne $cached) {
        return ,$cached
    }

    $result = [ColorMath]::Blend($rgbColors.ToArray(), $Steps, $Mode -eq 'ANSI8', $Space -eq 'OKLab')
    if ($script:GradientCache.Count -ge 256) {
        $script:GradientCache.Clear()
    }
    $script:GradientCache[$cacheKey] = $result

    # The comma keeps the array whole rather than unrolled into the pipeline
    return ,$result
}

# The gradients built so far, by mode, space, steps and waypoints
$script:GradientCache = @{}