Theme.Terminal.psm1

using namespace PoshCode.Pansies
#Region '.\Private\SelectCapturedString.ps1' 0
function SelectCapturedString {
    <#
        .Synopsis
            Collect named capture groups from regular expression matches
            (I still don't like the name of this function, and it should be in some other module)
        .Description
            Takes string data and a regular expression containing named captures,
            and outputs all of the resulting captures in one (or more) hashtable(s)
        .Example
            netstat | Select-CapturedString "(?<Protocol>\w{3})\s+(?<LocalIP>(\d{1,3}\.){3}\d{1,3}):(?<LocalPort>\d+)\s+(?<ForeignIP>.*):(?<ForeignPort>\d+)\s+(?<State>\w+)?"
            This is an example of how to use it for parsing when all the values are on one line
        .Example
            "Revoked Certificates:
                Serial Number: 011F63068E6BCD8CABF644026B80A903
                    Revocation Date: Jul 8 06:22:01 2012 GMT
                Serial Number: 01205F0018B6758D741B3DB43CFB26C2
                    Revocation Date: Feb 18 06:11:14 2013 GMT
                Serial Number: 012607175D820413ED0750E96B833A8F
                    Revocation Date: Jun 11 03:12:11 2015 GMT
            " | Select-CapturedString "(?m)Serial Number:\s+(?<SerialNumber>.*)\s*$|Revocation Date:\s+(?<RevocationDate>.*)\s*$" -Auto
            SerialNumber RevocationDate
            ------------ --------------
            011F63068E6BCD8CABF644026B80A903 Jul 8 06:22:01 2012 GMT
            01205F0018B6758D741B3DB43CFB26C2 Feb 18 06:11:14 2013 GMT
            012607175D820413ED0750E96B833A8F Jun 11 03:12:11 2015 GMT
            When your values are on multiple lines, you can use the -AutoGroup switch to automatically collect sets of matches.
    #>

    param(
        # The text to search for captures
        [Parameter(ValueFromPipeline = $true)]
        [string]$text,

        # A regular expression containing named capture groups (see examples)
        [Parameter(Position = 1)]
        [regex]$re,

        # By default, each match is returned as a single object.
        # When set, empty captures are ignored, and properties are collected until the capture groups repeat, allowing the collection of many lines using an OR regex (see Example 2)
        [switch]$AutoGroup,

        # If set, hide properties with empty values (default to the same as $AutoGroup)
        [switch]$HideEmpty = $AutoGroup
    )
    begin {
        [string[]]$FullData = $text
    }
    process {
        [string[]]$FullData += $text
    }
    end {
        $text = $FullData -join "`n"
        if ($VerbosePreference -eq "Continue") {
            Write-Verbose "Regex $re"
            Write-Verbose "Data $(-join $text.GetEnumerator().ForEach{ if (27 -ge $_) { [char](0x2400 + $_) } else { "$_" } })"
        }
        $names = $re.GetGroupNames().Where{ $_ -ne 0 }
        $result = [ordered]@{}
        foreach ($match in $re.Matches($text).Where{ $_.Success }) {
            Write-Verbose (-join $match.Value.GetEnumerator().ForEach{ if (27 -ge $_) { [char](0x2400 + $_) } else { "$_" } })
            foreach ($name in $names) {
                if (-not $HideEmpty -or $match.Groups[$name].Value) {
                    if ($AutoGroup -and $result.ContainsKey($name)) {
                        [PSCustomObject]$result
                        $result = [ordered]@{}
                    }
                    $result.$name = $match.Groups[$name].Value
                }
            }
            if (!$AutoGroup) {
                [PSCustomObject]$result
                $result = [ordered]@{}
            }
        }
        if ($result) {
            [PSCustomObject]$result
        }
    }
}
#EndRegion '.\Private\SelectCapturedString.ps1' 79
#Region '.\Public\Convert-ColorScheme.ps1' 0
#using namespace PoshCode.Pansies

function Convert-ColorScheme {
    [CmdletBinding(DefaultParameterSetName = "Dark")]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSTypeName("Terminal.ColorScheme")]
        [Alias("ColorScheme")]
        $InputObject,

        # If set, change the dark colors (use negative values to make them darker)
        [Parameter(Mandatory, ParameterSetName = "Dark", Position = 0)]
        [int]$DarkShift,

        # If set, change the bright colors (use positive values to make them brighter)
        [Parameter(Mandatory, ParameterSetName = "Bright")]
        [int]$BrightShift
    )
    begin {
        $comparer = [PoshCode.Pansies.ColorSpaces.Comparisons.CieDe2000Comparison]::new()
    }
    end {
        # brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightPurple, brightCyan, brightWhite,
        $background = @{
            color    = ([RgbColor]$InputObject.Background).ToHunterLab()
            distance = 360
            closest  = $null
            bright   = $false
        }
        $foreground = @{
            color    = ([RgbColor]$InputObject.Foreground).ToHunterLab()
            distance = 360
            closest  = $null
            bright   = $false
        }

        foreach ($color in "black", "red", "green", "yellow", "blue", "purple", "cyan", "white") {
            $brightColor = "bright$color"
            $Dark  = ([RgbColor]$InputObject.$color).ToHunterLab()
            $Light = ([RgbColor]$InputObject.$brightColor).ToHunterLab()

            if (($distance = $background.color.Compare($Dark, $comparer)) -lt $background.distance) {
                $background.distance = $distance
                $background.closest = $Dark
                $background.bright = $false
            }
            if (($distance = $background.color.Compare($Light, $comparer)) -lt $background.distance) {
                $background.distance = $distance
                $background.closest = $Light
                $background.bright = $true
            }
            if (($distance = $foreground.color.Compare($Dark, $comparer)) -lt $foreground.distance) {
                $foreground.distance = $distance
                $foreground.closest = $Dark
                $foreground.bright = $false
            }
            if (($distance = $foreground.color.Compare($Light, $comparer)) -lt $foreground.distance) {
                $foreground.distance = $distance
                $foreground.closest = $Light
                $foreground.bright = $true
            }

            if ($BrightShift) {
                if (100 -ge ($Light.L + $BrightShift)) {
                    $Light.L += $BrightShift
                } else {
                    $Light.L = 100
                }
            }

            if ($DarkShift) {
                if (0 -le ($Dark.L - $DarkShift)) {
                    $Dark.L -= $DarkShift
                } else {
                    $Dark.L = 0
                }
            }

            $InputObject.$color = [RgbColor]$Dark.ToRgb()
            $InputObject.$brightColor = [RgbColor]$Light.ToRgb()
        }

        if ($BrightShift) {
            if ($background.bright) {
                $background.Color.L += $BrightShift
                $InputObject.Background = [RgbColor]$background.Color.ToRgb()
            }
            if ($foreground.bright) {
                $foreground.Color.L += $BrightShift
                $InputObject.Foreground = [RgbColor]$foreground.Color.ToRgb()
            }
        }

        if ($DarkShift) {
            if (!$background.bright) {
                $background.Color.L -= $DarkShift
                $InputObject.Background = [RgbColor]$background.Color.ToRgb()
            }
            if (!$foreground.bright) {
                $foreground.Color.L -= $DarkShift
                $InputObject.Foreground = [RgbColor]$foreground.Color.ToRgb()
            }
        }

        $InputObject
    }
}
#EndRegion '.\Public\Convert-ColorScheme.ps1' 108
#Region '.\Public\Get-iTerm2ColorScheme.ps1' 0
function Get-iTerm2ColorScheme {
    <#
        .Synopsis
            Fetch a colorscheme from https://github.com/mbadolato/iTerm2-Color-Schemes
    #>

    [CmdletBinding()]
    param(
        # The name of an iTerm2 color scheme
        # See https://github.com/mbadolato/iTerm2-Color-Schemes#screenshots for visuals
        [Parameter(Mandatory)]
        [ValidateSet( "3024 Day", "3024 Night", "Adventure", "AdventureTime", "Afterglow", "AlienBlood", "Andromeda", "Argonaut", "Arthur", "AtelierSulphurpool", "Atom", "AtomOneLight", "Banana Blueberry", "Batman", "Belafonte Day", "Belafonte Night", "BirdsOfParadise", "Blazer", "Blue Matrix", "BlueBerryPie", "BlulocoDark", "BlulocoLight", "Borland", "Breeze", "Bright Lights", "Broadcast", "Brogrammer", "Builtin Dark", "Builtin Light", "Builtin Pastel Dark", "Builtin Solarized Dark", "Builtin Solarized Light", "Builtin Tango Dark", "Builtin Tango Light", "C64", "CLRS", "Calamity", "Chalk", "Chalkboard", "ChallengerDeep", "Chester", "Ciapre", "Cobalt Neon", "Cobalt2", "CrayonPonyFish", "Cyberdyne", "Dark Pastel", "Dark+", "Darkside", "Desert", "DimmedMonokai", "Django", "DjangoRebornAgain", "DjangoSmooth", "DoomOne", "DotGov", "Dracula+", "Dracula", "Duotone Dark", "ENCOM", "Earthsong", "Elemental", "Elementary", "Espresso Libre", "Espresso", "Fahrenheit", "Fideloper", "FirefoxDev", "Firewatch", "FishTank", "Flat", "Flatland", "Floraverse", "ForestBlue", "Framer", "FrontEndDelight", "FunForrest", "Galaxy", "Github", "Glacier", "Grape", "Grass", "Gruvbox Dark", "Gruvbox Light", "Hacktober", "Hardcore", "Harper", "Highway", "Hipster Green", "Hivacruz", "Homebrew", "Hopscotch.256", "Hopscotch", "Hurtado", "Hybrid", "IC_Green_PPL", "IC_Orange_PPL", "IR_Black", "Jackie Brown", "Japanesque", "Jellybeans", "JetBrains Darcula", "Kibble", "Kolorit", "Konsolas", "Lab Fox", "Laser", "Later This Evening", "Lavandula", "LiquidCarbon", "LiquidCarbonTransparent", "LiquidCarbonTransparentInverse", "Man Page", "Material", "MaterialDark", "MaterialDarker", "MaterialOcean", "Mathias", "Medallion", "Mirage", "Misterioso", "Molokai", "MonaLisa", "Monokai Remastered", "Monokai Soda", "Monokai Vivid", "N0tch2k", "Neopolitan", "Neutron", "Night Owlish Light", "NightLion v1", "NightLion v2", "Nocturnal Winter", "Novel", "Obsidian", "Ocean", "OceanicMaterial", "Ollie", "OneHalfDark", "OneHalfLight", "Operator Mono Dark", "Overnight Slumber", "Pandora", "Paraiso Dark", "Parasio Dark", "PaulMillr", "PencilDark", "PencilLight", "Piatto Light", "Pnevma", "Popping and Locking", "Pro Light", "Pro", "Purple Rain", "Rapture", "Red Alert", "Red Planet", "Red Sands", "Relaxed", "Rippedcasts", "Rouge 2", "Royal", "Ryuuko", "Sakura", "Scarlet Protocol", "SeaShells", "Seafoam Pastel", "Seti", "Shaman", "Slate", "SleepyHollow", "Smyck", "Snazzy", "SoftServer", "Solarized Darcula", "Solarized Dark - Patched", "Solarized Dark Higher Contrast", "SpaceGray Eighties Dull", "SpaceGray Eighties", "SpaceGray", "Spacedust", "Spiderman", "Spring", "Square", "Subliminal", "Sundried", "Symfonic", "Tango Adapted", "Tango Half Adapted", "Teerb", "Terminal Basic", "Thayer Bright", "The Hulk", "Tinacious Design (Dark)", "Tinacious Design (Light)", "Tomorrow Night Blue", "Tomorrow Night Bright", "Tomorrow Night Burns", "Tomorrow Night Eighties", "Tomorrow Night", "Tomorrow", "ToyChest", "Treehouse", "Twilight", "Ubuntu", "UltraViolent", "UnderTheSea", "Unikitty", "Urple", "Vaughn", "VibrantInk", "Violet Dark", "Violet Light", "WarmNeon", "Wez", "Whimsy", "WildCherry", "Wombat", "Wryan", "Zenburn", "ayu", "ayu_light", "coffee_theme", "cyberpunk", "deep", "idea", "idleToes", "jubi", "lovelace", "midnight-in-mojave", "nord", "primary", "purplepeter", "rebecca", "shades-of-purple", "synthwave-everything", "synthwave" )]
        $Name
    )
    process {
        $FileName = [System.Text.Encodings.Web.UrlEncoder]::Default.Encode($Name) + ".json"
        $ColorScheme = Invoke-RestMethod "https://raw.githubusercontent.com/mbadolato/iTerm2-Color-Schemes/master/windowsterminal/$FileName"

        $ColorScheme.PSTypeNames.Insert(0, "Terminal.ColorScheme")
        $ColorScheme
    }
}
#EndRegion '.\Public\Get-iTerm2ColorScheme.ps1' 22
#Region '.\Public\Get-TerminalColorMode.ps1' 0
function Get-TerminalColorMode {
    <#
        .SYNOPSIS
            Tests for FullColor (RGB) mode and X11/XTerm (XColor) modes by writing SGR and verifying it with a DECRQSS
            Returns "Uknown" if there's no DECRQSS support, or "FullColor" and/or "XColor" otherwise
    #>

    [CmdletBinding()]
    param()

    $ColorMode = @{
        '48:2:255:0:255' = 'FullColor'
        '48:5:254'       = 'XColor'
        '48;2;255;0;255' = 'FullColorCompatible'
        '48;5;254'       = 'XColorCompatible'
    }

    $SupportedModes = @(foreach ($SGR in $ColorMode.Keys) {
            $DECRQSS = -join @(
                # Set the background color
                "`e[0m`e[$($SGR)m"
                # Output the DECRQSS query
                "`eP`$qm`e`\"
                # Reset the background
                "`e[49m"
            )

            # strip the DCS and ST from the ends of the response and return just the SGR value as the Response
            $Result = Get-TerminalResponse $DECRQSS | SelectCapturedString '(?:\u001BP|\u0090)(?<Result>\d+)\$r(?:0;)?(?<Code>.*)m(?:\u001B[\\\t])'
            Write-Verbose "$($Result.Result)r$($Result.Code)m"

            # the result code is supposed to be 1 no matter what, no idea what other values represent, really
            if ($Result.Result -ne 1) {
                Write-Verbose "Request Status String (DECRQSS) not supported (returned $($Result.Result))"
                continue
            }
            if ($Result.Code -ne $SGR) {
                Write-Verbose "Received unexpected result, our '$($SGR)' color mode wasn't supported (returned $($Result.Result))"
                continue
            }
            $SGR
        })
    $ColorMode[$SupportedModes]
}
#EndRegion '.\Public\Get-TerminalColorMode.ps1' 44
#Region '.\Public\Get-TerminalResponse.ps1' 0
function Get-TerminalResponse {
    <#
        .SYNOPSIS
            Write a VT ANSI escape sequence to the host and capture the response
        .EXAMPLE
            $Row, $Col = (Get-VtResponse "`e[6n") -split ';' -replace "[`e\[R]"
            Gets the current cursor position into $Row and $Col
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        $Sequence
    )
    [console]::write($sequence)
    @(while ([console]::KeyAvailable) {
            [console]::ReadKey($true).KeyChar
        }) -join ""
}
#EndRegion '.\Public\Get-TerminalResponse.ps1' 19
#Region '.\Public\Get-TerminalTheme.ps1' 0
#using namespace PoshCode.Pansies

function Get-TerminalTheme {
    <#
        .SYNOPSIS
            Returns a hashtable of the _current_ values that can be splatted to Set-Theme
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        [string]$ColorScheme
    )
    process {
        if (!$ColorScheme) {
            $ColorScheme = (Get-ModuleTheme).Name
        }
        # if ($IsWindows) {
        # Write-Warning "On Windows, reading the current colors is not supported. Please use Theme.WindowsTerminal or Theme.WindowsConsole. See Get-Help et-TerminalTheme"
        # } else {
            Write-Warning "Reading the current colors from terminals is not supported yet. Returning the theme from `$Host.PrivateData.Theme['Theme.Terminal']"
        # }

        Get-ModuleTheme
    }
}
#EndRegion '.\Public\Get-TerminalTheme.ps1' 26
#Region '.\Public\Set-TerminalTheme.ps1' 0
#using namespace PoshCode.Pansies

function Set-TerminalTheme {
    <#
        .SYNOPSIS
            Set the theme for Windows Terminal
            Has parameters for each color, including foreground and background
    #>

    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Name           = "EzTheme",

        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$background   = "#0C0C0C",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$foreground   = "#CCCCCC",

        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$cursor       = "#CCCCCC",

        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$black        = "#0C0C0C",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$red          = "#C50F1F",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$green        = "#13A10E",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$yellow       = "#C19C00",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$blue         = "#0037DA",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$purple       = "#881798",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$cyan         = "#3A96DD",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$white        = "#CCCCCC",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightBlack  = "#767676",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightRed    = "#E74856",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightGreen  = "#16C60C",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightYellow = "#F9F1A5",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightBlue   = "#3B78FF",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightPurple = "#B4009E",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightCyan   = "#61D6D6",
        [Parameter(ValueFromPipelineByPropertyName)]
        [RgbColor]$brightWhite  = "#F2F2F2"
    )
    process {
        $e = [char]27
        $b = "$e\"

        # PowerShell is really dumb, and has this setting that ignores the terminal. We need to help it out:
        $Host.UI.RawUI.ForegroundColor = $foreground.ConsoleColor
        $Host.UI.RawUI.BackgroundColor =
            $Host.PrivateData.DebugBackgroundColor =
            $Host.PrivateData.VerboseBackgroundColor =
            $Host.PrivateData.WarningBackgroundColor =
            $Host.PrivateData.ErrorBackgroundColor = $background.ConsoleColor

        @(
            "$e]4;0;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$black.R, [int]$black.G, [int]$black.B
            "$e]4;1;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$red.R, [int]$red.G, [int]$red.B
            "$e]4;2;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$green.R, [int]$green.G, [int]$green.B
            "$e]4;3;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$yellow.R, [int]$yellow.G, [int]$yellow.B
            "$e]4;4;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$blue.R, [int]$blue.G, [int]$blue.B
            "$e]4;5;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$purple.R, [int]$purple.G, [int]$purple.B
            "$e]4;6;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$cyan.R, [int]$cyan.G, [int]$cyan.B
            "$e]4;7;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$white.R, [int]$white.G, [int]$white.B
            "$e]4;8;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$brightBlack.R, [int]$brightBlack.G, [int]$brightBlack.B
            "$e]4;9;rgb:{0:x}/{1:x}/{2:x}$b"  -f [int]$brightRed.R, [int]$brightRed.G, [int]$brightRed.B
            "$e]4;10;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$brightGreen.R, [int]$brightGreen.G, [int]$brightGreen.B
            "$e]4;11;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$brightYellow.R, [int]$brightYellow.G, [int]$brightYellow.B
            "$e]4;12;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$brightBlue.R, [int]$brightBlue.G, [int]$brightBlue.B
            "$e]4;13;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$brightPurple.R, [int]$brightPurple.G, [int]$brightPurple.B
            "$e]4;14;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$brightCyan.R, [int]$brightCyan.G, [int]$brightCyan.B
            "$e]4;15;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$brightWhite.R, [int]$brightWhite.G, [int]$brightWhite.B
            "$e]10;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$foreground.R, [int]$foreground.G, [int]$foreground.B
            "$e]11;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$background.R, [int]$background.G, [int]$background.B
            "$e]12;rgb:{0:x}/{1:x}/{2:x}$b" -f [int]$cursor.R, [int]$cursor.G, [int]$cursor.B
        ) -join "" | Write-Host -NoNewLine

        if ($Host.PrivateData.Theme) {
            $Host.PrivateData.Theme.Item("Theme.Terminal") = [PSCustomObject]@{
                PSTypeName   = "Terminal.ColorScheme"
                name         = $Name
                black        = $black
                red          = $red
                green        = $green
                yellow       = $yellow
                blue         = $blue
                purple       = $purple
                cyan         = $cyan
                white        = $white
                brightBlack  = $brightBlack
                brightRed    = $brightRed
                brightGreen  = $brightGreen
                brightYellow = $brightYellow
                brightBlue   = $brightBlue
                brightPurple = $brightPurple
                brightCyan   = $brightCyan
                brightWhite  = $brightWhite
                background   = $background
                foreground   = $foreground
                cursor       = $cursor
            }
        }
    }
}
#EndRegion '.\Public\Set-TerminalTheme.ps1' 116
#Region '.\postfix.ps1' 0
if (Get-Module EzTheme -ErrorAction SilentlyContinue) {
    Get-ModuleTheme | Set-TerminalTheme
}
#EndRegion '.\postfix.ps1' 4