├── .earthlyignore ├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Build.build.ps1 ├── Build.ps1 ├── Docs ├── Convert-ConsolePalette.md ├── Get-ColorWheel.md ├── Get-Complement.md ├── Get-Gradient.md ├── New-Hyperlink.md ├── New-Text.md ├── Pansies.md └── Write-Host.md ├── Earthfile ├── Examples ├── HslEnumerator.ps1 ├── Out-Rainbow.ps1 ├── Show-Complement.ps1 ├── Show-Gradient.ps1 └── Write-Gradient.ps1 ├── GitVersion.yml ├── LICENSE ├── Pansies.csproj ├── Presentation ├── 0.Introduction.md ├── 01.DrawingMode.ps1 ├── 02.Movement.ps1 ├── 03.Modifying.ps1 ├── 04.Color.ps1 ├── 05.Put-It-Together.ps1 ├── 06.Complete.ps1 └── images │ ├── get-complement-bw.png │ ├── get-complement-high.png │ ├── get-complement.png │ ├── get-gradient.png │ ├── hello-gradient.png │ ├── new-text.png │ ├── pansies.jpg │ ├── terminal-theme.png │ └── write-host.png ├── Properties └── launchSettings.json ├── README.md ├── RequiredModules.psd1 ├── ScriptAnalyzerSettings.psd1 ├── Source ├── Assembly │ ├── ColorMode.cs │ ├── ColorSpaces │ │ ├── ColorSpace.cs │ │ ├── ColorSpaces.cs │ │ ├── ColorSpaces.tt │ │ ├── ColorSpaces.xml │ │ ├── Comparisons │ │ │ ├── Cie1976Comparison.cs │ │ │ ├── Cie94Comparison.cs │ │ │ ├── CieDe2000Comparison.cs │ │ │ ├── CmcComparison.cs │ │ │ └── IColorSpaceComparison.cs │ │ └── Conversions │ │ │ ├── CmyConverter.cs │ │ │ ├── CmykConverter.cs │ │ │ ├── HsbConverter.cs │ │ │ ├── HslConverter.cs │ │ │ ├── HsvConverter.cs │ │ │ ├── HunterLabConverter.cs │ │ │ ├── LabConverter.cs │ │ │ ├── LchConverter.cs │ │ │ ├── LuvConverter.cs │ │ │ ├── RgbConverter.cs │ │ │ ├── Utility │ │ │ └── DoubleExtension.cs │ │ │ ├── XyzConverter.cs │ │ │ └── YxyConverter.cs │ ├── Colors.cs │ ├── Colors.tt │ ├── Commands │ │ ├── GetColorWheel.cs │ │ ├── GetComplement.cs │ │ ├── GetGradientCommand.cs │ │ ├── NewHyperlinkCommand.cs │ │ ├── NewTextCommand.cs │ │ └── WriteHostCommand.cs │ ├── EmptyStringAsNullAttribute.cs │ ├── Entities.Emoji.cs │ ├── Entities.NerdFonts.cs │ ├── Entities.cs │ ├── Gradient.cs │ ├── HslColor.cs │ ├── NativeMethods.cs │ ├── Palettes │ │ ├── ConsolePalette.cs │ │ ├── IPalette.cs │ │ ├── Palette.cs │ │ ├── X11Palette.cs │ │ └── XTermPalette.cs │ ├── Provider │ │ ├── Paths │ │ │ ├── RGbColorItem.cs │ │ │ └── RgbColorResolver.cs │ │ ├── RgbColorDrive.cs │ │ ├── RgbColorProvider.cs │ │ └── RgbColorProviderRoot.cs │ ├── RgbColor.cs │ └── Text.cs ├── Pansies.format.ps1xml ├── Pansies.psd1 ├── Pansies.psm1 └── Private │ └── _init.ps1 └── build.psd1 /.earthlyignore: -------------------------------------------------------------------------------- 1 | # ignore output (because we put stuff there) 2 | /output/* 3 | /Modules/* 4 | assemblies/ 5 | # ignore binary output files and folders 6 | bin/ 7 | obj/ 8 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: {} 5 | pull_request: 6 | branches: [ main ] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | env: 12 | FORCE_COLOR: 1 13 | steps: 14 | - uses: earthly/actions-setup@v1 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} 17 | 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: true 21 | fetch-depth: 0 22 | 23 | - name: earthly +test 24 | if: github.ref != 'refs/heads/main' 25 | run: earthly --strict +test 26 | 27 | - name: earthly +push 28 | if: github.ref == 'refs/heads/main' 29 | run: earthly --push --secret NUGET_API_KEY --secret PSGALLERY_API_KEY --strict +all 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 33 | PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} 34 | 35 | - uses: actions/upload-artifact@v4 36 | with: 37 | name: Pansies 38 | path: Modules/Pansies 39 | 40 | - uses: actions/upload-artifact@v4 41 | with: 42 | name: TestResults 43 | path: Modules/Pansies-TestResults 44 | 45 | - uses: actions/upload-artifact@v4 46 | with: 47 | name: Packages 48 | path: Modules/Pansies-Packages 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore output (because we put stuff there) 2 | Modules/ 3 | output/ 4 | 5 | # Ignore version number folders (these are our intermediate "output" directories for testing) 6 | /[0-9]*/ 7 | 8 | # ignore binary output files and folders 9 | bin/ 10 | obj/ 11 | lib/ 12 | assemblies/ 13 | 14 | # ignore visual studio artifacts 15 | *.suo 16 | /.vs/ 17 | 18 | *.zip 19 | Presentation/export/ 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/p2f"] 2 | path = lib/p2f 3 | url = https://github.com/Jaykul/p2f.git 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": ".NET Core Attach", 6 | "type": "coreclr", 7 | "request": "attach" 8 | }, 9 | { 10 | "type": "PowerShell", 11 | "request": "launch", 12 | "name": "PowerShell Launch (current file)", 13 | "script": "${file}", 14 | }, 15 | { 16 | "type": "PowerShell", 17 | "request": "launch", 18 | "name": "PowerShell Launch (current test file)", 19 | "script": "Invoke-Pester", 20 | "args": [ 21 | "-Path", "${file}", 22 | "-PesterOption", "@{ IncludeVSCodeMarker = $True }" 23 | ], 24 | "createTemporaryIntegratedConsole": true 25 | }, 26 | { 27 | "type": "PowerShell", 28 | "request": "attach", 29 | "name": "PowerShell Attach to Host Process", 30 | "processId": "${command:PickPSHostProcess}", 31 | "runspaceId": 1 32 | }, 33 | { 34 | "type": "PowerShell", 35 | "request": "launch", 36 | "name": "PowerShell Interactive Session", 37 | "cwd": "${workspaceRoot}" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.encoding": "utf8", 3 | "files.trimTrailingWhitespace": true, 4 | "files.insertFinalNewline": true, 5 | "editor.tabSize": 4, 6 | "editor.insertSpaces": true, 7 | "powershell.codeFormatting.preset": "OTBS", 8 | "powershell.codeFormatting.ignoreOneLineBlock": false, 9 | "powershell.codeFormatting.useCorrectCasing": true, 10 | "powershell.codeFormatting.alignPropertyValuePairs": true, 11 | "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", 12 | "powershell.scriptAnalysis.settingsPath": "ScriptAnalyzerSettings.psd1", 13 | "files.associations": { 14 | "*.ps1xml": "xml", 15 | ".earthlyignore": "ignore" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | }, 12 | "type": "shell", 13 | "command": "Invoke-Build", 14 | "presentation": { 15 | "echo": true, 16 | "reveal": "always", 17 | "focus": false, 18 | "panel": "shared", 19 | "showReuseMessage": true, 20 | "clear": false 21 | } 22 | }, 23 | { 24 | "label": "test", 25 | "group": { 26 | "kind": "test", 27 | "isDefault": true 28 | }, 29 | "type": "shell", 30 | "options": { 31 | "cwd": "${workspaceFolder}", 32 | "env": { 33 | "PSModulePath": "${workspaceFolder}\\Output;${env:PSModulePath}" 34 | } 35 | }, 36 | "command": "Invoke-Build", 37 | "args": [ 38 | "Test" 39 | ], 40 | "presentation": { 41 | "echo": true, 42 | "reveal": "always", 43 | "focus": true, 44 | "panel": "shared", 45 | "showReuseMessage": true, 46 | "clear": true 47 | } 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /Build.build.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | ./project.build.ps1 4 | .EXAMPLE 5 | Invoke-Build 6 | .NOTES 7 | 0.5.0 - Parameterize 8 | Add parameters to this script to control the build 9 | #> 10 | [CmdletBinding()] 11 | param( 12 | # dotnet build configuration parameter (Debug or Release) 13 | [ValidateSet('Debug', 'Release')] 14 | [string]$Configuration = 'Release', 15 | 16 | # Add the clean task before the default build 17 | [switch]$Clean, 18 | 19 | # Collect code coverage when tests are run 20 | [switch]$CollectCoverage, 21 | 22 | # Which projects to build 23 | [Alias("Projects")] 24 | $dotnetProjects = @( 25 | "Pansies" 26 | ), 27 | 28 | # Which projects are test projects 29 | [Alias("TestProjects")] 30 | $dotnetTestProjects = @(), 31 | 32 | # Further options to pass to dotnet 33 | [Alias("Options")] 34 | $dotnetOptions = @{ 35 | "-verbosity" = "minimal" 36 | # "-runtime" = "linux-x64" 37 | } 38 | ) 39 | $InformationPreference = "Continue" 40 | $ErrorView = 'DetailedView' 41 | 42 | # The name of the module to publish 43 | $script:PSModuleName = "TerminalBlocks" 44 | # Use Env because Earthly can override it 45 | $Env:OUTPUT_ROOT ??= Join-Path $BuildRoot Modules 46 | 47 | $Tasks = "Tasks", "../Tasks", "../../Tasks" | Convert-Path -ErrorAction Ignore | Select-Object -First 1 48 | Write-Information "$($PSStyle.Foreground.BrightCyan)Found shared tasks in $Tasks" -Tag "InvokeBuild" 49 | ## Self-contained build script - can be invoked directly or via Invoke-Build 50 | if ($MyInvocation.ScriptName -notlike '*Invoke-Build.ps1') { 51 | & "$Tasks/_Bootstrap.ps1" 52 | 53 | Invoke-Build -File $MyInvocation.MyCommand.Path @PSBoundParameters -Result Result 54 | 55 | if ($Result.Error) { 56 | $Error[-1].ScriptStackTrace | Out-String 57 | exit 1 58 | } 59 | exit 0 60 | } 61 | 62 | ## The first task defined is the default task. Put the right values for your project type here... 63 | if ($dotnetProjects -and $Clean) { 64 | Add-BuildTask CleanBuild Clean, ($Task ?? "Test") 65 | } elseif ($Clean) { 66 | Add-BuildTask CleanBuild Clean, ($Task ?? "Test") 67 | } 68 | 69 | ## Initialize the build variables, and import shared tasks, including DotNet tasks 70 | . "$Tasks/_Initialize.ps1" 71 | -------------------------------------------------------------------------------- /Build.ps1: -------------------------------------------------------------------------------- 1 | #requires -Module Configuration, @{ ModuleName = "ModuleBuilder"; ModuleVersion = "1.6.0" } 2 | 3 | [CmdletBinding()] 4 | param( 5 | [ValidateSet("Release","Debug")] 6 | $Configuration = "Release", 7 | 8 | # The version of the output module 9 | [Alias("ModuleVersion","Version")] 10 | [string]$SemVer 11 | ) 12 | $ErrorActionPreference = "Stop" 13 | Push-Location $PSScriptRoot -StackName BuildTestStack 14 | 15 | if (!$SemVer -and (Get-Command gitversion -ErrorAction Ignore)) { 16 | $SemVer = gitversion -showvariable nugetversion 17 | } 18 | 19 | try { 20 | if (!$SkipBinaryBuild) { 21 | Write-Host "## Compiling Pansies binary module" -ForegroundColor Cyan 22 | # dotnet restore 23 | # dotnet build -c $Configuration -o "$($folder)\lib" | Write-Host -ForegroundColor DarkGray 24 | dotnet publish -c $Configuration -o "$($Folder)/lib" | Write-Host -ForegroundColor DarkGray 25 | # We don't need to ship any of the System DLLs because they're all in PowerShell 26 | Get-ChildItem $Folder -Filter System.* -Recurse | Remove-Item 27 | } 28 | Write-Host "## Calling Build-Module" -ForegroundColor Cyan 29 | 30 | $null = $PSBoundParameters.Remove("Configuration") 31 | $Module = Build-Module @PSBoundParameters -Passthru 32 | 33 | Write-Host "## Compiling Documentation" -ForegroundColor Cyan 34 | $Folder = Split-Path $Module.Path 35 | 36 | Remove-Item "$($folder)\en-US" -Force -Recurse -ErrorAction SilentlyContinue 37 | $null = New-ExternalHelp -Path ".\Docs" -OutputPath "$($folder)\en-US" 38 | 39 | $Folder 40 | 41 | } catch { 42 | throw $_ 43 | } finally { 44 | Pop-Location -StackName BuildTestStack 45 | } -------------------------------------------------------------------------------- /Docs/Convert-ConsolePalette.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies-help.xml 3 | online version: 4 | schema: 2.0.0 5 | --- 6 | 7 | # Convert-ConsolePalette 8 | 9 | ## SYNOPSIS 10 | Converts the current 16 Color console palette by shifting either the dark or light colors to darker or lighter shades. 11 | 12 | ## SYNTAX 13 | 14 | ### Dark (Default) 15 | ``` 16 | Convert-ConsolePalette [-DarkShift] [-Copy] [-WhatIf] [-Confirm] 17 | ``` 18 | 19 | ### Bright 20 | ``` 21 | Convert-ConsolePalette -BrightShift [-Copy] [-WhatIf] [-Confirm] 22 | ``` 23 | 24 | ## DESCRIPTION 25 | Converts a 16 Color console palette by shifting either the dark or light colors to darker or lighter shades. 26 | 27 | ## EXAMPLES 28 | 29 | ### Example 1 30 | ``` 31 | PS C:\> Convert-ConsolePalette -BrightShift 20 -Copy 32 | ``` 33 | 34 | Copies the Dark* colors from the console palette to the bright side and brightens them by 20 (of 100) luminance. 35 | 36 | ## PARAMETERS 37 | 38 | ### -BrightShift 39 | How much to shift the bright colors. Positive values make the colors brighter, negative values make them darker 40 | 41 | ```yaml 42 | Type: Int32 43 | Parameter Sets: Bright 44 | Aliases: 45 | 46 | Required: True 47 | Position: Named 48 | Default value: None 49 | Accept pipeline input: False 50 | Accept wildcard characters: False 51 | ``` 52 | 53 | ### -Confirm 54 | Prompts you for confirmation before running the cmdlet. 55 | 56 | ```yaml 57 | Type: SwitchParameter 58 | Parameter Sets: (All) 59 | Aliases: cf 60 | 61 | Required: False 62 | Position: Named 63 | Default value: None 64 | Accept pipeline input: False 65 | Accept wildcard characters: False 66 | ``` 67 | 68 | ### -Copy 69 | By default, the colors are modified in-place. If copy is set: 70 | - the dark colors start with the value of the bright colors 71 | - the light colors start at the value of the dark colors 72 | 73 | ```yaml 74 | Type: SwitchParameter 75 | Parameter Sets: (All) 76 | Aliases: 77 | 78 | Required: False 79 | Position: Named 80 | Default value: None 81 | Accept pipeline input: False 82 | Accept wildcard characters: False 83 | ``` 84 | 85 | ### -DarkShift 86 | How much to shift the dark colors. Positive values make the colors brighter, negative values make them darker 87 | 88 | ```yaml 89 | Type: Int32 90 | Parameter Sets: Dark 91 | Aliases: 92 | 93 | Required: True 94 | Position: 0 95 | Default value: None 96 | Accept pipeline input: False 97 | Accept wildcard characters: False 98 | ``` 99 | 100 | ### -WhatIf 101 | Shows what would happen if the cmdlet runs. 102 | The cmdlet is not run. 103 | 104 | ```yaml 105 | Type: SwitchParameter 106 | Parameter Sets: (All) 107 | Aliases: wi 108 | 109 | Required: False 110 | Position: Named 111 | Default value: None 112 | Accept pipeline input: False 113 | Accept wildcard characters: False 114 | ``` 115 | 116 | ## INPUTS 117 | 118 | ### None 119 | 120 | 121 | ## OUTPUTS 122 | 123 | ### System.Object 124 | 125 | ## NOTES 126 | 127 | ## RELATED LINKS 128 | 129 | -------------------------------------------------------------------------------- /Docs/Get-ColorWheel.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies.dll-Help.xml 3 | Module Name: Pansies 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-ColorWheel 9 | 10 | ## SYNOPSIS 11 | 12 | Get a range of colors from a starting point aiming to avoid repetition. Doesn't return the starting color unless -Passthru is specified. 13 | 14 | ## SYNTAX 15 | 16 | ``` 17 | Get-Gradient [[-Color] ] [-Count ] [-HueStep ] [[-BrightStep] ] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Get a range of colors that wrap around the hue of the HSL color spectrum. 23 | 24 | NOTE: this calls Gradient.GetRainbow, but is named more correctly. When you ask for a lot of colors, the color wheel will wrap around. To avoid repeating colors, it defaults to a HueStep of 50 (meaning it goes a full 360 degrees and wraps around fter 7 steps), but it also changes the brightness, so by the time it wraps around, it's not the same shade. As a result, it doesn't actually repeat unless you ask for hundreds of colors, but the more colors you ask for, the more similar they get. You can tweak that by using BrightStep or HueStep 25 | 26 | ## EXAMPLES 27 | 28 | ### ---- Example 1 ------------------------------------------------------------- 29 | 30 | ``` 31 | PS C:\> Get-ColorWheel Magenta -Bright 0 32 | ``` 33 | 34 | Get a 7-color rainbow. Since we specified Magenta as the starting point, the first returned color is Red. 35 | 36 | 37 | ### ---- Example 2 ------------------------------------------------------------- 38 | 39 | ``` 40 | PS C:\> Get-ColorWheel Magenta 41 | ``` 42 | 43 | Gets a 2D gradient from the ConsoleColor Red to Blue, with as many colors as the current width of the console. 44 | 45 | 46 | ## PARAMETERS 47 | 48 | ### -BrightStep 49 | How much to adjust the lightness (alias Light step) 50 | 51 | ```yaml 52 | Type: Int32 53 | Parameter Sets: (All) 54 | Aliases: LightStep 55 | 56 | Required: False 57 | Position: Named 58 | Default value: 4 59 | Accept pipeline input: False 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -Color 64 | The starting color (as an RGB color value) 65 | 66 | ```yaml 67 | Type: RgbColor 68 | Parameter Sets: (All) 69 | Aliases: StartColor 70 | 71 | Required: False 72 | Position: 0 73 | Default value: Red 74 | Accept pipeline input: True (ByValue) 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -Count 79 | How many colors to return 80 | 81 | ```yaml 82 | Type: Int32 83 | Parameter Sets: (All) 84 | Aliases: 85 | 86 | Required: False 87 | Position: Named 88 | Default value: 7 89 | Accept pipeline input: False 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### -HueStep 94 | How many degrees of the color wheel to go for a new color. 95 | 96 | ```yaml 97 | Type: Int32 98 | Parameter Sets: (All) 99 | Aliases: 100 | 101 | Required: False 102 | Position: Named 103 | Default value: 50 104 | Accept pipeline input: False 105 | Accept wildcard characters: False 106 | ``` 107 | 108 | ### -Passthru 109 | If set, returns the start color in addition to the Count 110 | 111 | ```yaml 112 | Type: SwitchParameter 113 | Parameter Sets: (All) 114 | Aliases: 115 | 116 | Required: False 117 | Position: Named 118 | Default value: False 119 | Accept pipeline input: False 120 | Accept wildcard characters: False 121 | ``` 122 | 123 | ### CommonParameters 124 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 125 | 126 | ## INPUTS 127 | 128 | ### PoshCode.Pansies.RgbColor 129 | 130 | ## OUTPUTS 131 | 132 | ### PoshCode.Pansies.RgbColor 133 | 134 | ## NOTES 135 | 136 | ## RELATED LINKS 137 | -------------------------------------------------------------------------------- /Docs/Get-Complement.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies-help.xml 3 | Module Name: Pansies 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-Complement 9 | 10 | ## SYNOPSIS 11 | 12 | Get the Hue complement color 13 | 14 | ## SYNTAX 15 | 16 | ``` 17 | Get-Complement [-Color] [-HighContrast] [-BlackAndWhite] [-Passthru] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Returns a color that is 180 degrees opposite around the Hue component of the HSL color space. 23 | 24 | The primary reason for Get-Complement to exist is for generating contrasting colors for foreground and background. For that reason, it's usually called with `-HighContrast` mode, which changes the lightness or darkness to increase the contrast of the returned color. 25 | 26 | It also has a `-BlackAndWhite` switch that causes it to assume only 16 colors will work (PowerLine currently uses this mode by default because so many terminals don't support more --including Windows 10 prior to Creators Update). In BlackAndWhite mode, it always returns White or Black. 27 | 28 | ## EXAMPLES 29 | 30 | ### ---- Example 1 ------------------------------------------------------------- 31 | 32 | ``` 33 | PS C:\> Get-Complement Cyan 34 | ``` 35 | 36 | Gets the color Red back, as the complement for Cyan. 37 | 38 | 39 | ### ---- Example 2 ------------------------------------------------------------- 40 | 41 | ``` 42 | PS C:\> $Background, $Foreground = Get-Complement Cyan -Passthru -BlackAndWhite 43 | PS C:\> Microsoft.PowerShell.Utility\Write-Host " Hello World " -Foreground $Foreground.ConsoleColor -Background $Background.ConsoleColor 44 | ``` 45 | 46 | This example shows how using `-Passthru` returns both the original color and the contrasting color, and how using `-BlackAndWhite` results in a better contrast when you're being forced to use BlackAndWhite (as with the built-in Write-Host command). 47 | 48 | You can try the example without `-BlackAndWhite` to see the difference: with it, you'll get Black on Cyan, without, you'll get Red on Cyan. Note that using -HighContrast will make almost no difference if you're using the `BlackAndWhite` property, because downsampling to 16 colors has to result in either Red or DarkRed... 49 | 50 | 51 | 52 | ## PARAMETERS 53 | 54 | ### -Color 55 | 56 | The source color to calculate the complement of 57 | 58 | ```yaml 59 | Type: RgbColor 60 | Parameter Sets: (All) 61 | Aliases: 62 | 63 | Required: True 64 | Position: 1 65 | Default value: None 66 | Accept pipeline input: True (ByValue) 67 | Accept wildcard characters: False 68 | ``` 69 | 70 | ### -HighContrast 71 | 72 | Force the luminance to have "enough" contrast 73 | 74 | ```yaml 75 | Type: SwitchParameter 76 | Parameter Sets: (All) 77 | Aliases: 78 | 79 | Required: False 80 | Position: Named 81 | Default value: False 82 | Accept pipeline input: False 83 | Accept wildcard characters: False 84 | ``` 85 | 86 | ### -BlackAndWhite 87 | 88 | Returns either black or white to get a readable contrast color. 89 | 90 | ```yaml 91 | Type: SwitchParameter 92 | Parameter Sets: (All) 93 | Aliases: 94 | 95 | Required: False 96 | Position: Named 97 | Default value: False 98 | Accept pipeline input: False 99 | Accept wildcard characters: False 100 | ``` 101 | 102 | ### -Passthru 103 | 104 | If set, output the input $Color before the complement 105 | 106 | ```yaml 107 | Type: SwitchParameter 108 | Parameter Sets: (All) 109 | Aliases: 110 | 111 | Required: False 112 | Position: Named 113 | Default value: False 114 | Accept pipeline input: False 115 | Accept wildcard characters: False 116 | ``` 117 | 118 | ## INPUTS 119 | 120 | ### PoshCode.Pansies.RgbColor 121 | 122 | The color to find a complement for 123 | 124 | 125 | ## OUTPUTS 126 | 127 | ### PoshCode.Pansies.RgbColor 128 | 129 | The complement of the input color 130 | 131 | ## NOTES 132 | 133 | ## RELATED LINKS 134 | 135 | -------------------------------------------------------------------------------- /Docs/Get-Gradient.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies-help.xml 3 | Module Name: Pansies 4 | online version: 5 | schema: 2.0.0 6 | --- 7 | 8 | # Get-Gradient 9 | 10 | ## SYNOPSIS 11 | 12 | Get a range of colors between two colors 13 | 14 | ## SYNTAX 15 | 16 | ``` 17 | Get-Gradient [-StartColor] [-EndColor] [[-Width] ] [[-Height] ] [[-ColorSpace] ] [-Reverse] [-Flatten] 18 | ``` 19 | 20 | ## DESCRIPTION 21 | 22 | Get an array (or multiple arrays, one per line) of RgbColor values for a gradient from the start Color to the end Color. 23 | 24 | ## EXAMPLES 25 | 26 | ### ---- Example 1 ------------------------------------------------------------- 27 | 28 | ``` 29 | PS C:\> Get-Gradient Red Blue -Count 10 -Flatten 30 | ``` 31 | 32 | Gets a simple array of ten colors between Red and Blue. 33 | 34 | 35 | ### ---- Example 2 ------------------------------------------------------------- 36 | 37 | ``` 38 | PS C:\> Get-Gradient Red Blue 39 | ``` 40 | 41 | Gets a 2D gradient from the ConsoleColor Red to Blue, with as many colors as the current width of the console. 42 | 43 | ## PARAMETERS 44 | 45 | ### -StartColor 46 | 47 | The left color to start the gradient from. 48 | 49 | ```yaml 50 | Type: RgbColor 51 | Parameter Sets: (All) 52 | Aliases: 53 | 54 | Required: True 55 | Position: 1 56 | Default value: None 57 | Accept pipeline input: False 58 | Accept wildcard characters: False 59 | ``` 60 | 61 | 62 | ### -EndColor 63 | 64 | The right color to end the gradient at. 65 | 66 | ```yaml 67 | Type: RgbColor 68 | Parameter Sets: (All) 69 | Aliases: 70 | 71 | Required: True 72 | Position: 2 73 | Default value: None 74 | Accept pipeline input: False 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -Width 79 | 80 | The number of columns to generate in the gradient. Defaults to the width of the console. 81 | 82 | ```yaml 83 | Type: Int32 84 | Parameter Sets: (All) 85 | Aliases: Length, Count, Steps 86 | 87 | Required: False 88 | Position: 3 89 | Default value: $Host.UI.RawUI.WindowSize.Width 90 | Accept pipeline input: False 91 | Accept wildcard characters: False 92 | ``` 93 | 94 | ### -Height 95 | 96 | The number of rows to generate in the gradient. Defaults to 1 97 | 98 | ```yaml 99 | Type: Int32 100 | Parameter Sets: (All) 101 | Aliases: 102 | 103 | Required: False 104 | Position: 4 105 | Default value: $Host.UI.RawUI.WindowSize.Height 106 | Accept pipeline input: False 107 | Accept wildcard characters: False 108 | ``` 109 | 110 | ### -ColorSpace 111 | A color space to render the gradient in. Defaults to HunterLab, which 112 | generates gradients that are very consistent in their brightness, 113 | but can be any of CMY, CMYK, LAB, LCH, LUV, HunterLAB, HSL, HSV, HSB, RGB, XYZ, YXY 114 | 115 | ```yaml 116 | Type: Object 117 | Parameter Sets: (All) 118 | Aliases: 119 | 120 | Required: False 121 | Position: Named 122 | Default value: HunterLab 123 | Accept pipeline input: False 124 | Accept wildcard characters: False 125 | ``` 126 | 127 | ### -Reverse 128 | For color spaces with Hue (HSL, HSV, HSB), setting this generates the gradient the long way, creating a rainbow effect. 129 | 130 | ```yaml 131 | Type: SwitchParameter 132 | Parameter Sets: (All) 133 | Aliases: 134 | 135 | Required: False 136 | Position: Named 137 | Default value: False 138 | Accept pipeline input: False 139 | Accept wildcard characters: False 140 | ``` 141 | 142 | ### -Flatten 143 | Flattens the 2D array to a single array. 144 | 145 | ```yaml 146 | Type: SwitchParameter 147 | Parameter Sets: (All) 148 | Aliases: 149 | 150 | Required: False 151 | Position: Named 152 | Default value: False 153 | Accept pipeline input: False 154 | Accept wildcard characters: False 155 | ``` 156 | 157 | ## INPUTS 158 | 159 | ### RgbColor 160 | 161 | You must pass a start and end RgbColor 162 | 163 | ## OUTPUTS 164 | 165 | ### RgbColor 166 | 167 | With -Flatten, returns a simple array of colors between the start and end color 168 | 169 | Otherwise, returns a two dimensional array of colors of the specified height and width 170 | 171 | ## NOTES 172 | 173 | ## RELATED LINKS 174 | -------------------------------------------------------------------------------- /Docs/New-Hyperlink.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies-help.xml 3 | online version: 4 | schema: 2.0.0 5 | --- 6 | 7 | # New-Hyperlink 8 | 9 | ## SYNOPSIS 10 | Create a hyperlink with the specified Uri, and optionally using different text, background and foreground colors 11 | 12 | ## SYNTAX 13 | 14 | ``` 15 | New-Hyperlink [-Uri] [[-Object] ] [-Separator ] [-BackgroundColor ] [-ForegroundColor ] [-LeaveColor] [-IgnoreEntities] [-Passthru] 16 | ``` 17 | 18 | ## DESCRIPTION 19 | Create a hyperlink with the specified Uri, using ANSI Virtual Terminal escape sequences. 20 | As with New-Text, there's full support for setting background and foreground colors, and rendering HTML-style entities. 21 | 22 | With -Passthru, returns the Text object, but normally outputs the text string with the Uri hyperlink embedded. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1 27 | ``` 28 | PS C:\> New-Hyperlink https://PoshCode.org -ForegroundColor Green 29 | 30 | `e[92m`e]8;;https://PoshCode.org`ahttps://PoshCode.org`e]8;;`a`e[39m 31 | ``` 32 | 33 | Generates a hyperlink to https://PoshCode.org with the text https://PoshCode.org and a green foreground color. 34 | 35 | This use, without text (the `-Object` parameter) and with a highlight color, is the most compatible use, because if the terminal doesn't support links, at least the URL will be visible, and highlighted in color 36 | 37 | ### Example 2 38 | ``` 39 | PS C:\> "Please visit $(New-Hyperlink https://PoshCode.org PoshCode)" 40 | 41 | Please visit `e]8;;https://PoshCode.org`aPoshCode`e]8;;`a 42 | ``` 43 | 44 | Generates a hyperlink to https://PoshCode.org with the text "PoshCode". You should be careful of this syntax, where you don't include the full URL in the display, because terminals which don't support hyperlinks (like the default Windows console) will not display the Url, nor any indication that it should be a link. 45 | 46 | ## PARAMETERS 47 | 48 | ### -Uri 49 | Specifies the Uri to link to. 50 | 51 | ```yaml 52 | Type: String 53 | Parameter Sets: (All) 54 | Aliases: 55 | 56 | Required: True 57 | Position: 0 58 | Default value: None 59 | Accept pipeline input: True (ByPropertyName, ByValue) 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -BackgroundColor 64 | The background color. You may specify it as CSS hex "#RRGGBB" (or just "RRGGBB") or as an XTerm index "xt123" (or just "123") or as a ConsoleColor like "Red" or "DarkRed"... 65 | 66 | ```yaml 67 | Type: RgbColor 68 | Parameter Sets: (All) 69 | Aliases: Bg 70 | 71 | Required: False 72 | Position: Named 73 | Default value: None 74 | Accept pipeline input: False 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -ForegroundColor 79 | The foreground color. You may specify it as CSS hex "#RRGGBB" (or just "RRGGBB") or as an XTerm index "xt123" (or just "123") or as a ConsoleColor like "Red" or "DarkRed"... 80 | 81 | ```yaml 82 | Type: RgbColor 83 | Parameter Sets: (All) 84 | Aliases: Fg 85 | 86 | Required: False 87 | Position: Named 88 | Default value: None 89 | Accept pipeline input: False 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### -IgnoreEntities 94 | If set, don't render the HTML Entities to characters (i.e. leave "♥" as "♥" instead of as "♥") 95 | 96 | ```yaml 97 | Type: SwitchParameter 98 | Parameter Sets: (All) 99 | Aliases: 100 | 101 | Required: False 102 | Position: Named 103 | Default value: None 104 | Accept pipeline input: False 105 | Accept wildcard characters: False 106 | ``` 107 | 108 | ### -LeaveColor 109 | If set, don't clear the colors at the end of the output. 110 | 111 | ```yaml 112 | Type: SwitchParameter 113 | Parameter Sets: (All) 114 | Aliases: 115 | 116 | Required: False 117 | Position: Named 118 | Default value: None 119 | Accept pipeline input: False 120 | Accept wildcard characters: False 121 | ``` 122 | 123 | ### -Object 124 | Specifies objects to display as the text of the link. 125 | 126 | ```yaml 127 | Type: Object 128 | Parameter Sets: (All) 129 | Aliases: 130 | 131 | Required: True 132 | Position: 0 133 | Default value: None 134 | Accept pipeline input: True (ByValue, FromRemainingArguments) 135 | Accept wildcard characters: False 136 | ``` 137 | 138 | ### -Separator 139 | Specifies a separator string to output between objects displayed on the console. 140 | 141 | ```yaml 142 | Type: Object 143 | Parameter Sets: (All) 144 | Aliases: 145 | 146 | Required: False 147 | Position: Named 148 | Default value: None 149 | Accept pipeline input: False 150 | Accept wildcard characters: False 151 | ``` 152 | 153 | ### -Passthru 154 | If set, outputs a Text object, rather than simple string 155 | 156 | ```yaml 157 | Type: SwitchParameter 158 | Parameter Sets: (All) 159 | Aliases: 160 | 161 | Required: False 162 | Position: Named 163 | Default value: None 164 | Accept pipeline input: False 165 | Accept wildcard characters: False 166 | ``` 167 | 168 | ## INPUTS 169 | 170 | ### System.Object 171 | 172 | ## OUTPUTS 173 | 174 | ### System.Object 175 | 176 | ## NOTES 177 | 178 | ## RELATED LINKS 179 | 180 | -------------------------------------------------------------------------------- /Docs/New-Text.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies-help.xml 3 | online version: 4 | schema: 2.0.0 5 | --- 6 | 7 | # New-Text 8 | 9 | ## SYNOPSIS 10 | Create a Text object with specified background and foreground colors 11 | 12 | ## SYNTAX 13 | 14 | ``` 15 | New-Text [-Object] [-BackgroundColor ] [-Separator ] [-ForegroundColor ] [-LeaveColor] [-IgnoreEntities] 16 | ``` 17 | 18 | ## DESCRIPTION 19 | Create a Text object with specified background and foreground colors, and rendering HTML-style entities. 20 | When this object is rendered to the host with .ToString(), it inserts ANSI Virtual Terminal escape sequences for the specified colors, 21 | and by default, outputs escape sequences to clear those colors after the text. 22 | 23 | ## EXAMPLES 24 | 25 | ### Example 1 26 | ``` 27 | PS C:\> New-Text "♥" -ForegroundColor Red 28 | 29 | 30 | BackgroundColor ForegroundColor Object ToString 31 | --------------- --------------- ------ -------- 32 | `e[101m `e[0m Red ♥`e[0m `e[91m?`e[39m`e[0m 33 | ``` 34 | 35 | Generates a text object with the hearts symbol (♥) in red. The output will show the BackgroundColor, ForegroundColor, Text (with the entity text in it) and the rendered output of `.ToString()` where the entity will be replaced with the hearts symbol. 36 | 37 | ### Example 1 38 | ``` 39 | PS C:\> "I $(New-Text "♥" -ForegroundColor "#F00") PS" 40 | 41 | I `e[38;2;255;0;0m?`e[39m PS 42 | ``` 43 | 44 | Outputs the text "I ♥ PS" with the heart in red. 45 | 46 | ## PARAMETERS 47 | 48 | ### -BackgroundColor 49 | The background color. You may specify it as CSS hex "#RRGGBB" (or just "RRGGBB") or as an XTerm index "xt123" (or just "123") or as a ConsoleColor like "Red" or "DarkRed"... 50 | 51 | ```yaml 52 | Type: RgbColor 53 | Parameter Sets: (All) 54 | Aliases: Bg 55 | 56 | Required: False 57 | Position: Named 58 | Default value: None 59 | Accept pipeline input: False 60 | Accept wildcard characters: False 61 | ``` 62 | 63 | ### -ForegroundColor 64 | The foreground color. You may specify it as CSS hex "#RRGGBB" (or just "RRGGBB") or as an XTerm index "xt123" (or just "123") or as a ConsoleColor like "Red" or "DarkRed"... 65 | 66 | ```yaml 67 | Type: RgbColor 68 | Parameter Sets: (All) 69 | Aliases: Fg 70 | 71 | Required: False 72 | Position: Named 73 | Default value: None 74 | Accept pipeline input: False 75 | Accept wildcard characters: False 76 | ``` 77 | 78 | ### -IgnoreEntities 79 | If set, don't render the HTML Entities to characters (i.e. leave "♥" as "♥" instead of as "♥") 80 | 81 | ```yaml 82 | Type: SwitchParameter 83 | Parameter Sets: (All) 84 | Aliases: 85 | 86 | Required: False 87 | Position: Named 88 | Default value: None 89 | Accept pipeline input: False 90 | Accept wildcard characters: False 91 | ``` 92 | 93 | ### -LeaveColor 94 | If set, don't clear the colors at the end of the output. 95 | 96 | ```yaml 97 | Type: SwitchParameter 98 | Parameter Sets: (All) 99 | Aliases: 100 | 101 | Required: False 102 | Position: Named 103 | Default value: None 104 | Accept pipeline input: False 105 | Accept wildcard characters: False 106 | ``` 107 | 108 | ### -Object 109 | Specifies objects to display in the host. 110 | 111 | ```yaml 112 | Type: Object 113 | Parameter Sets: (All) 114 | Aliases: 115 | 116 | Required: True 117 | Position: 0 118 | Default value: None 119 | Accept pipeline input: True (ByPropertyName, ByValue, FromRemainingArguments) 120 | Accept wildcard characters: False 121 | ``` 122 | 123 | ### -Separator 124 | Specifies a separator string to the output between objects displayed on the console. 125 | 126 | ```yaml 127 | Type: Object 128 | Parameter Sets: (All) 129 | Aliases: 130 | 131 | Required: False 132 | Position: Named 133 | Default value: None 134 | Accept pipeline input: False 135 | Accept wildcard characters: False 136 | ``` 137 | 138 | ## INPUTS 139 | 140 | ### System.Object 141 | 142 | ## OUTPUTS 143 | 144 | ### System.Object 145 | 146 | ## NOTES 147 | 148 | ## RELATED LINKS 149 | 150 | -------------------------------------------------------------------------------- /Docs/Pansies.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: Pansies 3 | Module Guid: 6c376de1-1baf-4d52-9666-d46f6933bc16 4 | Download Help Link: {{Please enter FwLink manually}} 5 | Help Version: {{2.0}} 6 | Locale: en-US 7 | --- 8 | 9 | # Powershell ANSI Escape Sequences Module 10 | 11 | ## Description 12 | This module contains classes and functions for doing ANSI colored output using Virtual Terminal escape sequences in the console from .Net and PowerShell on platforms where they are supported: Windows 10, Linux, OS X, etc. 13 | 14 | It also contains commands for manipulating the Windows console color palette and converting color settings from iTerm and VSCode, including a "theme" format based on psd1 files. 15 | 16 | It requires PowerShell 5 or higher, and an ANSI-capable host like the Windows 10 console, or xTerm, iTerm, or ConEmu. 17 | 18 | ## Cmdlets 19 | 20 | ### [Get-ColorWheel](Get-ColorWheel.md) 21 | Like Get-Gradient, but allows you to specify the Hue step and by default adjusts the brightness so you don't get exact color repeatition 22 | 23 | ### [Get-Complement](Get-Complement.md) 24 | Get the Hue complement color 25 | 26 | ### [Get-Gradient](Get-Gradient.md) 27 | Get a range of colors between two colors 28 | 29 | ### [New-Hyperlink](New-Hyperlink.md) 30 | Takes a Uri and optional text and writes [a hyperlink](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#file-hyperlinks_in_terminal_emulators-md) supported by most terminals 31 | 32 | ### [New-Text](New-Text.md) 33 | Create a Text object with specified background and foreground colors. Supports [HTML named entities](https://www.w3schools.com/charsets/ref_html_entities_4.asp) like `♥` and `½` or `ü` and numerical unicode character entities in both decimal (e.g. `Ξ`) and hexadeximal (`Ξ`) 34 | 35 | ### [Write-Host](Write-Host.md) 36 | Backwards compatible Write-Host replacement which writes customized output to a host, but using full RGB color values. 37 | 38 | ## Providers 39 | 40 | Pansies provides an RgbColor provider, with two default drives: Fg, and Bg. These are "content" drives which provide a way for you to refer to any color as though it were a variable, like: `${fg:red}` or `${fg:#FF0000}` and even `${fg:clear}` so you can change colors in the middle of a string: 41 | 42 | ``` 43 | C:\PS> "I ${fg:red}♥${fg:clear} PS" 44 | ``` 45 | 46 | ## Classes 47 | 48 | Pansies provides a few important classes: 49 | 50 | *RgbColor* is a powerful representation of RGB colors with support for parsing CSS style color strings "#RRGGBB" and XTerm indexes, as well as handling the ConsoleColor values PowerShell users are used to. In addition to that, it has conversions to other color spaces for the purpose of doing color math like generating palettes and gradients, etc. The `ToString()` implementation shows the properties, but there is an overload which takes a boolean for Background or Foreground and renders to ANSI escape sequences. It has built-in palette for XTerm, and a built-in ConsoleColor palette which (on Windows) sniffs the current console's current color configuration. It uses these palettes to automatically downsample RGB colors to the nearest match when it's necessary to render in those color spaces. 51 | 52 | *Text* is a text class which contains BackgroundColor and ForegroundColor properties and a `ToString()` implementation based on VT escape sequences. It also supports HTML named enties like the `♥` example above. 53 | 54 | ### Color Spaces 55 | 56 | Pansies includes versions of all the old Colormine library classes, so it's a very complete library for color spaces regardless of whether you're doing terminal work or color work in graphical or web applications. There are colorspace classes for HSL, HSB, LAB, and Hunter LAB, CMY and CMYK, LCH, LUV, XYZ, YXY, and of course, RGB. These color spaces support converting colors between them, doing color math like measuring distance, calculating midpoints and gradients, etc. There are also color comparison libraries with implementations of [Cie 76]9http://en.wikipedia.org/wiki/Color_difference#CIE76), [Cie 94](http://en.wikipedia.org/wiki/Color_difference#CIE94), [Cie delta-e 2000](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000) and [Cmc l:c](http://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29). 57 | 58 | 59 | We also have *palette* classes which support the XTerm 256 color palette, the X11 named colors palette (with over 650 named colors), and the default 16 color console palette (which includes loading the actual palette of the `console` when in Windows console). Any palette has the ability to find the closest match to any given RgbColor (by default, using the Cie De2000 algorithm) so you can downscale nicely from full color to 256 or 16 color. 60 | 61 | You can play with setting `[PoshCode.Pansies.RgbColor]::ColorMode` to change how the colors are down-sampled, and you can even modify the actual palettes in `[PoshCode.Pansies.RgbColor]::ConsolePalette` and `[PoshCode.Pansies.RgbColor]::XTermPalette` 62 | 63 | -------------------------------------------------------------------------------- /Docs/Write-Host.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: Pansies-help.xml 3 | online version: 4 | schema: 2.0.0 5 | --- 6 | # Write-Host 7 | 8 | ## SYNOPSIS 9 | 10 | Backwards compatible Write-Host replacement which writes customized output to a host, but using full RGB color values. 11 | 12 | ## SYNTAX 13 | 14 | ``` 15 | Write-Host [[-Object] ] [-NoNewline] [-Separator ] [-ForegroundColor ] [-BackgroundColor ] 16 | ``` 17 | 18 | ## DESCRIPTION 19 | The **Write-Host** cmdlet customizes output. 20 | You can specify the color of text by using the *ForegroundColor* parameter, and you can specify the background color by using the *BackgroundColor* parameter. 21 | The *Separator* parameter lets you specify a string to use to separate displayed objects. 22 | The particular result depends on the program that is hosting Windows PowerShell. 23 | 24 | ## EXAMPLES 25 | 26 | ### Example 1: Write to the console and include a separator 27 | ``` 28 | PS C:\> Write-Host (2,4,6,8,10,12) -Separator ", +2= " 29 | 2, +2= 4, +2= 6, +2= 8, +2= 10, +2= 12 30 | ``` 31 | 32 | This command displays the even numbers from 2 through 12. 33 | The *Separator* parameter is used to add the string ", +2= " (comma, space, +, 2, =, space). 34 | 35 | 36 | ### Example 2: Write with different text and background colors 37 | ``` 38 | PS C:\> Write-Host (2,4,6,8,10,12) -Separator ", -> " -ForegroundColor DarkGreen -BackgroundColor white 39 | ``` 40 | 41 | This command displays the even numbers from 2 through 12. 42 | It uses the *ForegroundColor* parameter to output dark green text and the *BackgroundColor* parameter to display a white background. 43 | 44 | 45 | ### Example 3: Write with different text and background colors 46 | ``` 47 | PS C:\> Write-Host "Red on white text." -ForegroundColor "#FF0000" -BackgroundColor "#FFFFFF" 48 | e[48;2;255;255;255m`e[38;2;255;0;0mRed on white text.`e[49m`e[39m 49 | ``` 50 | 51 | This command displays the string "Red on white text." The text is red, as defined by the *ForegroundColor* parameter. 52 | The background is white, as defined by the *BackgroundColor* parameter. 53 | 54 | 55 | ### Example 4: Write arrays with embedded colors 56 | ``` 57 | PS C:\> Write-Host Check out (New-Hyperlink https://poshcode.org -ForegroundColor Navy) today! -ForegroundColor "#FF0000" -BackgroundColor "#FFFFFF" 58 | Check out https://poshcode.org today! 59 | `e[48;2;255;255;255m`e[38;2;255;0;0mCheck`e[48;2;255;255;255m`e[38;2;255;0;0m out`e[48;2;255;255;255m`e[38;2;255;0;0m `e[38;2;0;0;255m`e]8;;https://poshcode.org`ahttps://poshcode.org`e]8;;`a`e[39m`e[48;2;255;255;255m`e[38;2;255;0;0m today!`e[49m`e[39m 60 | 61 | ``` 62 | 63 | This command displays the string "Red on white text." The text is red, as defined by the *ForegroundColor* parameter. 64 | The background is white, as defined by the *BackgroundColor* parameter. 65 | 66 | ## PARAMETERS 67 | 68 | ### -BackgroundColor 69 | Specifies the background color. 70 | There is no default. 71 | The acceptable values for this parameter are: 72 | 73 | CSS Hex representations of RGB colors like "#FF00FF" or "FF00FF" 74 | 75 | XTerm indexes like "xt138" or "123" 76 | 77 | Console Color names: 78 | 79 | - Black 80 | - DarkBlue 81 | - DarkGreen 82 | - DarkCyan 83 | - DarkRed 84 | - DarkMagenta 85 | - DarkYellow 86 | - Gray 87 | - DarkGray 88 | - Blue 89 | - Green 90 | - Cyan 91 | - Red 92 | - Magenta 93 | - Yellow 94 | - White 95 | 96 | ```yaml 97 | Type: RgbColor 98 | Parameter Sets: (All) 99 | Aliases: 100 | 101 | Required: False 102 | Position: Named 103 | Default value: None 104 | Accept pipeline input: False 105 | Accept wildcard characters: False 106 | ``` 107 | 108 | ### -ForegroundColor 109 | Specifies the text color. 110 | There is no default. 111 | The acceptable values for this parameter are: 112 | 113 | CSS Hex representations of RGB colors like "#FF00FF" or "FF00FF" 114 | 115 | XTerm indexes like "xt138" or "123" 116 | 117 | Console Color names: 118 | 119 | - Black 120 | - DarkBlue 121 | - DarkGreen 122 | - DarkCyan 123 | - DarkRed 124 | - DarkMagenta 125 | - DarkYellow 126 | - Gray 127 | - DarkGray 128 | - Blue 129 | - Green 130 | - Cyan 131 | - Red 132 | - Magenta 133 | - Yellow 134 | - White 135 | 136 | ```yaml 137 | Type: PoshCode.Pansies.RgbColor 138 | Parameter Sets: (All) 139 | Aliases: 140 | 141 | Required: False 142 | Position: Named 143 | Default value: None 144 | Accept pipeline input: False 145 | Accept wildcard characters: False 146 | ``` 147 | 148 | ### -NoNewline 149 | Specifies that the content displayed in the host does not end with a newline character. 150 | 151 | ```yaml 152 | Type: SwitchParameter 153 | Parameter Sets: (All) 154 | Aliases: 155 | 156 | Required: False 157 | Position: Named 158 | Default value: None 159 | Accept pipeline input: False 160 | Accept wildcard characters: False 161 | ``` 162 | 163 | ### -Object 164 | Specifies objects to display in the host. 165 | 166 | ```yaml 167 | Type: Object 168 | Parameter Sets: (All) 169 | Aliases: 170 | 171 | Required: False 172 | Position: 0 173 | Default value: None 174 | Accept pipeline input: True (ByValue, FromRemainingArguments) 175 | Accept wildcard characters: False 176 | ``` 177 | 178 | ### -Separator 179 | Specifies a separator string to the output between objects displayed on the console. 180 | 181 | ```yaml 182 | Type: Object 183 | Parameter Sets: (All) 184 | Aliases: 185 | 186 | Required: False 187 | Position: Named 188 | Default value: None 189 | Accept pipeline input: False 190 | Accept wildcard characters: False 191 | ``` 192 | 193 | ### -PersistentColor 194 | Specifies that foreground and background colors will be repeated for each object. 195 | 196 | ```yaml 197 | Type: SwitchParameter 198 | Parameter Sets: (All) 199 | Aliases: 200 | 201 | Required: False 202 | Position: Named 203 | Default value: None 204 | Accept pipeline input: False 205 | Accept wildcard characters: False 206 | ``` 207 | 208 | ### CommonParameters 209 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 210 | 211 | ## INPUTS 212 | 213 | ### System.Object 214 | You can pipe objects to be written to the host. 215 | 216 | ## OUTPUTS 217 | 218 | ### None 219 | **Write-Host** sends the objects to the host. 220 | It does not return any objects. 221 | However, the host might display the objects that **Write-Host** sends to it. 222 | 223 | ## NOTES 224 | 225 | ## RELATED LINKS 226 | 227 | [Clear-Host](../Microsoft.PowerShell.Core/Functions/Clear-Host.md) 228 | 229 | [Write-Debug](Write-Debug.md) 230 | 231 | [Write-Error](Write-Error.md) 232 | 233 | [Write-Output](Write-Output.md) 234 | 235 | [Write-Progress](Write-Progress.md) 236 | 237 | [Write-Verbose](Write-Verbose.md) 238 | 239 | [Write-Warning](Write-Warning.md) 240 | 241 | 242 | -------------------------------------------------------------------------------- /Earthfile: -------------------------------------------------------------------------------- 1 | VERSION 0.7 2 | IMPORT github.com/poshcode/tasks 3 | FROM mcr.microsoft.com/dotnet/sdk:7.0 4 | WORKDIR /work 5 | 6 | ARG --global EARTHLY_GIT_ORIGIN_URL 7 | ARG --global EARTHLY_BUILD_SHA 8 | ARG --global EARTHLY_GIT_BRANCH 9 | # These are my common paths, used in my shared /Tasks repo 10 | ARG --global OUTPUT_ROOT=/Modules 11 | ARG --global TEST_ROOT=/tests 12 | ARG --global TEMP_ROOT=/temp 13 | # These are my common build args, used in my shared /Tasks repo 14 | ARG --global MODULE_NAME=Pansies 15 | ARG --global CONFIGURATION=Release 16 | 17 | 18 | worker: 19 | # Dotnet tools and scripts installed by PSGet 20 | ENV PATH=$HOME/.dotnet/tools:$HOME/.local/share/powershell/Scripts:$PATH 21 | RUN mkdir /Tasks \ 22 | && git config --global user.email "Jaykul@HuddledMasses.org" \ 23 | && git config --global user.name "Earthly Build" 24 | # I'm using Invoke-Build tasks from this other repo which rarely changes 25 | COPY tasks+tasks/* /Tasks 26 | # Dealing with dependencies first allows docker to cache packages for us 27 | # So the dependency cach only re-builds when you add a new dependency 28 | COPY RequiredModules.psd1 . 29 | COPY *.csproj . 30 | RUN ["pwsh", "-File", "/Tasks/_Bootstrap.ps1", "-RequiredModulesPath", "RequiredModules.psd1"] 31 | 32 | build: 33 | FROM +worker 34 | RUN mkdir $OUTPUT_ROOT $TEST_ROOT $TEMP_ROOT 35 | COPY . . 36 | # make sure you have bin and obj in .earthlyignore, as their content from context might cause problems 37 | RUN ["pwsh", "-Command", "Invoke-Build", "-Task", "Build", "-File", "Build.build.ps1"] 38 | 39 | # SAVE ARTIFACT [--keep-ts] [--keep-own] [--if-exists] [--force] [] [AS LOCAL ] 40 | SAVE ARTIFACT $OUTPUT_ROOT/$MODULE_NAME AS LOCAL ./Modules/$MODULE_NAME 41 | 42 | test: 43 | # If we run a target as a reference in FROM or COPY, it's outputs will not be produced 44 | BUILD +build 45 | FROM +build 46 | # make sure you have bin and obj in .earthlyignore, as their content from context might cause problems 47 | RUN ["pwsh", "-Command", "Invoke-Build", "-Task", "Test", "-File", "Build.build.ps1"] 48 | 49 | # SAVE ARTIFACT [--keep-ts] [--keep-own] [--if-exists] [--force] [] [AS LOCAL ] 50 | SAVE ARTIFACT $TEST_ROOT AS LOCAL ./Modules/$MODULE_NAME-TestResults 51 | 52 | pack: 53 | BUILD +test # So that we get the module artifact from build too 54 | FROM +test 55 | RUN ["pwsh", "-Command", "Invoke-Build", "-Task", "Pack", "-File", "Build.build.ps1", "-Verbose"] 56 | SAVE ARTIFACT $OUTPUT_ROOT/publish/*.nupkg AS LOCAL ./Modules/$MODULE_NAME-Packages/ 57 | 58 | push: 59 | FROM +pack 60 | RUN --push --secret NUGET_API_KEY --secret PSGALLERY_API_KEY -- \ 61 | pwsh -Command Invoke-Build -Task Push -File Build.build.ps1 -Verbose 62 | 63 | all: 64 | # BUILD +build 65 | BUILD +test 66 | BUILD +pack 67 | BUILD +push 68 | -------------------------------------------------------------------------------- /Examples/HslEnumerator.ps1: -------------------------------------------------------------------------------- 1 | #requires -Modules Pansies 2 | using namespace System 3 | using namespace System.IO 4 | using namespace System.Collections 5 | using namespace System.Collections.Generic 6 | using namespace System.Linq 7 | class HslEnumerator : IEnumerator[PoshCode.Pansies.RgbColor] { 8 | hidden [PoshCode.Pansies.RgbColor]$Start = "White" 9 | [int]$HueStep = 1 10 | [int]$LightStep = 1 11 | [int]$SatStep = 1 12 | 13 | # Default constructor so we can cast hashtables 14 | HslEnumerator() {} 15 | 16 | HslEnumerator([PoshCode.Pansies.RgbColor]$Start) { 17 | $this.Start = $Start 18 | } 19 | 20 | # Implement IEnumerator. 21 | [PoshCode.Pansies.RgbColor]$Current = $null 22 | 23 | [object] get_Current() { 24 | return $this.Current 25 | } 26 | 27 | [bool] MoveNext() { 28 | if ($null -eq $this.Current) { 29 | $this.Current = $this.Start 30 | return $true 31 | } 32 | 33 | $next = $this.Current.ToHsl(); 34 | $next.H += $this.HueStep; 35 | $next.H %= 360; 36 | 37 | if ($next.S + $this.SatStep -gt 100) { 38 | $next.S = 20; 39 | } else { 40 | $next.S += $this.SatStep; 41 | } 42 | if ($next.L + $this.LightStep -gt 80) { 43 | $next.L = 20; 44 | } else { 45 | $next.L += $this.LightStep 46 | } 47 | 48 | $this.Current = $next.ToRgb(); 49 | 50 | return $true; 51 | } 52 | 53 | [void] Reset() { 54 | $this.Current = $null; 55 | } 56 | 57 | # IDisposable is required by IEnumerator 58 | [void] Dispose() {} 59 | } -------------------------------------------------------------------------------- /Examples/Out-Rainbow.ps1: -------------------------------------------------------------------------------- 1 | filter Out-Colors { 2 | [CmdletBinding()] 3 | param( 4 | [HslEnumerator]$Colors = @{ Start = "GoldenRod"; HueStep = 18 }, 5 | 6 | [Parameter(ValueFromPipeline)] 7 | $FormatInfoData 8 | ) 9 | # Only color the data (not the headers) 10 | if ($_.GetType().Name -eq "FormatEntryData") { 11 | # Listview 12 | @($_.FormatEntryInfo.ListViewFieldList).Where{ $_ }.ForEach{ 13 | $null = $Colors.MoveNext() 14 | $_.FormatPropertyField.propertyValue = $Colors.Current.ToVt() + $_.FormatPropertyField.propertyValue 15 | } 16 | # Tableview 17 | @($_.FormatEntryInfo.FormatPropertyFieldList).Where{ $_ }.ForEach{ 18 | $null = $Colors.MoveNext() 19 | $_.propertyValue = $Colors.Current.ToVt() + $_.propertyValue 20 | } 21 | } 22 | # Pass through everything 23 | $_ 24 | } -------------------------------------------------------------------------------- /Examples/Show-Complement.ps1: -------------------------------------------------------------------------------- 1 | 0..255 | % { 2 | $_ = "xt$_" 3 | Write-Host " " -BackgroundColor $_ -NoNewline 4 | $Bg, $Fg = Get-Complement $_ -Passthru 5 | Write-Host " " -BackgroundColor $Fg -NoNewline 6 | Write-Host " Simple Complement " -ForegroundColor $Fg -BackgroundColor $Bg -NoNewline 7 | $Bg, $Fg = Get-Complement $_ -Passthru -ForceContrast 8 | Write-Host " " -BackgroundColor $Fg -NoNewline 9 | Write-Host " -ForceContrast " -ForegroundColor $Fg -BackgroundColor $Bg -NoNewline 10 | Write-Host 11 | } -------------------------------------------------------------------------------- /Examples/Show-Gradient.ps1: -------------------------------------------------------------------------------- 1 | using namespace PoshCode.Pansies 2 | [CmdletBinding()] 3 | param( 4 | [PoshCode.Pansies.RgbColor] 5 | $LeftColor = "DarkBlue", 6 | 7 | [PoshCode.Pansies.RgbColor] 8 | $RightColor = "Red", 9 | 10 | [ValidateSet("CMYK","LAB","LUV","HunterLAB","HSL","HSLReverse","RGB","XYZ")] 11 | $ColorSpace = "LAB" 12 | ) 13 | 14 | Get-Gradient $LeftColor $RightColor -ColorSpace $ColorSpace -Flatten | 15 | ForEach-Object { Write-Host " " -BackgroundColor $_ -NoNewline} -------------------------------------------------------------------------------- /Examples/Write-Gradient.ps1: -------------------------------------------------------------------------------- 1 | using namespace PoshCode.Pansies 2 | [CmdletBinding()] 3 | param( 4 | [Object]$Object = "Hello World", 5 | 6 | [PoshCode.Pansies.RgbColor] 7 | $StartColor = "xt33", 8 | 9 | [PoshCode.Pansies.RgbColor] 10 | $EndColor = "xt199", 11 | 12 | [ValidateSet("CMYK","LAB","LUV","HunterLAB","HSL","HSLReverse","HSV","RGB","XYZ")] 13 | $ColorSpace = "LAB" 14 | ) 15 | process { 16 | $Text = [Text]::ConvertToString($Object) 17 | 18 | $Color = Get-Gradient -Start $StartColor -End $EndColor -Height 1 -Width $Text.Length -ColorSpace $ColorSpace -Flatten 19 | 20 | for($c = 0; $c -lt $Color.Length; $c++) { 21 | # Fun twist: use HSL or LAB to pick a darker version of the color: 22 | $LAB = ([PoshCode.Pansies.RgbColor]$Color[$c]).ToHunterLab() 23 | # Invert the color 24 | $LAB.L = 100 - $LAB.L 25 | # And then push it to make it darker or lighter 26 | if($LAB.L -gt 50) { 27 | $LAB.L += (100 - $Lab.L) * .4 28 | } else { 29 | $LAB.L -= $LAB.L * .4 30 | } 31 | $fg = $LAB.ToRgb() 32 | 33 | $LAB | Out-String | Write-Verbose 34 | # # Or just rotate the Hue: 35 | # $HSL = ([PoshCode.Pansies.RgbColor]$Color[$c]).ToHsl() 36 | # $HSL.H = ($HSL.H + 180) % 360 37 | # $HSL.S = 100 38 | # if($HSL.L -gt .4) { 39 | # $HSL.L *= .75 40 | # } else { 41 | # $HSL.L *= 1.5 42 | # } 43 | # $fg = $HSL.ToRgb() 44 | 45 | Write-Host $Text[$c] -BackgroundColor $Color[$c] -ForegroundColor $fg -NoNewline 46 | } 47 | 48 | Write-Host 49 | } -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: Mainline 2 | assembly-versioning-format: '{Major}.{Minor}.{Patch}.{env:BUILDCOUNT ?? 0}' 3 | assembly-informational-format: '{NuGetVersionV2}+Build.{env:BUILDCOUNT ?? 0}.Date.{CommitDate}.Branch.{env:SAFEBRANCHNAME ?? unknown}.Sha.{ShortSha}' 4 | branches: 5 | master: 6 | increment: Minor 7 | pull-request: 8 | tag: rc 9 | increment: inherit 10 | hotfix: 11 | regex: /[Ff]ix 12 | increment: Patch 13 | source-branches: 14 | - master 15 | feature: 16 | increment: Minor 17 | regex: .*?/ 18 | source-branches: 19 | - master 20 | - feature 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Joel Bennett, Joe Zack 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pansies.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | PoshCode.Pansies 5 | embedded 6 | PoshCode.Pansies 7 | Joel Bennett 8 | HuddledMasses.org 9 | Release 10 | README.md 11 | MIT 12 | 13 | 14 | 15 | 16 | 17 | True 18 | True 19 | Colors.tt 20 | 21 | 22 | TextTemplatingFileGenerator 23 | Colors.cs 24 | 25 | 26 | ColorSpaces.tt 27 | True 28 | True 29 | 30 | 31 | ColorSpaces.cs 32 | TextTemplatingFileGenerator 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 57 | 58 | 59 | 60 | 61 | $(TargetsForTfmSpecificBuildOutput);PackEverything 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Presentation/0.Introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | theme: night 3 | transition: slide 4 | highlightTheme: atelier-lakeside-dark 5 | slideNumber: true 6 | defaultTiming: 100 7 | fragments: true 8 | --- 9 | 10 | # PANSIES 11 | ## A Primer on ANSI Escape Sequences 12 | 13 | 14 | 15 | 16 | --- 17 | 18 | ## A Little About Me 19 | 20 | Joel "Jaykul" Bennett 21 | 22 | 12x PowerShell MVP 23 | 24 | Principal DevOps Engineer at loanDepot 25 | 26 | --- 27 | 28 | ### ANSI Escape Sequences 29 | - AKA [Virtual Terminal Sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) 30 | - AKA [Control Sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html) 31 | 32 | > Character sequences for color, cursor movement, character modes, text modification, and other operations. 33 | 34 | note: 35 | Sequences may also be received on the input stream in response to an output stream query information sequence or as an encoding of user input when the appropriate mode is set. 36 | 37 | --- 38 | 39 | ### Historical Notes: 40 | 41 | ANSI X3.4 is also known as US-ASCII 42 | 43 | 128 characters, and their 7-bit encoding 44 | 45 | The first spec leading to ECMA, ISO and Unicode 46 | 47 | note: 48 | "American National Standard for Information Systems – Coded Character Sets – 7-Bit American National Standard Code for Information Interchange." 49 | 50 | -- 51 | 52 | #### American National Standards Institute 53 | 54 | The "X3" in ANSI X3.4 refers to the sponsoring committee: the Accredited Standards Committee X3, Information Technology, which is now the: 55 | 56 | ##### InterNational Committee for Information Technology Standards 57 | 58 | -- 59 | 60 | ### Evolving Standards 61 | 62 | - 7-bit ANSI X3.4 (is now [INCITS 4](https://webstore.ansi.org/Standards/INCITS/INCITS1986R2017))
63 | Is also [ECMA-6](https://www.ecma-international.org/publications-and-standards/standards/ecma-6/) and [ISO 646](https://en.wikipedia.org/wiki/ISO/IEC_646) 64 | - Is succeeded by [ECMA-43](https://www.ecma-international.org/publications-and-standards/standards/ecma-43/) & [94](https://www.ecma-international.org/publications-and-standards/standards/ecma-94/) and [ISO 8859](https://en.wikipedia.org/wiki/ISO/IEC_8859) 65 | - Is succeeded by UTF-8 66 | in [ISO 10646](https://en.wikipedia.org/wiki/ISO/IEC_10646) (Unicode) 67 | 68 | note: 69 | ISO 646 (ECMA-6) was the 7-bit encoding 70 | ISO 8859 (ECMA-43) is a 8-bit encoding 71 | ISO 10646 includes 16-bit and even 32-bit encodings 72 | 73 | -- 74 | 75 | ### Digital Equipment Company 76 | 77 | DEC made **ANSI-compatible** video terminals like the 7-bit VT100 78 | 79 | Proprietary control sequences known as ANSI escape sequences because they start with an ANSI escape character (ESC) -- encoded as 27 `0x1B`. 80 | 81 | Eventually, ANSI and ECMA created [ECMA-48](https://www.ecma-international.org/publications-and-standards/standards/ecma-48/) in 1976 -- it's now also [ISO/IEC 6429](https://en.wikipedia.org/wiki/ISO/IEC_6429) 82 | 83 | Only matters because [XTerm implements it](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)... 84 | 85 | --- 86 | 87 | ## Escape Sequences? 88 | 89 | In 7-bit mode, they all started with ESC (⎋) 90 | 91 | In 8-bit mode, you can use a few shortcuts: 92 | 93 | ESC | VALUE | NAME 94 | --- | ----- | ---- 95 | ⎋P | 0x90 | Device Control String 96 | ⎋[ | 0x9b | Control Sequence Introducer 97 | ⎋\\ | 0x9c | String Terminator 98 | ⎋] | 0x9d | Operating System Command 99 | 100 | There are more [8-Bit Control Characters](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-C1-_8-Bit_-Control-Characters) 101 | 102 | But you probably won't use them 103 | 104 | --- 105 | 106 | # Show Me! 107 | ### What Can They Do? 108 | 109 | --- 110 | 111 | ## You asked for it: 112 | #### DEC Line Drawing Mode 113 | 114 | Drawing Mode: `` `e(0 ``
115 | Normal Mode: `` `e(B `` 116 | 117 | ```PowerShell 118 | "`e(0 119 | lqqqqqqqqqqk 120 | x x 121 | x x 122 | mqqqqqqqqqqj 123 | `e(B" 124 | ``` 125 | Or this: 126 | ```PowerShell 127 | param( $Width = 15, $Height = 7 ) 128 | "`e(0" 129 | "l$("q"*$Width)k" 130 | "x$(" "*$Width)x`n" * $Height + 131 | "m$("q"*$Width)j" 132 | "`e(B" 133 | ``` 134 | 135 | note: OK, that's not too exciting, but consider how much fun it would be to stick a `e(0 into your friend's profile on April 1st! 136 | 137 | note: In addition to modes, we can position t 138 | 139 | --- 140 | 141 | ## Cursor Positioning 142 | 143 | -- 144 | 145 | ### Relative Movement 146 | note: Within the viewport, no scrolling 147 | 148 | CHAR | BEHAVIOR | CHAR | BEHAVIOR 149 | ---- | -------- | ---- | -------- 150 | `⎋A` | Cursor Up | `⎋B` | Cursor Down 151 | `⎋C` | Cursor Right | `⎋D` | Cursor Left 152 | `⎋7` | Save Position | `⎋8` | Restore Position 153 | 154 | These also work parameterized as: 155 | 156 | `⎋[4A` -- to go up 4 times 157 | 158 | -- 159 | 160 | ### Absolute Positioning 161 | 162 | Sequence | Result 163 | -------- | ------ 164 | `⎋[4G` | 4th column in the current line 165 | `⎋[4d` | 4th row in the current column 166 | `⎋[2;4f` | 4th column in 2nd row 167 | 168 | 169 | -- 170 | 171 | ### A PowerShell Example 172 | 173 | Now we can draw a box at a specific position: 174 | 175 | $X=8$, $Y=3$, $Height=12$, $Width=50$ 176 | 177 | 178 | ```PowerShell {data-line-numbers="4,8,11"} 179 | $position = "`e[{0};{1}f" 180 | -join @( 181 | "`e(0" 182 | $position -f $Top++, $Left 183 | "l" + ("q" * $Width) + "k" 184 | 185 | while ($Top -lt $Height) { 186 | $position -f $Top++, $Left 187 | "x" + (" " * $Width) + "x" 188 | } 189 | $position -f $Top++, $Left 190 | "m" + ("q" * $Width) + "j" 191 | "`e(B" 192 | ) 193 | ``` 194 | 195 | note: run this over the top of some output 196 | note: don't you want to clear the screen? 197 | 198 | --- 199 | 200 | ## Editing Text 201 | 202 | -- 203 | 204 | ### Inserting & Deleting Text 205 | 206 | Sequence | Result 207 | -------- | ------ 208 | `⎋[4@` | Insert 4 spaces, shifting text right 209 | `⎋[4L` | Insert 4 lines, pushing lines down 210 | `⎋[4X` | Erase 4 characters (with space) 211 | `⎋[4P` | Delete 4 characters, fill from right 212 | `⎋[4M` | Delete 4 lines (including this one) 213 | `⎋[2J` | Erase the whole display (0 ->, <- 1) 214 | `⎋[0K` | Erase the current line (0 ->, <- 1) 215 | 216 | -- 217 | 218 | ### Now we can clear: 219 | 220 | We just add `⎋[2J` at the top: 221 | 222 | ```PowerShell {data-line-numbers="3"} 223 | $position = "`e[{0};{1}f" 224 | -join @( 225 | "`e[2J" 226 | "`e(0" 227 | $position -f $Top++, $Left 228 | "l" + ("q" * $Width) + "k" 229 | 230 | while ($Top -lt $Height) { 231 | $position -f $Top++, $Left 232 | "x" + (" " * $Width) + "x" 233 | } 234 | $position -f $Top++, $Left 235 | "m" + ("q" * $Width) + "j" 236 | "`e(B" 237 | ) 238 | ``` 239 | 240 | note: but I really want some color 241 | 242 | --- 243 | 244 | ## Can we talk color yet? 245 | 246 | -- 247 | 248 | ### Text Colors 249 | 250 | Foreground: `⎋[3#m` (`⎋[9#m` bright)
251 | Background: `⎋[4#m` (`⎋[10#m` bright) 252 | 253 | 0 Black | 4 Blue 254 | 1 Red | 5 Magenta 255 | 2 Green | 6 Cyan 256 | 3 Yellow | 7 White 257 | - | - 258 | 8 Extended colors:
Full RGB values: `⎋[38;2;R;G;Bm`|| 259 | 9 Default color resets the color || 260 | 261 | `⎋[0m` to reset all colors 262 | 263 | note: ``` 264 | note: 38 ; 2 ; r ; g ; b Set foreground color to RGB value specified in r, g, b parameters* 265 | note: 48 ; 2 ; r ; g ; b Set background color to RGB value specified in r, g, b parameters* 266 | note: 38 ; 5 ; s Set foreground color to s index in 88 or 256 color table* 267 | note: 48 ; 5 ; s Set background color to s index in 88 or 256 color table* 268 | note: ``` 269 | 270 | -- 271 | 272 | ### Text Formatting 273 | 274 | `⎋[0m` | Default 275 | `⎋[1m` | Bold (Bright) 276 | - | - 277 | `⎋[22m` | Remove Bold/Bright 278 | `⎋[4m` | Underline 279 | `⎋[24m` | Remove Underline 280 | `⎋[7m` | Negative 281 | `⎋[27m` | Remove Negative 282 | 283 | -- 284 | 285 | ### A PowerShell Example 286 | Print a grid of the standard colors: 287 | 288 | 289 | ```powershell 290 | 291 | foreach ($f in 0..7) { 292 | -join (0..7).ForEach{ "`e[4$_;3$f;2m gYw `e[40;30m " } 293 | -join (0..7).ForEach{ "`e[4$_;3$f;1m gYw `e[40;30m " } 294 | } 295 | 296 | ``` 297 | 298 | -- 299 | 300 | 301 | 302 | --- 303 | 304 | ## But wait, there's more 305 | 306 | -- 307 | 308 | ### Scrolling 309 | 310 | Set the scrolling region: 311 | 312 | `⎋[ top ; bottom r` 313 | 314 | -- 315 | 316 | ### Buffers 317 | 318 | New alternate screen buffer: 319 | 320 | `⎋[?1049h` 321 | 322 | note: Can be called repeatedly 323 | 324 | Return to main screen buffer: 325 | 326 | `⎋[?1049l` 327 | 328 | --- 329 | 330 | ### Put it all together: 331 | 332 | 333 | ```PowerShell {data-line-numbers="2,14"} 334 | $position = "`e[{0};{1}f" 335 | "`e[?1049h`e[38;2;255;200;68m`e[48;2;200;79;104m`e(0" 336 | -join @( 337 | $position -f $Top++, $Left 338 | "l" + ("q" * $Width) + "k" 339 | while ($Top -lt $Height) { 340 | $position -f $Top++, $Left 341 | "x" + (" " * $Width) + "x" 342 | } 343 | $position -f $Top++, $Left 344 | "m" + ("q" * $Width) + "j" 345 | ) 346 | Read-Host "`e(B" 347 | "`e[?1049l" 348 | ``` 349 | 350 | --- 351 | 352 | # PANSIES 353 | ## Just Demo It 354 | ### 6 Commands 355 | ### 1 Provider 356 | 357 | -- 358 | 359 | ## Write-Host 360 | 361 | Obviously this clobbers the built-in `Write-Host` command. It's compatible, but faster: 362 | 363 | 364 | 365 | 366 | It supports console colors by name, X11 colors by name or indexes, and web-style `#rrggbb` colors 367 | 368 | note: which you may know as web-safe color names 369 | 370 | note: Write-Host "Hello World" -ForegroundColor "#ff0066" 371 | 372 | note: Write-Host "Hello World" -ForegroundColor "#ff0066" -BackgroundColor "Orange" 373 | 374 | -- 375 | 376 | ## Get-ColorWheel 377 | 378 | Colors _looping_ from a starting color, like a rainbow 379 | 380 | Useful for getting sets of unique colors 381 | 382 | ```PowerShell 383 | Get-ColorWheel "#880033" -Count 10 -HueStep 10 384 | ``` 385 | 386 | ```PowerShell 387 | Get-ColorWheel "#880033" -Count 10 -HueStep 20 | 388 | Write-Host -BackgroundColor {$_} " " -NoNewline 389 | ``` 390 | 391 | Rotates hue _and brightness_, by default: 392 | 393 | ```PowerShell 394 | Get-ColorWheel "#880033" -Count 20 | 395 | Write-Host -BackgroundColor {$_} " " -NoNewline 396 | ``` 397 | 398 | -- 399 | 400 | ## Get-Gradient 401 | 402 | A gradient between two colors: 403 | 404 | ```PowerShell 405 | Get-Gradient Blue IndianRed -Count 5 406 | ``` 407 | 408 | By default, it's full-screen width: 409 | 410 | ```PowerShell 411 | Get-Gradient Blue IndianRed | 412 | Write-Host " " -NoNewline -BackgroundColor { $_ } 413 | ``` 414 | 415 | 416 | 417 | ```PowerShell 418 | Get-Gradient Blue4 Yellow | 419 | Write-Host " " -NoNewline -BackgroundColor { $_ } 420 | ``` 421 | 422 | 423 | ```PowerShell 424 | $I = 0; $Text = " Hello World " 425 | Get-Gradient Blue4 Yellow -Width $Text.Length | 426 | Write-Host { $Text[$global:i++] } -No -Bg { $_ } 427 | ``` 428 | 429 | 430 | 431 | -- 432 | 433 | ## Get-Complement 434 | 435 | Gets the hue-complement color 436 | 437 | ```PowerShell 438 | Get-Complement Cyan 439 | ``` 440 | note: Returns Red (the complement for Cyan) 441 | 442 | ```PowerShell 443 | Get-Complement Cyan -Passthru 444 | ``` 445 | note: Get the original color also with -Pasthru 446 | 447 | ```PowerShell 448 | $I = 0; $Text = " Hello World " 449 | Get-Gradient Blue4 Yellow -Width $Text.Length -Pv Bg | 450 | Get-Complement | 451 | Write-Host { $Text[$global:i++] } -No -Bg { $Bg } -Fg { $_ } 452 | ``` 453 | 454 | Option | Result 455 | ------ | ------ 456 | Normal | 457 | High Contrast | 458 | Black & White | 459 | 460 | 461 | note: ```PowerShell 462 | $I = 0; $Text = " Hello World " 463 | Get-Gradient Blue4 Yellow -Width $Text.Length -Pv Bg | 464 | Get-Complement -HighContrast | 465 | Write-Host { $Text[$global:i++] } -No -Bg { $Bg } -Fg { $_ } 466 | note: ``` 467 | 468 | note: ```PowerShell 469 | $I = 0; $Text = " Hello World " 470 | Get-Gradient Blue4 Yellow -Width $Text.Length -Pv Bg | 471 | Get-Complement -BlackAndWhite | 472 | Write-Host { $Text[$global:i++] } -No -Bg { $Bg } -Fg { $_ } 473 | note: ``` 474 | 475 | -- 476 | 477 | ## New-Text 478 | 479 | #### Supports `&entities;` and colors. 480 | Outputs nestable rich objects you can modify and render later. 481 | 482 |
483 | 484 | :::block {class="fragment fade-out" data-fragment-index="0"} 485 | ```PowerShell 486 | "Your father was a hampster, and your mother smelled of elderberries." | 487 | % { $(switch -regex (-split $_ | New-Text) { 488 | 489 | "father" { $_.ForegroundColor = "DarkGreen" } 490 | "mother" { $_.ForegroundColor = "DarkRed" } 491 | "ther" { $_.BackgroundColor = "Wheat" } 492 | 493 | "." { $_ } 494 | }) -join " " } 495 | ``` 496 | ::: 497 | 498 | :::block {class="fragment" data-fragment-index="0"} 499 | 500 | ::: 501 | 502 |
503 | 504 | --- 505 | 506 | ## Colored Modules 507 | 508 | Module | Mechanism 509 | ------ | --------- 510 | [PowerLine](https://github.com/Jaykul/PowerLine/) | RGBColor, Inline 511 | [EzTheme](https://github.com/Jaykul/EzTheme/) | RGBColor, Format Files 512 | [Figlet](https://github.com/Jaykul/Figlet/) | Format Files 513 | 514 | -- 515 | 516 | # [PowerLine](https://github.com/Jaykul/PowerLine/) 517 | 518 | #### Beautiful, Powerful, PowerShell Prompts 519 | 520 | PowerLine uses Pansies (and `Get-Gradient`) 521 | 522 | 523 | ```PowerShell 524 | Set-PowerLinePrompt -Colors "#FFDD00", "#FF6600" -Time -Newline 525 | ``` 526 | 527 | ```PowerShell 528 | Set-PowerLinePrompt -Colors "#00DDFF", "#0066FF" 529 | ``` 530 | 531 | note: 532 | Don't forget to look at `$Prompt` 533 | 534 | note: 535 | Also talk about [PoshCode.Pansies.Entities] 536 | 537 | -- 538 | 539 | # [EzTheme](https://github.com/Jaykul/EzTheme/) 540 | 541 | - ### Theme.PowerShell 542 | - ### Theme.Terminal 543 | - ### Theme.PSReadLine 544 | 545 | 546 | -- 547 | 548 | # [Figlet](https://github.com/Jaykul/Figlet/) 549 | 550 | Even after the last one ... 551 | 552 | You may not believe this one. 553 | 554 | It's all done with format files! 555 | 556 | --- 557 | 558 | ## Have you seen Terminal.Gui? 559 | 560 | - [Microsoft.PowerShell.GraphicalTools](https://github.com/PowerShell/GraphicalTools/) 561 | - [TUI PowerShell Designer (Commercial)](https://blog.ironmansoftware.com/tui-powershell/) 562 | 563 | 564 | --- 565 | 566 | ``` 567 | a primer on ANSI escape sequences: 568 | - what they are, 569 | - what you can do with them, 570 | - why they're the cross-platform answer to 571 | - building user interfaces 572 | - better user experiences at the command-line. 573 | 574 | One of my favorite modules: PANSIES, 575 | - how you can use it 576 | - when you need it 577 | some of the ways you can use it to colorize and beautify the output of your PowerShell scripts and modules, and 578 | - demonstrate its use in a few modules to: 579 | - theme and color prompts 580 | - output 581 | - syntax highlighting 582 | - and more... 583 | ``` -------------------------------------------------------------------------------- /Presentation/01.DrawingMode.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Width = 15, 3 | $Height = 7 4 | ) 5 | 6 | "`e(0" 7 | "l$("q"*$Width)k" 8 | "x$(" "*$Width)x`n" * $Height + 9 | "m$("q"*$Width)j" 10 | "`e(B" -------------------------------------------------------------------------------- /Presentation/02.Movement.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Top = 3, 3 | $Left = 6, 4 | $height = 12, 5 | $width = 50 6 | ) 7 | $Height += $Top 8 | $position = "`e[{0};{1}f" 9 | -join @( 10 | "`e(0" 11 | $position -f $Top++, $Left 12 | "l" + ("q" * $Width) + "k" 13 | 14 | while ($Top -lt $Height) { 15 | $position -f $Top++, $Left 16 | "x" + (" " * $Width) + "x" 17 | } 18 | $position -f $Top++, $Left 19 | "m" + ("q" * $Width) + "j" 20 | "`e(B" 21 | ) -------------------------------------------------------------------------------- /Presentation/03.Modifying.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Top = 3, 3 | $Left = 6, 4 | $height = 12, 5 | $width = 50 6 | ) 7 | $Height += $Top 8 | $position = "`e[{0};{1}f" 9 | -join @( 10 | "`e[2J" 11 | "`e(0" 12 | $position -f $Top++, $Left 13 | "l" + ("q" * $Width) + "k" 14 | 15 | while ($Top -lt $Height) { 16 | $position -f $Top++, $Left 17 | "x" + (" " * $Width) + "x" 18 | } 19 | $position -f $Top++, $Left 20 | "m" + ("q" * $Width) + "j" 21 | "`e(B" 22 | ) -------------------------------------------------------------------------------- /Presentation/04.Color.ps1: -------------------------------------------------------------------------------- 1 | foreach ($f in 0..7) { 2 | -join (0..7).ForEach{ "`e[4$_;3$f;2m gYw `e[40;30m " } 3 | -join (0..7).ForEach{ "`e[4$_;3$f;1m gYw `e[40;30m " } 4 | } -------------------------------------------------------------------------------- /Presentation/05.Put-It-Together.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Top = 3, 3 | $Left = 6, 4 | $height = 12, 5 | $width = 50 6 | ) 7 | $Height += $Top 8 | $position = "`e[{0};{1}f" 9 | "`e[?1049h`e[38;2;255;200;68m`e[48;2;200;79;104m`e(0" 10 | -join @( 11 | $position -f $Top++, $Left 12 | "l" + ("q" * $Width) + "k" 13 | while ($Top -lt $Height) { 14 | $position -f $Top++, $Left 15 | "x" + (" " * $Width) + "x" 16 | } 17 | $position -f $Top++, $Left 18 | "m" + ("q" * $Width) + "j" 19 | ) 20 | "`e(B" 21 | Read-Host 22 | "`e[?1049l" -------------------------------------------------------------------------------- /Presentation/06.Complete.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | $Top = 3, 3 | $Left = 6, 4 | $height = 12, 5 | $width = 50, 6 | $bg = "`e[48;2;{0};{1};{2}m" -f (189, 56, 89), 7 | $fg = "`e[38;2;{0};{1};{2}m" -f (255, 255, 255) 8 | ) 9 | $j,$k,$l,$m,$q,$x = [string[]][char[]]"╝╗╔╚═║" 10 | 11 | $Height += $Top 12 | $position = "`e[{0};{1}f$bg$fg" 13 | 14 | -join @( 15 | "`e[?1049h" 16 | $position -f $Top++, $Left 17 | $l + ($q * $Width) + $k 18 | 19 | while ($Top -lt $Height) { 20 | $position -f $Top++, $Left 21 | $x + (" " * $Width) + $x 22 | } 23 | $position -f $Top++, $Left 24 | $m + ($q * $Width) + $j 25 | ) 26 | $Top -= [int]($height / 2) 27 | $Left += 8 28 | 29 | "`e7" # Save location 30 | -join @( 31 | $position -f $Top, $Left 32 | "Your Warning Here" 33 | ) 34 | "`e8`e[0m" # Restore Location 35 | Read-Host 36 | "`e[?1049l" -------------------------------------------------------------------------------- /Presentation/images/get-complement-bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/get-complement-bw.png -------------------------------------------------------------------------------- /Presentation/images/get-complement-high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/get-complement-high.png -------------------------------------------------------------------------------- /Presentation/images/get-complement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/get-complement.png -------------------------------------------------------------------------------- /Presentation/images/get-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/get-gradient.png -------------------------------------------------------------------------------- /Presentation/images/hello-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/hello-gradient.png -------------------------------------------------------------------------------- /Presentation/images/new-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/new-text.png -------------------------------------------------------------------------------- /Presentation/images/pansies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/pansies.jpg -------------------------------------------------------------------------------- /Presentation/images/terminal-theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/terminal-theme.png -------------------------------------------------------------------------------- /Presentation/images/write-host.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PoshCode/Pansies/45ff80a8f6b4ae52a4d7c36cbeb91ca93b8ed3d1/Presentation/images/write-host.png -------------------------------------------------------------------------------- /Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Pansies": { 4 | "commandName": "Executable", 5 | "executablePath": "C:\\Program Files\\PowerShell\\6.0.0.16\\powershell.exe", 6 | "commandLineArgs": "-noProfile" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PansiesPansies 2 | 3 | Powershell ANSI Escape Sequences 4 | 5 | This MIT Licensed cross-platform binary module contains classes and functions for doing ANSI colored output, named entities, and more in the console from .NET and PowerShell on platforms where they are supported: Windows 10, Linux, OS X, etc. 6 | 7 | ```posh 8 | PS>function prompt { "I $(Text '&redheart; ' -fg Red) PS> " } 9 | I ❤️ PS> 10 | ``` 11 | 12 | The goal of this project was to experiment with some classes and interfaces to address [PowerShell #2381](https://github.com/PowerShell/PowerShell/issues/2381) and give PowerShell full RGB support for Write-Host, but also provide full color support in format files, etc. Along the way, I've incorporated a whole library worth of color space theory to make comparing colors and generating gradients and complementary colors easy. 13 | 14 | ## Installing 15 | 16 | For terminal output, you require an ANSI-capable host like xTerm, wezterm, contour, ConEmu (Cmder), or Windows Terminal (or just PowerShell.exe) on Windows 10 or later. 17 | 18 | For PowerShell support, you need PowerShell 5.x or higher. You can [install PANSIES from the PowerShell Gallery](https://www.powershellgallery.com/packages/Pansies): 19 | 20 | ```posh 21 | Install-Module Pansies -AllowClobber 22 | ``` 23 | 24 | For .NET Projects, you can find PANSIES on NuGet. Install with: 25 | 26 | ```posh 27 | dotnet add reference PoshCode.Pansies 28 | ``` 29 | 30 | If you have troubles, please file [issues](https://github.com/PoshCode/Pansies/issues). 31 | 32 | ## Building from source 33 | 34 | First things first: there is a submodule being used (my [personally modified version](https://github.com/Jaykul/p2f) version of [beefarino/p2f](https://github.com/beefarino/p2f)), so you need to `git clone --recursive` or run `git submodule update --init --recursive` after cloning. You will also occasionally need to update it with `git submodule update --init --recursive`. 35 | 36 | The easiest, fastest build uses [earthly](https://docs.earthly.dev/). Earthly builds use containers, so on Windows it requires WSL2, Docker Desktop, and then the earthly CLI. If you already have those, you can just run `earthly +build` to build the module. 37 | 38 | ### Building without earthly 39 | 40 | Compiling Pansies requires the [.NET SDK](https://dotnet.microsoft.com/en-us/download), and building the module additionally requires [Invoke-Build](https://github.com/nightroman/Invoke-Build), [ModuleBuilder](https://github.com/PoshCode/ModuleBuilder), and my [Configuration](http://github.com/PoshCode/Configuration) and [Metadata](https://github.com/PoshCode/Metadata) modules. Once you have `dotnet`, you can install all of the PowerShell dependencies with: 41 | 42 | ```PowerShell 43 | Install-Script Install-RequiredModule 44 | Install-RequiredModule 45 | ``` 46 | 47 | With those dependencies installed and on your path, you can just run `Invoke-Build`. 48 | 49 | ### Currently Pansies provides six commands 50 | 51 | Cmdlet | Description 52 | ------ | ----------- 53 | New-Text | Creates a `Text` object. Provides parameters for `BackgroundColor` and `ForegroundColor` properties, that renders in console 54 | New-Hyperlink | Takes a Uri and optional text and writes [a hyperlink](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#file-hyperlinks_in_terminal_emulators-md) supported by most terminals 55 | Write-Host | Writes to host just like Write-Host, but with full RGBColor support and a -PersistentColor option 56 | Get-Gradient | Get a range of colors between two colors 57 | Get-ColorWheel | Like Get-Gradient, but allows you to specify the Hue step and by default adjusts the brightness so you don't get exact color repeatition 58 | Get-Complement | Get the Hue complement of a color 59 | 60 | One key feature is that `New-Text` and `Write-Host` both support [HTML named entities](https://www.w3schools.com/charsets/ref_html_entities_4.asp) like `♥` and `½` or `ü`, and numerical unicode character entities in both decimal (e.g. `Ξ`) and hexadeximal (`Ξ`), so you can easily embed characters, and even color them, so to write out "I ♥ PS" with a red heart you can just: 61 | 62 | ```posh 63 | "I $(Text "♥" -Fg Red) PS" 64 | ``` 65 | 66 | ### Pansies also provides a couple of important classes: 67 | 68 | *RgbColor* is a powerful representation of RGB colors with support for parsing CSS style color strings "#RRGGBB" and XTerm indexes, as well as handling the ConsoleColor values PowerShell users are used to. In addition to that, it has conversions to other color spaces for the purpose of doing color math like generating palettes and gradients, etc. The `ToString()` implementation shows the properties, but there is an overload which takes a boolean for Background or Foreground and renders to ANSI escape sequences. It has built-in palette for XTerm, and a built-in ConsoleColor palette which (on Windows) sniffs the current console's current color configuration. It uses these palettes to automatically downsample RGB colors to the nearest match when it's necessary to render in those color spaces. 69 | 70 | *Text* is a text class which contains BackgroundColor and ForegroundColor properties and a `ToString()` implementation based on VT escape sequences. It also supports HTML named enties like the `♥` example above. 71 | 72 | There are also *Palette* classes which support the XTerm 256 color palette and the default ConsoleColor 16 color palette (which currently supports loading the actual palette of the console in Windows, but may _therefore_ break off of Windows), with the ability to find the closest match to any RgbColor. 73 | 74 | You can play with setting `[PoshCode.Pansies.RgbColor]::ColorMode` to change how the colors are down-sampled, and modify the actual palettes in `[PoshCode.Pansies.RgbColor]::ConsolePalette` and `[PoshCode.Pansies.RgbColor]::XTermPalette` 75 | 76 | 77 | ## Contribute 78 | 79 | The end goal for this project is for the Color and Text classes (possibly without the color space conversions) to make it into the core PowerShell product, so what I'm most interested in here is [any ideas](https://github.com/PoshCode/Pansies/issues) people have for a better user experience for writing text and partially colored text, as well as other ANSI Virtual Terminal escape sequences. 80 | 81 | For the sake of PowerShell 5, I intend to keep this module around, and features that don't belong in PowerShell core for awhile, and we'll even make _some_ attempt to support older versions of PowerShell for Windows (running in ConEmu with ANSI support, or just downsampling everything to ConsoleColors). 82 | -------------------------------------------------------------------------------- /RequiredModules.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Metadata = "[1.5.4, 2.0)" 3 | Configuration = "[1.6.0, 2.0)" 4 | Pester = "[5.3.0, 6.0)" 5 | ModuleBuilder = "[3.0.2, 4.0)" 6 | PSScriptAnalyzer = "[1.21.0,2.0)" 7 | InvokeBuild = "[5.10.4,6.0)" 8 | } 9 | -------------------------------------------------------------------------------- /ScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | Severity = @('Error', 'Warning') 3 | ExcludeRules = @('PSAvoidGlobalVars') 4 | } 5 | -------------------------------------------------------------------------------- /Source/Assembly/ColorMode.cs: -------------------------------------------------------------------------------- 1 | namespace PoshCode.Pansies 2 | { 3 | public enum ColorMode { 4 | Automatic, 5 | ConsoleColor, 6 | XTerm256, 7 | X11, 8 | Rgb24Bit 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/ColorSpace.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces.Comparisons; 2 | using System.Linq; 3 | 4 | namespace PoshCode.Pansies.ColorSpaces 5 | { 6 | public delegate double ComparisonAlgorithm(IColorSpace a, IColorSpace b); 7 | 8 | /// 9 | /// Defines the public methods for all color spaces 10 | /// 11 | public interface IColorSpace 12 | { 13 | /// 14 | /// Initialize settings from an Rgb object 15 | /// 16 | /// 17 | void Initialize(IRgb color); 18 | 19 | /// 20 | /// Convert the color space to Rgb, you should probably using the "To" method instead. Need to figure out a way to "hide" or otherwise remove this method from the public interface. 21 | /// 22 | /// 23 | IRgb ToRgb(); 24 | 25 | /// 26 | /// Convert any IColorSpace to any other IColorSpace. 27 | /// 28 | /// IColorSpace type to convert to 29 | /// 30 | T To() where T : IColorSpace, new(); 31 | 32 | /// 33 | /// Determine how close two IColorSpaces are to each other using a passed in algorithm 34 | /// 35 | /// Other IColorSpace to compare to 36 | /// Algorithm to use for comparison 37 | /// Distance in 3d space as double 38 | double Compare(IColorSpace compareToValue, IColorSpaceComparison comparer); 39 | 40 | /// 41 | /// Array of signifigant values in a consistent order. Useful for generic n-dimensional math. 42 | /// 43 | double[] Ordinals { get; set; } 44 | } 45 | 46 | /// 47 | /// Abstract ColorSpace class, defines the To method that converts between any IColorSpace. 48 | /// 49 | public abstract class ColorSpace : IColorSpace 50 | { 51 | public abstract void Initialize(IRgb color); 52 | public abstract IRgb ToRgb(); 53 | public abstract double[] Ordinals { get; set; } 54 | internal abstract string[] OrdinalLabels { get; } 55 | 56 | public override string ToString() 57 | { 58 | string[] fields = new string[Ordinals.Length]; 59 | 60 | for (int i = 0; i < Ordinals.Length; i++) 61 | { 62 | string value; 63 | if (Ordinals[i] >= 0 && Ordinals[i] <= 1) { 64 | value = Ordinals[i].ToString("N3"); 65 | } else { 66 | value = Ordinals[i].ToString("N0"); 67 | } 68 | fields[i] = $"{OrdinalLabels[i]}={value}"; 69 | } 70 | 71 | return string.Join("; ", fields); 72 | } 73 | 74 | /// 75 | /// Convienience method for comparing any IColorSpace 76 | /// 77 | /// 78 | /// 79 | /// Single number representing the difference between two colors 80 | public double Compare(IColorSpace compareToValue, IColorSpaceComparison comparer) 81 | { 82 | return comparer.Compare(this, compareToValue); 83 | } 84 | 85 | /// 86 | /// Convert any IColorSpace to any other IColorSpace 87 | /// 88 | /// Must implement IColorSpace, new() 89 | /// 90 | public T To() where T : IColorSpace, new() 91 | { 92 | if (typeof(T) == GetType()) 93 | { 94 | return (T)MemberwiseClone(); 95 | } 96 | 97 | var newColorSpace = new T(); 98 | newColorSpace.Initialize(ToRgb()); 99 | 100 | return newColorSpace; 101 | } 102 | 103 | 104 | public T[] GradientTo(T end, int size = 10) where T : IColorSpace, new() 105 | { 106 | T start = new T(); 107 | start.Initialize(ToRgb()); 108 | return Gradient.GetGradient(start, end, size).ToArray(); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/ColorSpaces.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostspecific="true" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ assembly name="System.Xml.dll" #> 4 | <#@ import namespace="System.Linq" #> 5 | <#@ import namespace="System.Text" #> 6 | <#@ import Namespace="System.Xml" #> 7 | <#@ import Namespace="System.IO" #> 8 | <#@ import namespace="System.Collections.Generic" #> 9 | <# 10 | var document = new XmlDocument(); 11 | var file = Host.ResolvePath("ColorSpaces.xml"); 12 | document.Load(file); 13 | var colorSpaces = document.SelectNodes("colorSpaces/colorSpace"); 14 | #> 15 | <#@ output extension=".cs" #>// Note: This is a generated file. 16 | using PoshCode.Pansies.ColorSpaces.Conversions; 17 | using System.Linq; 18 | 19 | namespace PoshCode.Pansies.ColorSpaces 20 | { 21 | public interface IHue 22 | { 23 | double H { get; set; } 24 | } 25 | <# foreach (XmlNode space in colorSpaces) { 26 | var spaceName = space.Attributes["name"].Value; 27 | var points = space.SelectNodes("dataPoints/dataPoint");#> 28 | public interface I<#=spaceName#> : IColorSpace 29 | { 30 | <# foreach(XmlNode point in points) { 31 | #> 32 | double <#=point.Attributes["label"].Value#> { get; set; } 33 | <# } #> 34 | } 35 | 36 | public class <#=spaceName#> : ColorSpace, I<#=spaceName#><# if(space.SelectNodes("dataPoints/dataPoint[@label='H']").Count > 0) { #>, IHue<# } #> 37 | { 38 | <# foreach(XmlNode point in points) { #> 39 | public double <#=point.Attributes["label"].Value#> { get; set; } 40 | <# } #> 41 | 42 | public <#=spaceName#>() { } 43 | 44 | public <#=spaceName#>(<# foreach(XmlNode point in points) { #>double <#=point.Attributes["label"].Value.ToLower()#><# if(point.Attributes != points[points.Count - 1].Attributes) { #>, <# } #><# } #>) 45 | { 46 | <# foreach(XmlNode point in points) { #> 47 | <#=point.Attributes["label"].Value#> = <#=point.Attributes["label"].Value.ToLower()#>; 48 | <# } #> 49 | } 50 | 51 | public <#=spaceName#>(IColorSpace color) 52 | { 53 | Ordinals = color.To<<#=spaceName#>>().Ordinals; 54 | } 55 | 56 | public <#=spaceName#>(double[] ordinals) 57 | { 58 | Ordinals = ordinals; 59 | } 60 | 61 | public override void Initialize(IRgb color) 62 | { 63 | <#=spaceName#>Converter.ToColorSpace(color,this); 64 | } 65 | 66 | public override string ToString() 67 | { 68 | return string.Join("; ", new []{ 69 | <# foreach(XmlNode point in points) { #> 70 | "<#=point.Attributes["label"].Value#>=" + <#=point.Attributes["label"].Value#>, 71 | <# } #> 72 | }); 73 | } 74 | 75 | public override IRgb ToRgb() 76 | { 77 | return <#=spaceName#>Converter.ToColor(this); 78 | } 79 | 80 | public <#=spaceName#>[] Rainbow(int size = 7) 81 | { 82 | return Gradient.GetRainbow(this, size).ToArray(); 83 | } 84 | 85 | internal override string[] OrdinalLabels { get; } = new[] { <# foreach(XmlNode point in points) { #>"<#=point.Attributes["label"].Value#>",<# } #> }; 86 | 87 | public override sealed double[] Ordinals 88 | { 89 | get 90 | { 91 | return new[] {<# foreach(XmlNode point in points) { #> <#=point.Attributes["label"].Value#>,<# } #> }; 92 | } 93 | set 94 | { 95 | <# var counter = 0; 96 | foreach(XmlNode point in points) { #> 97 | <#=point.Attributes["label"].Value#> = value[<#=counter++#>]; 98 | <# } #> 99 | } 100 | } 101 | } 102 | <# } #>} -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/ColorSpaces.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Comparisons/Cie1976Comparison.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Comparisons 4 | { 5 | /// 6 | /// Implements the CIE76 method of delta-e: http://en.wikipedia.org/wiki/Color_difference#CIE76 7 | /// 8 | public class Cie1976Comparison : IColorSpaceComparison 9 | { 10 | /// 11 | /// Calculates the CIE76 delta-e value: http://en.wikipedia.org/wiki/Color_difference#CIE76 12 | /// 13 | public double Compare(IColorSpace colorA, IColorSpace colorB) 14 | { 15 | var a = colorA.To(); 16 | var b = colorB.To(); 17 | 18 | var differences = Distance(a.L, b.L) + Distance(a.A, b.A) + Distance(a.B, b.B); 19 | return Math.Sqrt(differences); 20 | } 21 | 22 | private static double Distance(double a, double b) 23 | { 24 | return (a - b) * (a - b); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Comparisons/Cie94Comparison.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Comparisons 4 | { 5 | /// 6 | /// Implements the Cie94 method of delta-e: http://en.wikipedia.org/wiki/Color_difference#CIE94 7 | /// 8 | public class Cie94Comparison : IColorSpaceComparison 9 | { 10 | /// 11 | /// Application type defines constants used in the Cie94 comparison 12 | /// 13 | public enum Application 14 | { 15 | GraphicArts, 16 | Textiles 17 | } 18 | 19 | 20 | internal ApplicationConstants Constants { get; private set; } 21 | 22 | /// 23 | /// Create new Cie94Comparison. Defaults to GraphicArts application type. 24 | /// 25 | public Cie94Comparison() 26 | { 27 | Constants = new ApplicationConstants(Application.GraphicArts); 28 | } 29 | 30 | /// 31 | /// Create new Cie94Comparison for specific application type. 32 | /// 33 | /// 34 | public Cie94Comparison(Application application) 35 | { 36 | Constants = new ApplicationConstants(application); 37 | } 38 | 39 | /// 40 | /// Compare colors using the Cie94 algorithm. The first color (a) will be used as the reference color. 41 | /// 42 | /// Reference color 43 | /// Comparison color 44 | /// 45 | public double Compare(IColorSpace a, IColorSpace b) 46 | { 47 | var labA = a.To(); 48 | var labB = b.To(); 49 | 50 | var deltaL = labA.L - labB.L; 51 | var deltaA = labA.A - labB.A; 52 | var deltaB = labA.B - labB.B; 53 | 54 | var c1 = Math.Sqrt(labA.A * labA.A + labA.B * labA.B); 55 | var c2 = Math.Sqrt(labB.A * labB.A + labB.B * labB.B); 56 | var deltaC = c1 - c2; 57 | 58 | var deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC; 59 | deltaH = deltaH < 0 ? 0 : Math.Sqrt(deltaH); 60 | 61 | const double sl = 1.0; 62 | const double kc = 1.0; 63 | const double kh = 1.0; 64 | 65 | var sc = 1.0 + Constants.K1 * c1; 66 | var sh = 1.0 + Constants.K2 * c1; 67 | 68 | var deltaLKlsl = deltaL / (Constants.Kl * sl); 69 | var deltaCkcsc = deltaC / (kc * sc); 70 | var deltaHkhsh = deltaH / (kh * sh); 71 | var i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh; 72 | return i < 0 ? 0 : Math.Sqrt(i); 73 | } 74 | 75 | internal class ApplicationConstants 76 | { 77 | internal double Kl { get; private set; } 78 | internal double K1 { get; private set; } 79 | internal double K2 { get; private set; } 80 | 81 | public ApplicationConstants(Application application) 82 | { 83 | switch (application) 84 | { 85 | case Application.GraphicArts: 86 | Kl = 1.0; 87 | K1 = .045; 88 | K2 = .015; 89 | break; 90 | case Application.Textiles: 91 | Kl = 2.0; 92 | K1 = .048; 93 | K2 = .014; 94 | break; 95 | } 96 | } 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Comparisons/CieDe2000Comparison.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Comparisons 4 | { 5 | /// 6 | /// Implements the DE2000 method of delta-e: http://en.wikipedia.org/wiki/Color_difference#CIEDE2000 7 | /// Correct implementation provided courtesy of Jonathan Hofinger, jaytar42 8 | /// 9 | public class CieDe2000Comparison : IColorSpaceComparison 10 | { 11 | /// 12 | /// Calculates the DE2000 delta-e value: http://en.wikipedia.org/wiki/Color_difference#CIEDE2000 13 | /// Correct implementation provided courtesy of Jonathan Hofinger, jaytar42 14 | /// 15 | public double Compare(IColorSpace c1, IColorSpace c2) 16 | { 17 | //Set weighting factors to 1 18 | double k_L = 1.0d; 19 | double k_C = 1.0d; 20 | double k_H = 1.0d; 21 | 22 | 23 | //Change Color Space to L*a*b: 24 | Lab lab1 = c1.To(); 25 | Lab lab2 = c2.To(); 26 | 27 | //Calculate Cprime1, Cprime2, Cabbar 28 | double c_star_1_ab = Math.Sqrt(lab1.A * lab1.A + lab1.B * lab1.B); 29 | double c_star_2_ab = Math.Sqrt(lab2.A * lab2.A + lab2.B * lab2.B); 30 | double c_star_average_ab = (c_star_1_ab + c_star_2_ab) / 2; 31 | 32 | double c_star_average_ab_pot7 = c_star_average_ab * c_star_average_ab * c_star_average_ab; 33 | c_star_average_ab_pot7 *= c_star_average_ab_pot7 * c_star_average_ab; 34 | 35 | double G = 0.5d * (1 - Math.Sqrt(c_star_average_ab_pot7 / (c_star_average_ab_pot7 + 6103515625))); //25^7 36 | double a1_prime = (1 + G) * lab1.A; 37 | double a2_prime = (1 + G) * lab2.A; 38 | 39 | double C_prime_1 = Math.Sqrt(a1_prime * a1_prime + lab1.B * lab1.B); 40 | double C_prime_2 = Math.Sqrt(a2_prime * a2_prime + lab2.B * lab2.B); 41 | //Angles in Degree. 42 | double h_prime_1 = ((Math.Atan2(lab1.B, a1_prime) * 180d / Math.PI) + 360) % 360d; 43 | double h_prime_2 = ((Math.Atan2(lab2.B, a2_prime) * 180d / Math.PI) + 360) % 360d; 44 | 45 | double delta_L_prime = lab2.L - lab1.L; 46 | double delta_C_prime = C_prime_2 - C_prime_1; 47 | 48 | double h_bar = Math.Abs(h_prime_1 - h_prime_2); 49 | double delta_h_prime; 50 | if (C_prime_1 * C_prime_2 == 0) delta_h_prime = 0; 51 | else 52 | { 53 | if (h_bar <= 180d) 54 | { 55 | delta_h_prime = h_prime_2 - h_prime_1; 56 | } 57 | else if (h_bar > 180d && h_prime_2 <= h_prime_1) 58 | { 59 | delta_h_prime = h_prime_2 - h_prime_1 + 360.0; 60 | } 61 | else 62 | { 63 | delta_h_prime = h_prime_2 - h_prime_1 - 360.0; 64 | } 65 | } 66 | double delta_H_prime = 2 * Math.Sqrt(C_prime_1 * C_prime_2) * Math.Sin(delta_h_prime * Math.PI / 360d); 67 | 68 | // Calculate CIEDE2000 69 | double L_prime_average = (lab1.L + lab2.L) / 2d; 70 | double C_prime_average = (C_prime_1 + C_prime_2) / 2d; 71 | 72 | //Calculate h_prime_average 73 | 74 | double h_prime_average; 75 | if (C_prime_1 * C_prime_2 == 0) h_prime_average = 0; 76 | else 77 | { 78 | if (h_bar <= 180d) 79 | { 80 | h_prime_average = (h_prime_1 + h_prime_2) / 2; 81 | } 82 | else if (h_bar > 180d && (h_prime_1 + h_prime_2) < 360d) 83 | { 84 | h_prime_average = (h_prime_1 + h_prime_2 + 360d) / 2; 85 | } 86 | else 87 | { 88 | h_prime_average = (h_prime_1 + h_prime_2 - 360d) / 2; 89 | } 90 | } 91 | double L_prime_average_minus_50_square = (L_prime_average - 50); 92 | L_prime_average_minus_50_square *= L_prime_average_minus_50_square; 93 | 94 | double S_L = 1 + ((.015d * L_prime_average_minus_50_square) / Math.Sqrt(20 + L_prime_average_minus_50_square)); 95 | double S_C = 1 + .045d * C_prime_average; 96 | double T = 1 97 | - .17 * Math.Cos(DegToRad(h_prime_average - 30)) 98 | + .24 * Math.Cos(DegToRad(h_prime_average * 2)) 99 | + .32 * Math.Cos(DegToRad(h_prime_average * 3 + 6)) 100 | - .2 * Math.Cos(DegToRad(h_prime_average * 4 - 63)); 101 | double S_H = 1 + .015 * T * C_prime_average; 102 | double h_prime_average_minus_275_div_25_square = (h_prime_average - 275) / (25); 103 | h_prime_average_minus_275_div_25_square *= h_prime_average_minus_275_div_25_square; 104 | double delta_theta = 30 * Math.Exp(-h_prime_average_minus_275_div_25_square); 105 | 106 | double C_prime_average_pot_7 = C_prime_average * C_prime_average * C_prime_average; 107 | C_prime_average_pot_7 *= C_prime_average_pot_7 * C_prime_average; 108 | double R_C = 2 * Math.Sqrt(C_prime_average_pot_7 / (C_prime_average_pot_7 + 6103515625)); 109 | 110 | double R_T = -Math.Sin(DegToRad(2 * delta_theta)) * R_C; 111 | 112 | double delta_L_prime_div_k_L_S_L = delta_L_prime / (S_L * k_L); 113 | double delta_C_prime_div_k_C_S_C = delta_C_prime / (S_C * k_C); 114 | double delta_H_prime_div_k_H_S_H = delta_H_prime / (S_H * k_H); 115 | 116 | double CIEDE2000 = Math.Sqrt( 117 | delta_L_prime_div_k_L_S_L * delta_L_prime_div_k_L_S_L 118 | + delta_C_prime_div_k_C_S_C * delta_C_prime_div_k_C_S_C 119 | + delta_H_prime_div_k_H_S_H * delta_H_prime_div_k_H_S_H 120 | + R_T * delta_C_prime_div_k_C_S_C * delta_H_prime_div_k_H_S_H 121 | ); 122 | 123 | return CIEDE2000; 124 | } 125 | private double DegToRad(double degrees) 126 | { 127 | return degrees * Math.PI / 180; 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Comparisons/CmcComparison.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Comparisons 4 | { 5 | /// 6 | /// Implements the CMC l:c (1984) method of delta-e: http://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29 7 | /// 8 | public class CmcComparison : IColorSpaceComparison 9 | { 10 | public const double DefaultLightness = 2.0; 11 | public const double DefaultChroma = 1.0; 12 | 13 | private readonly double _lightness; 14 | private readonly double _chroma; 15 | 16 | /// 17 | /// Create CMC l:c comparison with DefaultLightness and DefaultChroma values. 18 | /// 19 | public CmcComparison() 20 | { 21 | _lightness = DefaultLightness; 22 | _chroma = DefaultChroma; 23 | } 24 | 25 | /// 26 | /// Create CMC l:c comparison with specific lightness (l) and chroma (c) values. 27 | /// 28 | /// 29 | /// 30 | public CmcComparison(double lightness = DefaultLightness, double chroma = DefaultChroma) 31 | { 32 | _lightness = lightness; 33 | _chroma = chroma; 34 | } 35 | 36 | /// 37 | /// Calculates the CMC l:c (1984) delta-e value: http://en.wikipedia.org/wiki/Color_difference#CMC_l:c_.281984.29 38 | /// 39 | /// 40 | /// 41 | /// 42 | public double Compare(IColorSpace colorA, IColorSpace colorB) 43 | { 44 | var aLab = colorA.To(); 45 | var bLab = colorB.To(); 46 | 47 | var deltaL = aLab.L - bLab.L; 48 | var h = Math.Atan2(aLab.B, aLab.A); 49 | 50 | var c1 = Math.Sqrt(aLab.A * aLab.A + aLab.B * aLab.B); 51 | var c2 = Math.Sqrt(bLab.A * bLab.A + bLab.B * bLab.B); 52 | var deltaC = c1 - c2; 53 | 54 | var deltaH = Math.Sqrt( 55 | (aLab.A - bLab.A) * (aLab.A - bLab.A) + 56 | (aLab.B - bLab.B) * (aLab.B - bLab.B) - 57 | deltaC * deltaC); 58 | 59 | var c1_4 = c1 * c1; 60 | c1_4 *= c1_4; 61 | var t = 164 <= h && h <= 345 62 | ? .56 + Math.Abs(.2 * Math.Cos(h + 168.0)) 63 | : .36 + Math.Abs(.4 * Math.Cos(h + 35.0)); 64 | var f = Math.Sqrt(c1_4 / (c1_4 + 1900.0)); 65 | 66 | var sL = aLab.L < 16 ? .511 : (.040975 * aLab.L) / (1.0 + .01765 * aLab.L); 67 | var sC = (.0638 * c1) / (1 + .0131 * c1) + .638; 68 | var sH = sC * (f * t + 1 - f); 69 | 70 | var differences = DistanceDivided(deltaL, _lightness * sL) + 71 | DistanceDivided(deltaC, _chroma * sC) + 72 | DistanceDivided(deltaH, sH); 73 | 74 | return Math.Sqrt(differences); 75 | } 76 | 77 | private static double DistanceDivided(double a, double dividend) 78 | { 79 | var adiv = a / dividend; 80 | return adiv * adiv; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Comparisons/IColorSpaceComparison.cs: -------------------------------------------------------------------------------- 1 | namespace PoshCode.Pansies.ColorSpaces.Comparisons 2 | { 3 | /// 4 | /// Defines how comparison methods may be called 5 | /// 6 | public interface IColorSpaceComparison 7 | { 8 | /// 9 | /// Returns the difference between two colors given based on the specified defined in the called class. 10 | /// 11 | /// 12 | /// 13 | /// Score based on similarity, the lower the score the closer the colors 14 | double Compare(IColorSpace a, IColorSpace b); 15 | } 16 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/CmyConverter.cs: -------------------------------------------------------------------------------- 1 | namespace PoshCode.Pansies.ColorSpaces.Conversions 2 | { 3 | internal static class CmyConverter 4 | { 5 | internal static void ToColorSpace(IRgb color, ICmy item) 6 | { 7 | item.C = 1 - color.R / 255.0; 8 | item.M = 1 - color.G / 255.0; 9 | item.Y = 1 - color.B / 255.0; 10 | } 11 | 12 | internal static IRgb ToColor(ICmy item) 13 | { 14 | return new Rgb 15 | { 16 | R = (1 - item.C) * 255.0, 17 | G = (1 - item.M) * 255.0, 18 | B = (1 - item.Y) * 255.0 19 | }; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/CmykConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using PoshCode.Pansies.ColorSpaces.Conversions.Utility; 3 | 4 | namespace PoshCode.Pansies.ColorSpaces.Conversions 5 | { 6 | public static class CmykConverter 7 | { 8 | public static void ToColorSpace(IRgb color, ICmyk item) 9 | { 10 | var cmy = new Cmy(); 11 | cmy.Initialize(color); 12 | 13 | var k = 1.0; 14 | if (cmy.C < k) 15 | k = cmy.C; 16 | if (cmy.M < k) 17 | k = cmy.M; 18 | if (cmy.Y < k) 19 | k = cmy.Y; 20 | item.K = k; 21 | 22 | if (k.BasicallyEqualTo(1)) 23 | { 24 | item.C = 0; 25 | item.M = 0; 26 | item.Y = 0; 27 | } 28 | else 29 | { 30 | item.C = (cmy.C - k) / (1 - k); 31 | item.M = (cmy.M - k) / (1 - k); 32 | item.Y = (cmy.Y - k) / (1 - k); 33 | } 34 | } 35 | 36 | public static IRgb ToColor(ICmyk item) 37 | { 38 | var cmy = new Cmy 39 | { 40 | C = (item.C * (1 - item.K) + item.K), 41 | M = (item.M * (1 - item.K) + item.K), 42 | Y = (item.Y * (1 - item.K) + item.K) 43 | }; 44 | 45 | return cmy.ToRgb(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/HsbConverter.cs: -------------------------------------------------------------------------------- 1 | namespace PoshCode.Pansies.ColorSpaces.Conversions 2 | { 3 | /// 4 | /// HSB is another name for HSV 5 | /// 6 | internal static class HsbConverter 7 | { 8 | internal static void ToColorSpace(IRgb color, IHsb item) 9 | { 10 | var hsv = new Hsv(); 11 | HsvConverter.ToColorSpace(color, hsv); 12 | 13 | item.H = hsv.H; 14 | item.S = hsv.S; 15 | item.B = hsv.V; 16 | } 17 | 18 | internal static IRgb ToColor(IHsb item) 19 | { 20 | var hsv = new Hsv 21 | { 22 | H = item.H, 23 | S = item.S, 24 | V = item.B 25 | }; 26 | return HsvConverter.ToColor(hsv); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/HslConverter.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces.Conversions.Utility; 2 | using System; 3 | using static System.Math; 4 | 5 | namespace PoshCode.Pansies.ColorSpaces.Conversions 6 | { 7 | internal static class HslConverter 8 | { 9 | public static IHsl ToColorSpace(double R, double G, double B) 10 | { 11 | var min = Min(R, Min(G, B)); //Min. value of RGB 12 | var max = Max(R, Max(G, B)); //Max. value of RGB 13 | var chroma = max - min; 14 | 15 | double H = 0, S = 0, L = 0; 16 | L = (max + min) / 2; 17 | 18 | if (chroma == 0) // This is a gray, no chroma... 19 | { 20 | H = 0; 21 | S = 0; 22 | } 23 | else 24 | { 25 | if (L <= 0.5) 26 | { 27 | S = chroma / (2 * L); //max / (max + min); 28 | } 29 | else 30 | { 31 | S = chroma / (2 - 2 * L); // max / (2 - max - min); 32 | } 33 | 34 | if (R == max) 35 | { 36 | H = (G - B) / chroma; 37 | if (G < B) 38 | { 39 | H += 6; 40 | } 41 | } 42 | else if (B == max) 43 | { 44 | H = 4 + ((R - G) / chroma); 45 | } 46 | else if (G == max) 47 | { 48 | H = 2 + ((B - R) / chroma); 49 | } 50 | 51 | 52 | H *= 60; 53 | } 54 | return new Hsl(H, S, L); 55 | } 56 | 57 | internal static void ToColorSpace(IRgb color, IHsl item) 58 | { 59 | var result = ToColorSpace(color.R / 255d, color.G / 255d, color.B / 255d); 60 | item.H = result.H; 61 | item.S = result.S; 62 | item.L = result.L; 63 | 64 | // Range expected by HSL is integer 65 | item.S = Round(item.S * 100, 3); 66 | item.L = Round(item.L * 100, 3); 67 | } 68 | 69 | private static IRgb Rotate(double h, double s, ref double l) 70 | { 71 | // Avoid exactly zero hue, it does weird things 72 | if(h==0) { h = 0.00001; } 73 | var chroma = (1 - Abs(2 * l - 1)) * s; 74 | var x = chroma * (1 - Abs((h % 2d) - 1)); 75 | l -= 0.5 * chroma; 76 | 77 | switch (Ceiling(h)) 78 | { 79 | case 1d: 80 | return new Rgb(chroma, x, 0); 81 | case 2d: 82 | return new Rgb(x, chroma, 0); 83 | case 3d: 84 | return new Rgb(0, chroma, x); 85 | case 4d: 86 | return new Rgb(0, x, chroma); 87 | case 5d: 88 | return new Rgb(x, 0, chroma); 89 | case 6d: 90 | return new Rgb(chroma, 0, x); 91 | default: 92 | return new Rgb(0, 0, 0); 93 | } 94 | } 95 | 96 | internal static IRgb ToColor(IHsl item) 97 | { 98 | var h = item.H / 60.0; 99 | var s = item.S / 100.0; 100 | var l = item.L / 100.0; 101 | if (s > 0) 102 | { 103 | var result = Rotate(h, s, ref l); 104 | 105 | return new Rgb 106 | { 107 | R = (result.R + l) * 255, 108 | G = (result.G + l) * 255, 109 | B = (result.B + l) * 255 110 | }; 111 | } 112 | else 113 | { 114 | return new Rgb 115 | { 116 | R = l * 255, 117 | G = l * 255, 118 | B = l * 255 119 | }; 120 | } 121 | } 122 | 123 | private static double GetColorComponent(double temp1, double temp2, double temp3) 124 | { 125 | temp3 = MoveIntoRange(temp3); 126 | if (temp3 < 1.0 / 6.0) 127 | { 128 | return temp1 + (temp2 - temp1) * 6.0 * temp3; 129 | } 130 | 131 | if (temp3 < 0.5) 132 | { 133 | return temp2; 134 | } 135 | 136 | if (temp3 < 2.0 / 3.0) 137 | { 138 | return temp1 + ((temp2 - temp1) * ((2.0 / 3.0) - temp3) * 6.0); 139 | } 140 | 141 | return temp1; 142 | } 143 | 144 | private static double MoveIntoRange(double temp3) 145 | { 146 | if (temp3 < 0.0) return temp3 + 1.0; 147 | if (temp3 > 1.0) return temp3 - 1.0; 148 | return temp3; 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/HsvConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static System.Math; 3 | 4 | namespace PoshCode.Pansies.ColorSpaces.Conversions 5 | { 6 | internal static class HsvConverter 7 | { 8 | public static IHsv ToColorSpace(double R, double G, double B) 9 | { 10 | var min = Min(R, Min(G, B)); //Min. value of RGB 11 | var max = Max(R, Max(G, B)); //Max. value of RGB 12 | var chroma = max - min; 13 | 14 | double H = 0, S = 0, V = 0; 15 | V = max; 16 | 17 | if (chroma == 0) // This is a gray, no chroma... 18 | { 19 | H = 0; 20 | S = 0; 21 | } 22 | else 23 | { 24 | S = chroma / max; 25 | 26 | if (R == max) 27 | { 28 | H = (G - B) / chroma; 29 | if (G < B) 30 | { 31 | H += 6; 32 | } 33 | } 34 | else if (G == max) 35 | { 36 | H = 2 + ((B - R) / chroma); 37 | } 38 | else if (B == max) 39 | { 40 | H = 4 + ((R - G) / chroma); 41 | } 42 | 43 | H *= 60; 44 | } 45 | return new Hsv(H, S, V); 46 | } 47 | 48 | internal static void ToColorSpace(IRgb color, IHsv item) 49 | { 50 | var result = ToColorSpace(color.R / 255d, color.G / 255d, color.B / 255d); 51 | item.H = result.H; 52 | item.S = result.S; 53 | item.V = result.V; 54 | 55 | //item.H = Color.FromArgb(255, (int)color.R, (int)color.G, (int)color.B).GetHue(); 56 | //item.S = (max <= 0) ? 0 : 1d - (1d * min / max); 57 | //item.V = max / 255d; 58 | } 59 | 60 | internal static IRgb ToColor(IHsv item) 61 | { 62 | var range = Convert.ToInt32(Math.Floor(item.H / 60.0)) % 6; 63 | var f = item.H / 60.0 - Math.Floor(item.H / 60.0); 64 | 65 | var v = item.V * 255.0; 66 | var p = v * (1 - item.S); 67 | var q = v * (1 - f * item.S); 68 | var t = v * (1 - (1 - f) * item.S); 69 | 70 | switch (range) 71 | { 72 | case 0: 73 | return NewRgb(v, t, p); 74 | case 1: 75 | return NewRgb(q, v, p); 76 | case 2: 77 | return NewRgb(p, v, t); 78 | case 3: 79 | return NewRgb(p, q, v); 80 | case 4: 81 | return NewRgb(t, p, v); 82 | } 83 | return NewRgb(v, p, q); 84 | } 85 | 86 | private static IRgb NewRgb(double r, double g, double b) 87 | { 88 | return new Rgb { R = r, G = g, B = b }; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/HunterLabConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions 4 | { 5 | internal static class HunterLabConverter 6 | { 7 | 8 | internal static void ToColorSpace(IRgb color, IHunterLab item) 9 | { 10 | var xyz = color.To(); 11 | 12 | item.L = 10.0 * Math.Sqrt(xyz.Y); 13 | item.A = xyz.Y != 0 ? 17.5 * (((1.02 * xyz.X) - xyz.Y) / Math.Sqrt(xyz.Y)) : 0; 14 | item.B = xyz.Y != 0 ? 7.0 * ((xyz.Y - (.847 * xyz.Z)) / Math.Sqrt(xyz.Y)) : 0; 15 | } 16 | 17 | internal static IRgb ToColor(IHunterLab item) 18 | { 19 | var x = (item.A / 17.5) * (item.L / 10.0); 20 | var itemL_10 = item.L / 10.0; 21 | var y = itemL_10 * itemL_10; 22 | var z = item.B / 7.0 * item.L / 10.0; 23 | 24 | var xyz = new Xyz 25 | { 26 | X = (x + y) / 1.02, 27 | Y = y, 28 | Z = -(z - y) / .847 29 | }; 30 | return xyz.To(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/LabConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions 4 | { 5 | internal static class LabConverter 6 | { 7 | internal static void ToColorSpace(IRgb color, ILab item) 8 | { 9 | var xyz = new Xyz(); 10 | xyz.Initialize(color); 11 | 12 | var white = XyzConverter.WhiteReference; 13 | var x = PivotXyz(xyz.X / white.X); 14 | var y = PivotXyz(xyz.Y / white.Y); 15 | var z = PivotXyz(xyz.Z / white.Z); 16 | 17 | item.L = Math.Max(0, 116 * y - 16); 18 | item.A = 500 * (x - y); 19 | item.B = 200 * (y - z); 20 | } 21 | 22 | internal static IRgb ToColor(ILab item) 23 | { 24 | var y = (item.L + 16.0) / 116.0; 25 | var x = item.A / 500.0 + y; 26 | var z = y - item.B / 200.0; 27 | 28 | var white = XyzConverter.WhiteReference; 29 | var x3 = x * x * x; 30 | var z3 = z * z * z; 31 | var xyz = new Xyz 32 | { 33 | X = white.X * (x3 > XyzConverter.Epsilon ? x3 : (x - 16.0 / 116.0) / 7.787), 34 | Y = white.Y * (item.L > (XyzConverter.Kappa * XyzConverter.Epsilon) ? Math.Pow(((item.L + 16.0) / 116.0), 3) : item.L / XyzConverter.Kappa), 35 | Z = white.Z * (z3 > XyzConverter.Epsilon ? z3 : (z - 16.0 / 116.0) / 7.787) 36 | }; 37 | 38 | return xyz.ToRgb(); 39 | } 40 | 41 | private static double PivotXyz(double n) 42 | { 43 | return n > XyzConverter.Epsilon ? CubicRoot(n) : (XyzConverter.Kappa * n + 16) / 116; 44 | } 45 | 46 | private static double CubicRoot(double n) 47 | { 48 | return Math.Pow(n, 1.0 / 3.0); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/LchConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions 4 | { 5 | internal static class LchConverter 6 | { 7 | internal static void ToColorSpace(IRgb color, ILch item) 8 | { 9 | var lab = color.To(); 10 | var h = Math.Atan2(lab.B, lab.A); 11 | 12 | // convert from radians to degrees 13 | if (h > 0) 14 | { 15 | h = (h / Math.PI) * 180.0; 16 | } 17 | else 18 | { 19 | h = 360 - (Math.Abs(h) / Math.PI) * 180.0; 20 | } 21 | 22 | if (h < 0) 23 | { 24 | h += 360.0; 25 | } 26 | else if (h >= 360) 27 | { 28 | h -= 360.0; 29 | } 30 | 31 | item.L = lab.L; 32 | item.C = Math.Sqrt(lab.A * lab.A + lab.B * lab.B); 33 | item.H = h; 34 | } 35 | 36 | internal static IRgb ToColor(ILch item) 37 | { 38 | var hRadians = item.H * Math.PI / 180.0; 39 | var lab = new Lab 40 | { 41 | L = item.L, 42 | A = Math.Cos(hRadians) * item.C, 43 | B = Math.Sin(hRadians) * item.C 44 | }; 45 | return lab.To(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/LuvConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions 4 | { 5 | internal static class LuvConverter 6 | { 7 | internal static void ToColorSpace(IRgb color, ILuv item) 8 | { 9 | var xyz = new Xyz(); 10 | var white = XyzConverter.WhiteReference; 11 | xyz.Initialize(color); 12 | 13 | var y = xyz.Y / XyzConverter.WhiteReference.Y; 14 | item.L = y > XyzConverter.Epsilon ? 116.0 * XyzConverter.CubicRoot(y) - 16.0 : XyzConverter.Kappa * y; 15 | 16 | var targetDenominator = GetDenominator(xyz); 17 | var referenceDenominator = GetDenominator(white); 18 | // ReSharper disable CompareOfFloatsByEqualityOperator 19 | var xTarget = targetDenominator == 0 ? 0 : ((4.0 * xyz.X / targetDenominator) - (4.0 * white.X / referenceDenominator)); 20 | var yTarget = targetDenominator == 0 ? 0 : ((9.0 * xyz.Y / targetDenominator) - (9.0 * white.Y / referenceDenominator)); 21 | // ReSharper restore CompareOfFloatsByEqualityOperator 22 | 23 | item.U = 13.0 * item.L * xTarget; 24 | item.V = 13.0 * item.L * yTarget; 25 | } 26 | 27 | internal static IRgb ToColor(ILuv item) 28 | { 29 | var white = XyzConverter.WhiteReference; 30 | const double c = -1.0 / 3.0; 31 | var uPrime = (4.0 * white.X) / GetDenominator(white); 32 | var vPrime = (9.0 * white.Y) / GetDenominator(white); 33 | var a = (1.0 / 3.0) * ((52.0 * item.L) / (item.U + 13 * item.L * uPrime) - 1.0); 34 | var imteL_16_116 = (item.L + 16.0) / 116.0; 35 | var y = item.L > XyzConverter.Kappa * XyzConverter.Epsilon 36 | ? imteL_16_116 * imteL_16_116 * imteL_16_116 37 | : item.L / XyzConverter.Kappa; 38 | var b = -5.0 * y; 39 | var d = y * ((39.0 * item.L) / (item.V + 13.0 * item.L * vPrime) - 5.0); 40 | var x = (d - b) / (a - c); 41 | var z = x * a + b; 42 | var xyz = new Xyz 43 | { 44 | X = 100 * x, 45 | Y = 100 * y, 46 | Z = 100 * z 47 | }; 48 | return xyz.ToRgb(); 49 | 50 | } 51 | 52 | private static double GetDenominator(IXyz xyz) 53 | { 54 | return xyz.X + 15.0 * xyz.Y + 3.0 * xyz.Z; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/RgbConverter.cs: -------------------------------------------------------------------------------- 1 | namespace PoshCode.Pansies.ColorSpaces.Conversions 2 | { 3 | internal static class RgbConverter 4 | { 5 | internal static void ToColorSpace(IRgb color, IRgb item) 6 | { 7 | item.R = color.R; 8 | item.G = color.G; 9 | item.B = color.B; 10 | } 11 | 12 | internal static IRgb ToColor(IRgb item) 13 | { 14 | return item; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/Utility/DoubleExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions.Utility 4 | { 5 | internal static class DoubleExtension 6 | { 7 | private const double DefaultPrecision = .0001; 8 | 9 | internal static bool BasicallyEqualTo(this double a, double b) 10 | { 11 | return a.BasicallyEqualTo(b, DefaultPrecision); 12 | } 13 | 14 | internal static bool BasicallyEqualTo(this double a, double b, double precision) 15 | { 16 | return Math.Abs(a - b) <= precision; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/XyzConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions 4 | { 5 | internal static class XyzConverter 6 | { 7 | #region Constants/Helper methods for Xyz related spaces 8 | internal static IXyz WhiteReference { get; private set; } // TODO: Hard-coded! 9 | internal const double Epsilon = 0.008856; // Intent is 216/24389 10 | internal const double Kappa = 903.3; // Intent is 24389/27 11 | static XyzConverter() 12 | { 13 | WhiteReference = new Xyz 14 | { 15 | X = 95.047, 16 | Y = 100.000, 17 | Z = 108.883 18 | }; 19 | } 20 | 21 | internal static double CubicRoot(double n) 22 | { 23 | return Math.Pow(n, 1.0 / 3.0); 24 | } 25 | #endregion 26 | 27 | internal static void ToColorSpace(IRgb color, IXyz item) 28 | { 29 | var r = PivotRgb(color.R / 255.0); 30 | var g = PivotRgb(color.G / 255.0); 31 | var b = PivotRgb(color.B / 255.0); 32 | 33 | // Observer. = 2°, Illuminant = D65 34 | item.X = r * 0.4124 + g * 0.3576 + b * 0.1805; 35 | item.Y = r * 0.2126 + g * 0.7152 + b * 0.0722; 36 | item.Z = r * 0.0193 + g * 0.1192 + b * 0.9505; 37 | } 38 | 39 | internal static IRgb ToColor(IXyz item) 40 | { 41 | // (Observer = 2°, Illuminant = D65) 42 | var x = item.X / 100.0; 43 | var y = item.Y / 100.0; 44 | var z = item.Z / 100.0; 45 | 46 | var r = x * 3.2406 + y * -1.5372 + z * -0.4986; 47 | var g = x * -0.9689 + y * 1.8758 + z * 0.0415; 48 | var b = x * 0.0557 + y * -0.2040 + z * 1.0570; 49 | 50 | r = r > 0.0031308 ? 1.055 * Math.Pow(r, 1 / 2.4) - 0.055 : 12.92 * r; 51 | g = g > 0.0031308 ? 1.055 * Math.Pow(g, 1 / 2.4) - 0.055 : 12.92 * g; 52 | b = b > 0.0031308 ? 1.055 * Math.Pow(b, 1 / 2.4) - 0.055 : 12.92 * b; 53 | 54 | return new Rgb 55 | { 56 | R = ToRgb(r), 57 | G = ToRgb(g), 58 | B = ToRgb(b) 59 | }; 60 | } 61 | 62 | private static double ToRgb(double n) 63 | { 64 | var result = 255.0 * n; 65 | if (result < 0) return 0; 66 | if (result > 255) return 255; 67 | return result; 68 | } 69 | 70 | private static double PivotRgb(double n) 71 | { 72 | return (n > 0.04045 ? Math.Pow((n + 0.055) / 1.055, 2.4) : n / 12.92) * 100.0; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /Source/Assembly/ColorSpaces/Conversions/YxyConverter.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces.Conversions.Utility; 2 | 3 | namespace PoshCode.Pansies.ColorSpaces.Conversions 4 | { 5 | internal static class YxyConverter 6 | { 7 | internal static void ToColorSpace(IRgb color, IYxy item) 8 | { 9 | var xyz = new Xyz(); 10 | xyz.Initialize(color); 11 | 12 | item.Y1 = xyz.Y; 13 | 14 | var xDividend = xyz.X + xyz.Y + xyz.Z; 15 | item.X = xDividend.BasicallyEqualTo(0) ? 0.0 : xyz.X / xDividend; 16 | 17 | var y2Dividend = xyz.X + xyz.Y + xyz.Z; 18 | item.Y2 = y2Dividend.BasicallyEqualTo(0) ? 0.0 : xyz.Y / (xyz.X + xyz.Y + xyz.Z); 19 | } 20 | 21 | internal static IRgb ToColor(IYxy item) 22 | { 23 | var xyz = new Xyz 24 | { 25 | X = item.X * (item.Y1 / item.Y2), 26 | Y = item.Y1, 27 | Z = (1.0 - item.X - item.Y2) * (item.Y1 / item.Y2) 28 | }; 29 | return xyz.ToRgb(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Source/Assembly/Colors.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostspecific="true" language="C#" #> 2 | <#@ output extension=".cs" #>// Note: This is a generated file. 3 | <#@ assembly name="System.Core" #> 4 | <#@ assembly name="System.Xml.dll" #> 5 | <#@ import namespace="System.Linq" #> 6 | <#@ import namespace="System.Text" #> 7 | <#@ import Namespace="System.Xml" #> 8 | <#@ import namespace="System.Collections.Generic" #> 9 | using PoshCode.Pansies.ColorSpaces.Conversions; 10 | using PoshCode.Pansies.ColorSpaces; 11 | 12 | namespace PoshCode.Pansies 13 | { 14 | <# 15 | var document2 = new XmlDocument(); 16 | var file2 = Host.ResolvePath("./ColorSpaces/ColorSpaces.xml"); 17 | document2.Load(file2); 18 | var colorSpaces = document2.SelectSingleNode("colorSpaces"); 19 | 20 | foreach (XmlNode space in colorSpaces) 21 | { 22 | var className = space.Attributes["name"].Value; 23 | var points = space.SelectNodes("dataPoints/dataPoint"); 24 | #> 25 | public partial class <#=className#>Color : PoshCode.Pansies.ColorSpaces.<#=className#> 26 | { 27 | public <#=className#>Color(){} 28 | 29 | // IColorSpace means they have To() and Initialize(IRgb) 30 | // But PowerShell doesn't handle generic methods properly 31 | // And we want cast capability 32 | <# 33 | foreach (XmlNode otherSpace in colorSpaces) 34 | { 35 | var otherClassName = otherSpace.Attributes["name"].Value; 36 | #> 37 | public <#=className#>Color(I<#=otherClassName#> <#=otherClassName.ToLower()#>) 38 | { 39 | <# if (otherClassName == className) 40 | { 41 | #> 42 | Ordinals = <#=otherClassName.ToLower()#>.Ordinals; 43 | <# 44 | } 45 | else 46 | { 47 | #> 48 | Initialize(<#=otherClassName.ToLower()#>.ToRgb()); 49 | <# 50 | } 51 | #> 52 | } 53 | 54 | <# 55 | if (otherClassName != "Rgb") 56 | { 57 | #> 58 | public I<#=otherClassName#> To<#=otherClassName#>() 59 | { 60 | return new <#=otherClassName#>Color(this); 61 | } 62 | <# 63 | } 64 | if (otherClassName != className) 65 | { 66 | #> 67 | public static implicit operator <#=className#>Color(<#=otherClassName#> <#=otherClassName.ToLower()#>) 68 | { 69 | return new <#=className#>Color(<#=otherClassName.ToLower()#>); 70 | } 71 | 72 | public static implicit operator <#=className#>Color(<#=otherClassName#>Color <#=otherClassName.ToLower()#>) 73 | { 74 | return new <#=className#>Color(<#=otherClassName.ToLower()#>); 75 | } 76 | <# 77 | } 78 | } 79 | #> 80 | } 81 | <# 82 | } 83 | #> 84 | } -------------------------------------------------------------------------------- /Source/Assembly/Commands/GetColorWheel.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using System.Linq; 3 | using System.Management.Automation; 4 | using System.Management.Automation.Host; 5 | 6 | namespace PoshCode.Pansies.Commands 7 | { 8 | [Cmdlet("Get","ColorWheel")] 9 | public class GetColorWheel : Cmdlet 10 | { 11 | [Parameter(ValueFromPipeline = true, Position = 0)] 12 | [Alias("StartColor")] 13 | public RgbColor Color { get; set; } = new RgbColor(255, 0, 0); 14 | 15 | // Force the luminance to have "enough" contrast 16 | [Parameter()] 17 | public int Count { get; set; } = 7; 18 | 19 | // Assume there are only 16 colors 20 | [Parameter()] 21 | public int HueStep { get; set; } = 50; 22 | 23 | [Parameter()] 24 | [Alias("LightStep")] 25 | public int BrightStep { get; set; } = 4; 26 | 27 | // # If set, output the input $Color before the complement 28 | [Parameter()] 29 | public SwitchParameter Passthru { get; set; } 30 | 31 | protected override void ProcessRecord() 32 | { 33 | base.ProcessRecord(); 34 | if (Passthru) { 35 | Color.Mode = ColorMode.Rgb24Bit; 36 | WriteObject(Color); 37 | } 38 | } 39 | protected override void EndProcessing() 40 | { 41 | base.EndProcessing(); 42 | 43 | WriteObject(Gradient.GetRainbow(Color, Count, HueStep, BrightStep).Select( c => { c.Mode = ColorMode.Rgb24Bit; return c; }), true); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Source/Assembly/Commands/GetComplement.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Host; 4 | 5 | namespace PoshCode.Pansies.Commands 6 | { 7 | [Cmdlet("Get","Complement")] 8 | public class GetComplementCommand : Cmdlet 9 | { 10 | [Parameter(ValueFromPipeline = true, Mandatory = true, Position = 0)] 11 | [Alias("BackgroundColor")] 12 | public RgbColor Color { get; set; } 13 | 14 | // Force the luminance to have "enough" contrast 15 | [Parameter()] 16 | [Alias("ForceContrast")] 17 | public SwitchParameter HighContrast { get; set; } 18 | 19 | // Assume there are only 16 colors 20 | [Parameter()] 21 | [Alias("ConsoleColor")] 22 | public SwitchParameter BlackAndWhite { get; set; } 23 | 24 | // # If set, output the input $Color before the complement 25 | [Parameter()] 26 | public SwitchParameter Passthru { get; set; } 27 | 28 | [Parameter()] 29 | public SwitchParameter AsObject { get; set; } 30 | 31 | protected override void ProcessRecord() 32 | { 33 | base.ProcessRecord(); 34 | 35 | if (!AsObject) 36 | { 37 | if (Passthru) 38 | { 39 | WriteObject(Color); 40 | } 41 | 42 | WriteObject(Color.GetComplement(HighContrast, BlackAndWhite)); 43 | } 44 | else 45 | { 46 | WriteObject(new PSObject(new { 47 | BackgroundColor = Color, 48 | ForegroundColor = Color.GetComplement(HighContrast, BlackAndWhite) 49 | })); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Source/Assembly/Commands/GetGradientCommand.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using System.Management.Automation; 3 | using System.Management.Automation.Host; 4 | 5 | namespace PoshCode.Pansies.Commands 6 | { 7 | [Cmdlet("Get","Gradient")] 8 | public class GetGradientCommand : PSCmdlet 9 | { 10 | /// 11 | /// Gets or Sets the start color for the gradient 12 | /// 13 | [Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = true)] 14 | [Alias("SC")] 15 | public RgbColor StartColor { get; set; } 16 | 17 | /// 18 | /// Gets or Sets the end color for the gradient 19 | /// 20 | [Parameter(Position = 1, ValueFromPipelineByPropertyName = true, Mandatory = true)] 21 | [Alias("EC")] 22 | public RgbColor EndColor { get; set; } 23 | 24 | [Parameter(Position = 2)] 25 | [Alias("Length", "Count", "Steps")] 26 | [ValidateRange(3, int.MaxValue)] 27 | public int Width { get; set; } = -1; 28 | 29 | [Parameter(Position = 3)] 30 | [ValidateRange(1, int.MaxValue)] 31 | public int Height { get; set; } = 1; 32 | 33 | [Parameter] 34 | public SwitchParameter Reverse { get; set; } 35 | 36 | [Parameter] 37 | public SwitchParameter Flatten { get; set; } 38 | 39 | [Parameter(ValueFromPipeline = true)] 40 | [ValidateSet("Hsl", "Lch", "Rgb", "Lab", "Xyz")] 41 | public string ColorSpace { get; set; } = "Lch"; 42 | 43 | private static System.Reflection.MethodInfo Get2DGradient; 44 | 45 | static GetGradientCommand() 46 | { 47 | Get2DGradient = typeof(Gradient).GetMethod("Get2DGradient", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); 48 | } 49 | 50 | protected override void ProcessRecord() 51 | { 52 | base.ProcessRecord(); 53 | 54 | if (Width <= 0) 55 | { 56 | PSHost host = GetVariableValue("Host") as PSHost; 57 | Width = host.UI.RawUI.WindowSize.Width; 58 | } 59 | Height = System.Math.Max(1, Height); 60 | Width = System.Math.Max(1, Width); 61 | ColorSpace = ColorSpace.Substring(0, 1).ToUpperInvariant() + ColorSpace.Substring(1).ToLowerInvariant(); 62 | var colorType = GetType().Assembly.GetType($"PoshCode.Pansies.ColorSpaces.{ColorSpace}"); 63 | var colors = (RgbColor[][])Get2DGradient.MakeGenericMethod(typeof(RgbColor), colorType).Invoke(null, 64 | new object[] { StartColor, EndColor, Height, Width, Reverse.ToBool() }); 65 | 66 | if (Flatten || Height == 1) 67 | { 68 | foreach(var row in colors) 69 | { 70 | foreach(var col in row) 71 | { 72 | WriteObject(col); 73 | } 74 | } 75 | } 76 | else 77 | { 78 | WriteObject(colors, false); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Source/Assembly/Commands/NewHyperlinkCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace PoshCode.Pansies.Commands 4 | { 5 | [Cmdlet("New", "Hyperlink")] 6 | [Alias("Url")] 7 | public class NewHyperlinkCommand : Cmdlet 8 | { 9 | 10 | /// 11 | /// Gets or sets the Uri for the hyperlink. 12 | /// 13 | /// A string 14 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] 15 | public string Uri { get; set; } 16 | 17 | /// 18 | /// Gets or sets the object to be linked. The Object will be converted to string, and wrapped in a link. Defaults to the Uri 19 | /// 20 | /// A string 21 | [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, Position = 1)] 22 | [Alias("Text")] 23 | public object Object { get; set; } 24 | 25 | /// 26 | /// Gets or Sets the separator text to use if the Object is an array 27 | /// 28 | [Parameter()] 29 | public object Separator { get; set; } = " "; 30 | 31 | /// 32 | /// Gets or Sets the background color for the block 33 | /// 34 | [Parameter(ValueFromPipelineByPropertyName = true)] 35 | [Alias("Bg")] 36 | public RgbColor BackgroundColor { get; set; } 37 | 38 | /// 39 | /// Gets or Sets the foreground color for the block 40 | /// 41 | [Parameter(ValueFromPipelineByPropertyName = true)] 42 | [Alias("Fg")] 43 | public RgbColor ForegroundColor { get; set; } 44 | 45 | [Parameter] 46 | public SwitchParameter LeaveColor { get; set; } 47 | 48 | [Parameter] 49 | public SwitchParameter IgnoreEntities { get; set; } 50 | 51 | [Parameter] 52 | public SwitchParameter Passthru { get; set; } 53 | 54 | protected override void ProcessRecord() 55 | { 56 | base.ProcessRecord(); 57 | 58 | if (null != Object) { 59 | Object = Text.ConvertToString(Object, Separator.ToString()); 60 | } else { 61 | Object = Uri; 62 | } 63 | 64 | Object = $"\u001B]8;;{Uri}\a{Object}\u001B]8;;\a"; 65 | 66 | var result = new Text() 67 | { 68 | BackgroundColor = BackgroundColor, 69 | ForegroundColor = ForegroundColor, 70 | Separator = Separator, 71 | Object = Object, 72 | Clear = !LeaveColor, 73 | Entities = !IgnoreEntities 74 | }; 75 | 76 | if (Passthru) { 77 | WriteObject(result); 78 | } else { 79 | WriteObject(result.ToString()); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Source/Assembly/Commands/NewTextCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | 3 | namespace PoshCode.Pansies.Commands 4 | { 5 | [Cmdlet("New","Text")][Alias("Text")] 6 | public class NewTextCommand : Cmdlet 7 | { 8 | /// 9 | /// Gets or sets the object. The Object will be converted to string when it's set, and this property always returns a string. 10 | /// 11 | /// A string 12 | [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ValueFromRemainingArguments = true, Position = 0)] 13 | public object Object { get; set; } 14 | 15 | /// 16 | /// Gets or Sets the background color for the block 17 | /// 18 | [Parameter(ValueFromPipelineByPropertyName = true)] 19 | [Alias("Bg")] 20 | public RgbColor BackgroundColor { get; set; } 21 | 22 | 23 | [Parameter()] 24 | public object Separator { get; set; } = " "; 25 | 26 | /// 27 | /// Gets or Sets the foreground color for the block 28 | /// 29 | [Parameter(ValueFromPipelineByPropertyName = true)] 30 | [Alias("Fg")] 31 | public RgbColor ForegroundColor { get; set; } 32 | 33 | [Parameter] 34 | public SwitchParameter LeaveColor { get; set; } 35 | 36 | [Parameter] 37 | public SwitchParameter IgnoreEntities { get; set; } 38 | 39 | protected override void ProcessRecord() 40 | { 41 | base.ProcessRecord(); 42 | 43 | var result = new Text() 44 | { 45 | BackgroundColor = BackgroundColor, 46 | ForegroundColor = ForegroundColor, 47 | Separator = Separator, 48 | Object = Object, 49 | Clear = !LeaveColor, 50 | Entities = !IgnoreEntities, 51 | }; 52 | WriteObject(result); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/Assembly/Commands/WriteHostCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Management.Automation; 4 | 5 | namespace PoshCode.Pansies.Commands 6 | { 7 | [Cmdlet("Write","Host")] 8 | public class WriteHostCommand : PSCmdlet 9 | { 10 | 11 | [Parameter(Position=0, ValueFromPipeline=true, ValueFromRemainingArguments=true)] 12 | public object Object { get; set; } 13 | 14 | [Parameter()] 15 | public SwitchParameter NoNewline { get; set; } 16 | 17 | [Parameter()] 18 | public object Separator { get; set; } = " "; 19 | [Alias("Fg")] 20 | [Parameter(ValueFromPipelineByPropertyName = true)] 21 | public RgbColor ForegroundColor { get; set; } 22 | 23 | [Parameter(ValueFromPipelineByPropertyName = true)] 24 | [Alias("Bg")] 25 | public RgbColor BackgroundColor { get; set; } 26 | 27 | [Parameter] 28 | public SwitchParameter PersistentColor { get; set; } 29 | 30 | protected override void ProcessRecord() 31 | { 32 | HostInformationMessage informationMessage = new HostInformationMessage(); 33 | informationMessage.Message = Text.GetString(ForegroundColor, BackgroundColor, Object, Separator.ToString(), true, true, PersistentColor); 34 | informationMessage.NoNewLine = NoNewline.IsPresent; 35 | 36 | var tags = new string[] { "PSHOST" }; 37 | 38 | // Discuss: is it worth implementing this, even though Cmdlet.WriteHost won't respect it? 39 | var value = GetVariableValue("HostPreference", ActionPreference.Continue); 40 | // NOTE: Anything but Continue and SilentlyContinue (or Ignore) is pointless, since you can set them on Information 41 | if (value is ActionPreference preference && (preference == ActionPreference.SilentlyContinue || preference == ActionPreference.Ignore)) 42 | { 43 | tags = new string[] { }; 44 | } 45 | 46 | WriteInformation(informationMessage, tags); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Source/Assembly/EmptyStringAsNullAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation; 3 | 4 | /// 5 | /// An argument transformation attribute for treating empty strings as null when casting an empty string won't work 6 | /// 7 | public class EmptyStringAsNullAttribute : ArgumentTransformationAttribute 8 | { 9 | public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) 10 | { 11 | if (inputData is string && ((string)inputData).Length == 0) 12 | { 13 | return null; 14 | } 15 | else 16 | { 17 | return inputData; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Source/Assembly/Entities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Text; 5 | 6 | namespace PoshCode.Pansies 7 | { 8 | public static partial class Entities 9 | { 10 | public static bool EnableEmoji = true; 11 | public static bool EnableNerdFonts = true; 12 | private static readonly char[] _entityEndingChars = new char[] { ';', '&' }; 13 | 14 | public static SortedList EscapeSequences = new SortedList(StringComparer.InvariantCultureIgnoreCase) 15 | { 16 | ["Clear"] = "\u001B[0m", 17 | ["Esc"] = "\u001B[", 18 | ["Store"] = "\u001B[s", 19 | ["Recall"] = "\u001B[u", 20 | }; 21 | 22 | public static SortedList ExtendedCharacters = new SortedList(StringComparer.InvariantCultureIgnoreCase) 23 | { 24 | ["Branch"] = "\ue0a0", // Branch symbol 25 | ["ColorSeparator"] = "\u258C", // ▌ 26 | ["Gear"] = "\u26ef", // The settings icon, I use it for debug 27 | ["Lock"] = "\ue0a2", // Padlock 28 | ["Power"] = "\u26a1", // The Power lightning-bolt icon 29 | ["ReverseColorSeparator"] = "\u2590", // ▐ 30 | ["ReverseSeparator"] = "\u25C4", // ◄ 31 | ["Separator"] = "\u25BA", // ► 32 | }; 33 | 34 | public static string Decode(string value) 35 | { 36 | // Don't create StringBuilder if we don't have anything to encode 37 | if ((string.IsNullOrEmpty(value)) || value.IndexOf('&') == -1) 38 | { 39 | return value; 40 | } 41 | int stop = value.Length; 42 | var output = new StringBuilder(stop); 43 | 44 | int end = 0, start = 0; 45 | while((start = value.IndexOf('&', end)) != -1) 46 | { 47 | // if it's at the end, we're done here 48 | if (start == value.Length - 1) 49 | { 50 | break; 51 | } 52 | string result; 53 | output.Append(value.Substring(end, start - end)); 54 | 55 | // We found a '&'. Now look for the next ';' or '&'. 56 | // If we find another '&' then this is not an entity, but that one might be 57 | end = value.IndexOfAny(_entityEndingChars, start + 1); 58 | if (end > 0 && value[end] == ';') 59 | { 60 | string entity = value.Substring(start + 1, end - start - 1); 61 | end++; 62 | 63 | if (EscapeSequences.TryGetValue(entity, out result)) 64 | { 65 | output.Append(result); 66 | } 67 | else if (ExtendedCharacters.TryGetValue(entity, out result)) 68 | { 69 | output.Append(result); 70 | } 71 | else if (EnableEmoji && Emoji.TryGetValue(entity, out result)) 72 | { 73 | output.Append(result); 74 | } 75 | else if (EnableNerdFonts && NerdFonts.TryGetValue(entity, out result)) 76 | { 77 | output.Append(result); 78 | } 79 | else 80 | { 81 | output.Append('&'); 82 | output.Append(entity); 83 | output.Append(';'); 84 | continue; 85 | } 86 | } 87 | // if we reached the end, stop looking 88 | if (end < 0) 89 | { 90 | // and don't loose the end of the string 91 | end = start; 92 | break; 93 | } 94 | 95 | } 96 | // make sure we don't loose anything off the end 97 | output.Append(value.Substring(end, value.Length - end)); 98 | 99 | // we don't handle { let WebUtility do that 100 | value = WebUtility.HtmlDecode(output.ToString()); 101 | return value; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/Assembly/Gradient.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace PoshCode.Pansies 9 | { 10 | public class Gradient 11 | { 12 | public static T[][] Get2DGradient(T startColor, T endColor, int height = 1, int width = 120, bool reverseHue = false) where T : IColorSpace, new() where WorkT : IColorSpace, new() 13 | { 14 | var colors = new T[height][]; 15 | 16 | //Simple pythagorean distance 17 | WorkT start = startColor.To(); 18 | WorkT end = endColor.To(); 19 | 20 | double size = Math.Sqrt((height - 1) * (height - 1) + (width - 1) * (width - 1)); 21 | var sizeOrdinals = new double[start.Ordinals.Length]; 22 | for (var i = 0; i < sizeOrdinals.Length; i++) 23 | { 24 | sizeOrdinals[i] = (end.Ordinals[i] - start.Ordinals[i]) / size; 25 | } 26 | var stepSize = new WorkT 27 | { 28 | Ordinals = sizeOrdinals 29 | }; 30 | 31 | /* 32 | Write-Verbose "Size: $('{0:N2}' -f $Size) (width x height) ($($Colors.Length) x $($Colors[0].Length))" 33 | Write-Verbose "Diff: {$StepSize}" 34 | Write-Verbose "From: {$Left} $($StartColor.Ordinals)" 35 | Write-Verbose "To: {$Right} $($EndColor.Ordinals)" 36 | */ 37 | 38 | // For colors based on hue rotation, the math is slightly more complex: 39 | double ceiling = 360; 40 | bool hued = start is IHue; 41 | if (hued) 42 | { 43 | var change = Math.Abs(((IHue)stepSize).H) > 180 / size; 44 | if (reverseHue) 45 | { 46 | change = !change; 47 | } 48 | if (change) 49 | { 50 | if (((IHue)stepSize).H > 0) 51 | { 52 | ((IHue)stepSize).H -= 360 / size; 53 | } 54 | else 55 | { 56 | ((IHue)stepSize).H += 360 / size; 57 | } 58 | } 59 | } 60 | 61 | for (var line = 1; line <= height; line++) 62 | { 63 | colors[line - 1] = new T[width]; 64 | 65 | for (var column = 1; column <= width; column++) 66 | { 67 | var d = Math.Sqrt((line - 1) * (line - 1) + (column - 1) * (column - 1)); 68 | 69 | var stepOrdinals = new double[sizeOrdinals.Length]; 70 | 71 | for (var i = 0; i < sizeOrdinals.Length; i++) 72 | { 73 | stepOrdinals[i] = start.Ordinals[i] + stepSize.Ordinals[i] * d; 74 | } 75 | var stepColor = new WorkT 76 | { 77 | Ordinals = stepOrdinals 78 | }; 79 | // For colors based on hue rotation, the math is slightly more complex: 80 | if (hued) 81 | { 82 | if (((IHue)stepColor).H < 0) 83 | { 84 | ((IHue)stepColor).H += 360; 85 | } 86 | ((IHue)stepColor).H %= ceiling; 87 | } 88 | 89 | colors[line - 1][column - 1] = stepColor.To(); 90 | } 91 | } 92 | return colors; 93 | } 94 | 95 | public static RgbColor[][] GetRgbGradient(RgbColor startColor, RgbColor endColor, int height = 1, int width = 120, bool reverseHue = false) 96 | { 97 | return (RgbColor[][])Get2DGradient(startColor, endColor, height, width, reverseHue); 98 | } 99 | 100 | public static IEnumerable GetGradient(T start, T end, int size = 10, bool reverseHue = false) where T : IColorSpace, new() 101 | { 102 | var sizeOrdinals = new double[start.Ordinals.Length]; 103 | for (var i = 0; i < sizeOrdinals.Length; i++) 104 | { 105 | sizeOrdinals[i] = (end.Ordinals[i] - start.Ordinals[i]) / size; 106 | } 107 | var stepSize = new T 108 | { 109 | Ordinals = sizeOrdinals 110 | }; 111 | 112 | // For colors based on hue rotation, the math is slightly more complex: 113 | double ceiling = 360; 114 | bool hued = start is IHue; 115 | if (hued) 116 | { 117 | var change = Math.Abs(((IHue)stepSize).H) > 180 / size; 118 | if (reverseHue) 119 | { 120 | change = !change; 121 | } 122 | if (change) 123 | { 124 | if (((IHue)stepSize).H > 0) 125 | { 126 | ((IHue)stepSize).H -= 360 / size; 127 | } 128 | else 129 | { 130 | ((IHue)stepSize).H += 360 / size; 131 | } 132 | } 133 | } 134 | 135 | 136 | for (var column = 1; column <= size; column++) 137 | { 138 | var stepOrdinals = new double[sizeOrdinals.Length]; 139 | 140 | for (var i = 0; i < sizeOrdinals.Length; i++) 141 | { 142 | stepOrdinals[i] = start.Ordinals[i] + stepSize.Ordinals[i] * column; 143 | } 144 | var stepColor = new T 145 | { 146 | Ordinals = stepOrdinals 147 | }; 148 | // For colors based on hue rotation, the math is slightly more complex: 149 | if (hued) 150 | { 151 | if (((IHue)stepColor).H < 0) 152 | { 153 | ((IHue)stepColor).H += 360; 154 | } 155 | ((IHue)stepColor).H %= ceiling; 156 | } 157 | 158 | yield return stepColor; 159 | } 160 | } 161 | 162 | 163 | public static IEnumerable GetRainbow(T start, int size, int hueStep = 50, int lightStep = 3, int satStep = 0) where T : IColorSpace, new() 164 | { 165 | Hsl next = start.To(); 166 | 167 | for (int i = 0; i < size; i++) 168 | { 169 | next = new Hsl { Ordinals = next.Ordinals }; 170 | next.H += hueStep; 171 | next.H %= 360; 172 | 173 | if (next.S + satStep > 100) { 174 | next.S = 20; 175 | } else { 176 | next.S += satStep; 177 | } 178 | if (next.L + lightStep > 80) { 179 | next.L = 20; 180 | } else { 181 | next.L += lightStep; 182 | } 183 | 184 | yield return next.To(); 185 | } 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /Source/Assembly/HslColor.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace PoshCode.Pansies 7 | { 8 | public enum Harmony 9 | { 10 | Analogous, 11 | Complementary, 12 | Split, 13 | Triad, 14 | } 15 | public partial class HslColor : Hsl 16 | { 17 | private static Random random = new Random(); 18 | 19 | public IEnumerable GetHarmony(Harmony harmony) 20 | { 21 | switch (harmony) 22 | { 23 | case Harmony.Analogous: 24 | //Analogous: Choose second and third ranges 0. 25 | return GetHarmony(3, 30, 30, 30, 0, 0); 26 | case Harmony.Complementary: 27 | //Complementary: Choose the third range 0, and first offset angle 180. 28 | return GetHarmony(2, 180, 0, 0, 1, 0); 29 | case Harmony.Split: 30 | //Split Complementary: Choose offset angles 180 +/ -a small angle. The second and third ranges must be smaller than the difference between the two offset angles. 31 | return GetHarmony(3, 160, 200, 30, 2, 20); 32 | case Harmony.Triad: 33 | //Triad: Choose offset angles 120 and 240. 34 | return GetHarmony(3, 120, 240, 30, 2, 20); 35 | default: 36 | return GetHarmony(6, random.NextDouble() * 180, random.NextDouble() * 180, random.NextDouble() * 60, random.NextDouble() * 60, random.NextDouble() * 60); 37 | } 38 | 39 | 40 | } 41 | 42 | public IEnumerable GetHarmony(int colorCount, double offsetAngle1, double offsetAngle2, double rangeAngle0, double rangeAngle1, double rangeAngle2) 43 | { 44 | var distance = 360 / colorCount; 45 | offsetAngle1 %= distance; 46 | offsetAngle2 %= distance; 47 | var minimum = distance - offsetAngle1; 48 | var maximum = distance + offsetAngle2; 49 | double newAngle = (random.NextDouble() * (maximum - minimum)) + minimum; 50 | 51 | var next = this; 52 | 53 | for (int i = 0; i < colorCount; i++) 54 | { 55 | yield return next; 56 | 57 | next = new HslColor 58 | { 59 | Ordinals = next.Ordinals 60 | }; 61 | 62 | 63 | if (next.H > (this.H + distance) % 360 ) 64 | { 65 | newAngle *= -1; 66 | } 67 | 68 | next.H = (H + newAngle + 360) % 360.0; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Source/Assembly/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace PoshCode.Pansies 5 | { 6 | public static class NativeMethods 7 | { 8 | private static readonly IntPtr ConsoleOutputHandle; 9 | public const int StandardOutputHandle = -11; 10 | public static readonly IntPtr InvalidHandle = new IntPtr(-1); 11 | 12 | 13 | [DllImport("kernel32.dll", SetLastError = true)] 14 | public static extern IntPtr GetStdHandle(int nStdHandle); 15 | 16 | [DllImport("kernel32.dll")] 17 | internal static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); 18 | 19 | [DllImport("kernel32.dll")] 20 | internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out ConsoleOutputModes mode); 21 | 22 | [Flags] 23 | public enum ConsoleOutputModes : uint 24 | { 25 | EnableProcessedOutput = 1, 26 | EnableWrapAtEOL = 2, 27 | EnableVirtualTerminalProcessing = 4, 28 | DisableNewlineAutoReturn = 8, 29 | EnableLvbGridWorldwide = 10, 30 | } 31 | 32 | static NativeMethods() 33 | { 34 | ConsoleOutputHandle = GetStdHandle(StandardOutputHandle); // 7 35 | if (ConsoleOutputHandle == InvalidHandle) 36 | { 37 | throw new System.Exception("GetStdHandle->WinError: #" + Marshal.GetLastWin32Error()); 38 | } 39 | } 40 | 41 | public static void EnableVirtualTerminalProcessing() 42 | { 43 | ConsoleOutputModes mode; 44 | if (!GetConsoleMode(ConsoleOutputHandle, out mode)) 45 | { 46 | mode = ConsoleOutputModes.EnableProcessedOutput | ConsoleOutputModes.EnableWrapAtEOL; 47 | } 48 | mode |= ConsoleOutputModes.EnableVirtualTerminalProcessing; 49 | 50 | SetConsoleMode(ConsoleOutputHandle, (uint)mode); 51 | } 52 | 53 | public static void DisableVirtualTerminalProcessing() 54 | { 55 | ConsoleOutputModes mode; 56 | if (!GetConsoleMode(ConsoleOutputHandle, out mode)) 57 | { 58 | mode = ConsoleOutputModes.EnableProcessedOutput | ConsoleOutputModes.EnableWrapAtEOL; 59 | } 60 | mode &= ~ConsoleOutputModes.EnableVirtualTerminalProcessing; 61 | 62 | SetConsoleMode(ConsoleOutputHandle, (uint)mode); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Source/Assembly/Palettes/ConsolePalette.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces.Comparisons; 2 | using PoshCode.Pansies.Palettes; 3 | 4 | namespace PoshCode.Pansies.Palettes 5 | { 6 | public class ConsolePalette : Palette 7 | { 8 | public override IColorSpaceComparison ComparisonAlgorithm { get; set; } = new CieDe2000Comparison(); 9 | 10 | public ConsolePalette() 11 | { 12 | // The ConsolePalette needs to be in the .NET ConsoleColors enum order 13 | Add(new RgbColor(0x00, 0x00, 0x00)); 14 | Add(new RgbColor(0x00, 0x00, 0x80)); 15 | Add(new RgbColor(0x00, 0x80, 0x00)); 16 | Add(new RgbColor(0x00, 0x80, 0x80)); 17 | Add(new RgbColor(0x80, 0x00, 0x00)); 18 | Add(new RgbColor(0x80, 0x00, 0x80)); 19 | Add(new RgbColor(0x80, 0x80, 0x00)); 20 | Add(new RgbColor(0xc0, 0xc0, 0xc0)); 21 | Add(new RgbColor(0x80, 0x80, 0x80)); 22 | Add(new RgbColor(0x00, 0x00, 0xff)); 23 | Add(new RgbColor(0x00, 0xff, 0x00)); 24 | Add(new RgbColor(0x00, 0xff, 0xff)); 25 | Add(new RgbColor(0xff, 0x00, 0x00)); 26 | Add(new RgbColor(0xff, 0x00, 0xff)); 27 | Add(new RgbColor(0xff, 0xff, 0x00)); 28 | Add(new RgbColor(0xff, 0xff, 0xff)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/Assembly/Palettes/IPalette.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using PoshCode.Pansies.ColorSpaces.Comparisons; 3 | using System.Collections.Generic; 4 | 5 | namespace PoshCode.Pansies.Palettes 6 | { 7 | public interface IPalette : IList where T : IColorSpace, new() 8 | { 9 | /// 10 | /// A used to find the closest match from a Palette to another color 11 | /// 12 | IColorSpaceComparison ComparisonAlgorithm { get; set; } 13 | 14 | /// 15 | /// Find the closest match in the palette to the specified color 16 | /// 17 | /// A color to match 18 | /// The closest color that's already in the palette 19 | T FindClosestColor(IColorSpace color); 20 | 21 | /// 22 | /// Find the Palette index of the closest match to the specified color 23 | /// 24 | /// A color to match 25 | /// The index of the closest color that's already in the palette 26 | int FindClosestColorIndex(IColorSpace color); 27 | } 28 | } -------------------------------------------------------------------------------- /Source/Assembly/Palettes/Palette.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces; 2 | using PoshCode.Pansies.ColorSpaces.Comparisons; 3 | using System.Collections.Generic; 4 | using System; 5 | using System.Collections; 6 | using System.Linq; 7 | 8 | namespace PoshCode.Pansies.Palettes 9 | { 10 | public class Palette : IPalette where T: IColorSpace, new() 11 | { 12 | protected IList nativeColors = new List(); 13 | private IList labColors = new List(); 14 | 15 | public T this[int index] 16 | { 17 | get => nativeColors[index]; 18 | set 19 | { 20 | nativeColors[index] = value; 21 | labColors[index] = value.To(); 22 | } 23 | } 24 | 25 | public virtual IColorSpaceComparison ComparisonAlgorithm { get; set; } = new CieDe2000Comparison(); 26 | 27 | public int Count => nativeColors.Count; 28 | 29 | public bool IsReadOnly => nativeColors.IsReadOnly; 30 | 31 | public void Add(T item) 32 | { 33 | nativeColors.Add(item); 34 | labColors.Add(item.To()); 35 | } 36 | 37 | public void Clear() 38 | { 39 | nativeColors.Clear(); 40 | labColors.Clear(); 41 | } 42 | 43 | public Palette(){} 44 | 45 | public Palette(IEnumerable items) 46 | { 47 | foreach(var item in items) 48 | { 49 | Add(item); 50 | } 51 | } 52 | 53 | /// 54 | /// This comparer ought to be overriden by any implementation which also stores colors in another color space. 55 | /// 56 | public virtual bool Contains(T item) 57 | { 58 | return nativeColors.Contains(item); 59 | } 60 | 61 | public void CopyTo(T[] array, int arrayIndex) 62 | { 63 | nativeColors.CopyTo(array, arrayIndex); 64 | } 65 | 66 | public struct FindResult 67 | { 68 | public int Index; 69 | public double Distance; 70 | public C Color; 71 | 72 | public FindResult(int index, double distance, C color) 73 | { 74 | Index = index; 75 | Distance = distance; 76 | Color = color; 77 | } 78 | } 79 | 80 | public FindResult FindClosestColor(IColorSpace color) where TColor : IColorSpace, new() 81 | { 82 | var result = 0; 83 | var minValue = double.MaxValue; 84 | for (var index = 0; index < labColors.Count; index++) 85 | { 86 | var paletteColor = labColors[index]; 87 | var distance = ComparisonAlgorithm.Compare(color, paletteColor); 88 | if (distance < minValue) 89 | { 90 | result = index; 91 | minValue = distance; 92 | } 93 | } 94 | 95 | return new FindResult(result, minValue, labColors[result].To()); 96 | } 97 | 98 | public T FindClosestColor(IColorSpace color) 99 | { 100 | return FindClosestColor(color).Color; 101 | } 102 | 103 | 104 | public int FindClosestColorIndex(IColorSpace color) 105 | { 106 | return FindClosestColor(color).Index; 107 | } 108 | 109 | public IEnumerator GetEnumerator() 110 | { 111 | return nativeColors.GetEnumerator(); 112 | } 113 | 114 | /// 115 | /// This comparer ought to be overriden by any implementation which also stores colors in another color space. 116 | /// 117 | public virtual int IndexOf(T item) 118 | { 119 | return nativeColors.IndexOf(item); 120 | } 121 | 122 | public void Insert(int index, T item) 123 | { 124 | nativeColors.Insert(index, item); 125 | labColors.Insert(index, item.To()); 126 | } 127 | 128 | public bool Remove(T item) 129 | { 130 | 131 | 132 | var index = nativeColors.IndexOf(item); 133 | if (index >= 0) 134 | { 135 | nativeColors.RemoveAt(index); 136 | labColors.RemoveAt(index); 137 | } 138 | return index >= 0; 139 | } 140 | 141 | public void RemoveAt(int index) 142 | { 143 | nativeColors.RemoveAt(index); 144 | labColors.RemoveAt(index); 145 | } 146 | 147 | IEnumerator IEnumerable.GetEnumerator() 148 | { 149 | return nativeColors.GetEnumerator(); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /Source/Assembly/Palettes/XTermPalette.cs: -------------------------------------------------------------------------------- 1 | using PoshCode.Pansies.ColorSpaces.Comparisons; 2 | using PoshCode.Pansies.Palettes; 3 | 4 | namespace PoshCode.Pansies.Palettes 5 | { 6 | /// 7 | /// This is the xTerm color palette with the default colors. 8 | /// On Windows, in the default terminal there's no way to customize them (yet). 9 | /// 10 | public class XTermPalette : Palette 11 | { 12 | public override IColorSpaceComparison ComparisonAlgorithm { get; set; } = new CieDe2000Comparison(); 13 | 14 | public XTermPalette() 15 | { 16 | // Sixteen Console Colors 17 | Add(new RgbColor { R = 0x00, G = 0x00, B = 0x00 }); 18 | 19 | Add(new RgbColor { R = 0x80, G = 0x00, B = 0x00 }); 20 | Add(new RgbColor { R = 0x00, G = 0x80, B = 0x00 }); 21 | Add(new RgbColor { R = 0x80, G = 0x80, B = 0x00 }); 22 | Add(new RgbColor { R = 0x00, G = 0x00, B = 0x80 }); 23 | Add(new RgbColor { R = 0x80, G = 0x00, B = 0x80 }); 24 | Add(new RgbColor { R = 0x00, G = 0x80, B = 0x80 }); 25 | 26 | Add(new RgbColor { R = 0xc0, G = 0xc0, B = 0xc0 }); 27 | Add(new RgbColor { R = 0x80, G = 0x80, B = 0x80 }); 28 | 29 | Add(new RgbColor { R = 0xff, G = 0x00, B = 0x00 }); 30 | Add(new RgbColor { R = 0x00, G = 0xff, B = 0x00 }); 31 | Add(new RgbColor { R = 0xff, G = 0xff, B = 0x00 }); 32 | Add(new RgbColor { R = 0x00, G = 0x00, B = 0xff }); 33 | Add(new RgbColor { R = 0xff, G = 0x00, B = 0xff }); 34 | Add(new RgbColor { R = 0x00, G = 0xff, B = 0xff }); 35 | 36 | Add(new RgbColor { R = 0xff, G = 0xff, B = 0xff }); 37 | 38 | // 215 Color Table 39 | int[] colorValue = new[] { 0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff }; 40 | 41 | for (var r = 0; r <= 5; r++) 42 | { 43 | for (var g = 0; g <= 5; g++) 44 | { 45 | for (var b = 0; b <= 5; b++) 46 | { 47 | var index = 16 + 36 * r + 6 * g + b; 48 | Add(new RgbColor 49 | { 50 | R = colorValue[r], 51 | G = colorValue[g], 52 | B = colorValue[b] 53 | }); 54 | } 55 | } 56 | } 57 | 58 | // 24 Shades of Gray 59 | for (var x = 0; x < 24; x++) 60 | { 61 | Add(new RgbColor 62 | { 63 | R = 0x8 + (0xA * x), 64 | G = 0x8 + (0xA * x), 65 | B = 0x8 + (0xA * x) 66 | }); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Source/Assembly/Provider/Paths/RGbColorItem.cs: -------------------------------------------------------------------------------- 1 | using CodeOwls.PowerShell.Paths; 2 | using CodeOwls.PowerShell.Provider.PathNodeProcessors; 3 | using CodeOwls.PowerShell.Provider.PathNodes; 4 | using PoshCode.Pansies.Palettes; 5 | using System; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Management.Automation.Provider; 11 | using System.Text; 12 | 13 | namespace PoshCode.Pansies.Provider 14 | { 15 | enum RgbColorMode { Foreground, Background } 16 | 17 | class RgbColorContainer : PathNodeBase 18 | { 19 | private RgbColorMode RgbColorMode; 20 | 21 | public RgbColorContainer(RgbColorMode mode) 22 | { 23 | RgbColorMode = mode; 24 | } 25 | 26 | public override IPathValue GetNodeValue() 27 | { 28 | return new ContainerPathValue(RgbColorMode, Name); 29 | } 30 | 31 | public override string Name 32 | { 33 | get { return RgbColorMode.ToString(); } 34 | } 35 | 36 | public override IEnumerable GetNodeChildren(CodeOwls.PowerShell.Provider.PathNodeProcessors.IProviderContext providerContext) 37 | { 38 | return new XTermPalette().Select(color => new RgbColorItem(color, RgbColorMode)); 39 | } 40 | } 41 | 42 | class ColorContentReader : IContentReader 43 | { 44 | private RgbColor Color; 45 | private RgbColorMode RgbColorMode; 46 | 47 | public ColorContentReader(RgbColor color, RgbColorMode mode) 48 | { 49 | Color = color; 50 | RgbColorMode = mode; 51 | } 52 | public void Close() 53 | { 54 | } 55 | 56 | public void Dispose() 57 | { 58 | } 59 | 60 | public IList Read(long readCount) 61 | { 62 | if (Color == null) 63 | { 64 | if (RgbColorMode == RgbColorMode.Background) 65 | { 66 | return new[] { "\u001B[49m" }; 67 | } 68 | else 69 | { 70 | return new[] { "\u001B[39m" }; 71 | } 72 | } 73 | else 74 | { 75 | return new[] { Color.ToVtEscapeSequence(RgbColorMode == RgbColorMode.Background) }; 76 | } 77 | } 78 | 79 | public void Seek(long offset, SeekOrigin origin) 80 | { 81 | throw new NotImplementedException(); 82 | } 83 | } 84 | 85 | 86 | class RgbColorItem : PathNodeBase, IGetItemContent 87 | { 88 | private readonly String name; 89 | private RgbColor Color; 90 | private RgbColorMode RgbColorMode; 91 | 92 | public RgbColorItem(RgbColor color, RgbColorMode mode, string name = null) 93 | { 94 | Color = color; 95 | RgbColorMode = mode; 96 | this.name = name ?? Color.ToString(); 97 | } 98 | 99 | /// 100 | /// supplies the item for the current path value 101 | /// 102 | /// the item it wrapped in either a PathValue instance 103 | /// that describes the item, its name, and whether it is 104 | /// a container. 105 | /// 106 | /// 107 | /// 108 | /// 109 | public override IPathValue GetNodeValue() 110 | { 111 | return new LeafPathValue(Color, Name); 112 | } 113 | 114 | public IContentReader GetContentReader(IProviderContext providerContext) 115 | { 116 | return new ColorContentReader(Color, RgbColorMode); 117 | } 118 | 119 | public object GetContentReaderDynamicParameters(IProviderContext providerContext) 120 | { 121 | throw new NotImplementedException(); 122 | } 123 | 124 | /// 125 | /// supplies the name for the item at the current path value 126 | /// 127 | public override string Name 128 | { 129 | get { return name; } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Source/Assembly/Provider/Paths/RgbColorResolver.cs: -------------------------------------------------------------------------------- 1 | using CodeOwls.PowerShell.Paths.Processors; 2 | using CodeOwls.PowerShell.Provider.PathNodes; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace PoshCode.Pansies.Provider 8 | { 9 | class RgbColorResolver : PathResolverBase 10 | { 11 | /// 12 | /// returns the first node factory object in the path graph 13 | /// 14 | protected override IPathNode Root 15 | { 16 | get 17 | { 18 | return new RgbColorProviderRoot(); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/Assembly/Provider/RgbColorDrive.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Management.Automation; 3 | 4 | namespace PoshCode.Pansies.Provider 5 | { 6 | 7 | // Thinking about renaming this to AnsiColor to prevent collisions with System.Drawing.Color, as I would 8 | // like to add a ctor that takes a System.Drawing.Color.AntiqueWhite, etc. 9 | public class RgbColorDrive : CodeOwls.PowerShell.Provider.Drive 10 | { 11 | // PSDriveRoot constructor 12 | public RgbColorDrive(PSDriveInfo name): base(name) 13 | { 14 | 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Source/Assembly/Provider/RgbColorProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Management.Automation; 6 | using System.Management.Automation.Provider; 7 | using System.Text; 8 | 9 | namespace PoshCode.Pansies.Provider 10 | { 11 | [CmdletProvider("RgbColor", ProviderCapabilities.None)] 12 | public class RgbColorProvider : CodeOwls.PowerShell.Provider.Provider 13 | { 14 | /// 15 | /// a required P2F override 16 | /// 17 | /// supplies P2F with the path processor for this provider 18 | /// 19 | protected override CodeOwls.PowerShell.Paths.Processors.IPathResolver PathResolver 20 | { 21 | get { return new RgbColorResolver(); } 22 | } 23 | 24 | /// 25 | /// overridden to supply a default drive when the provider is loaded 26 | /// 27 | protected override Collection InitializeDefaultDrives() 28 | { 29 | return new Collection 30 | { 31 | new RgbColorDrive( 32 | new PSDriveInfo( "Fg", ProviderInfo, "RgbColor::Foreground:" + System.IO.Path.DirectorySeparatorChar, "Foreground Colors", null ) 33 | ), 34 | new RgbColorDrive( 35 | new PSDriveInfo( "Bg", ProviderInfo, "RgbColor::Background:" + System.IO.Path.DirectorySeparatorChar, "Background Colors", null ) 36 | ) 37 | }; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/Assembly/Provider/RgbColorProviderRoot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using CodeOwls.PowerShell.Provider.PathNodes; 6 | using PoshCode.Pansies.Palettes; 7 | 8 | namespace PoshCode.Pansies.Provider 9 | { 10 | class RgbColorProviderRoot : PathNodeBase 11 | { 12 | #region unchanged code from previous version 13 | public override IPathValue GetNodeValue() 14 | { 15 | return new ContainerPathValue(this, Name); 16 | } 17 | 18 | public override string Name 19 | { 20 | get { return "RgbColor"; } 21 | } 22 | 23 | public override IEnumerable GetNodeChildren(CodeOwls.PowerShell.Provider.PathNodeProcessors.IProviderContext providerContext) 24 | { 25 | var color = providerContext.Path.Split(new[] { System.IO.Path.DirectorySeparatorChar }, 2).Last(); 26 | var mode = providerContext.Path.StartsWith("RgbColor::Background:" + System.IO.Path.DirectorySeparatorChar ) ? RgbColorMode.Background : RgbColorMode.Foreground; 27 | 28 | if (string.IsNullOrEmpty(color) || color.Contains("*")) 29 | { 30 | //if (Enum.TryParse(color, true, out X11ColorName x11Color) && string.Equals(color, x11Color.ToString(), StringComparison.OrdinalIgnoreCase)) 31 | return Enum.GetValues(typeof(X11ColorName)).Cast().Select(name => new RgbColorItem(RgbColor.X11Palette[(int)name], mode, name.ToString())); 32 | //return new X11Palette().Distinct().Select(xColor => new RgbColorItem(xColor, mode)); 33 | } 34 | else if(StringComparer.OrdinalIgnoreCase.Equals(color, "clear")) 35 | { 36 | return new[] { 37 | new RgbColorItem(null, mode, color) 38 | }; 39 | } 40 | else 41 | { 42 | return new[] { 43 | new RgbColorItem(new RgbColor(color), mode, color) 44 | }; 45 | } 46 | } 47 | #endregion 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Source/Assembly/Text.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using System.Management.Automation; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace PoshCode.Pansies 9 | { 10 | public class Text : IEquatable 11 | { 12 | private Regex _escapeCode = new Regex("\u001B\\P{L}+\\p{L}", RegexOptions.Compiled); 13 | 14 | /// 15 | /// Gets or sets the object. 16 | /// 17 | /// A string 18 | public object Object { get; set; } 19 | 20 | public object Separator { get; set; } = " "; 21 | 22 | /// 23 | /// Copy the simple stringification from Write-Host instead of using LanguagePrimitives.ConvertTo 24 | /// 25 | /// 26 | /// Specifically, Write-Host uses it's own Separator instead of $OFS 27 | /// 28 | /// The object 29 | /// The separator 30 | /// A simple string representation 31 | public static string ConvertToString(object @object, string separator = " ") 32 | { 33 | if (@object != null) 34 | { 35 | string s = @object as string; 36 | ScriptBlock sb = null; 37 | IEnumerable enumerable = null; 38 | if (s != null) 39 | { 40 | // strings are IEnumerable, so we special case them 41 | if (s.Length > 0) 42 | { 43 | return s; 44 | } 45 | } 46 | else if ((enumerable = @object as IEnumerable) != null) 47 | { 48 | // unroll enumerables, including arrays. 49 | 50 | bool printSeparator = false; 51 | StringBuilder result = new StringBuilder(); 52 | 53 | foreach (object element in enumerable) 54 | { 55 | if (printSeparator == true && separator != null) 56 | { 57 | result.Append(separator.ToString()); 58 | } 59 | 60 | result.Append(ConvertToString(element, separator)); 61 | printSeparator = true; 62 | } 63 | 64 | return result.ToString(); 65 | } 66 | else if((sb = @object as ScriptBlock) != null) 67 | { 68 | return ConvertToString(sb.Invoke(), separator); 69 | } 70 | else 71 | { 72 | s = @object.ToString(); 73 | 74 | if (s.Length > 0) 75 | { 76 | return s; 77 | } 78 | } 79 | } 80 | 81 | return null; 82 | } 83 | 84 | /// 85 | /// Gets or Sets the background color for the block 86 | /// 87 | public RgbColor BackgroundColor { get; set; } 88 | 89 | /// 90 | /// Gets or Sets the foreground color for the block 91 | /// 92 | public RgbColor ForegroundColor { get; set; } 93 | 94 | /// 95 | /// Gets the length of the text representation (without ANSI escape sequences). 96 | /// 97 | public int Length { 98 | get { 99 | var result = ConvertToString(Object, Separator.ToString()); 100 | return result != null ? _escapeCode.Replace(result,"").Length : 0 ; 101 | } 102 | } 103 | 104 | public bool Clear { get; set; } = false; 105 | public bool Entities { get; set; } = true; 106 | 107 | public bool PersistentColor { get; set; } = true; 108 | 109 | /// 110 | /// This constructor is here so we can allow partial matches to the property names. 111 | /// 112 | /// 113 | public Text(IDictionary values) : this() 114 | { 115 | FromDictionary(values); 116 | } 117 | 118 | private void FromDictionary(IDictionary values) 119 | { 120 | foreach (string key in values.Keys) 121 | { 122 | var pattern = "^" + Regex.Escape(key); 123 | if ("bg".Equals(key, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch("BackgroundColor", pattern, RegexOptions.IgnoreCase)) 124 | { 125 | BackgroundColor = RgbColor.ConvertFrom(values[key]); 126 | } 127 | else if ("fg".Equals(key, StringComparison.OrdinalIgnoreCase) || Regex.IsMatch("ForegroundColor", pattern, RegexOptions.IgnoreCase)) 128 | { 129 | ForegroundColor = RgbColor.ConvertFrom(values[key]); 130 | } 131 | else if (Regex.IsMatch("text", pattern, RegexOptions.IgnoreCase) || Regex.IsMatch("Content", pattern, RegexOptions.IgnoreCase) || Regex.IsMatch("Object", pattern, RegexOptions.IgnoreCase)) 132 | { 133 | Object = values[key]; 134 | } 135 | else if (Regex.IsMatch("clear", pattern, RegexOptions.IgnoreCase) ) 136 | { 137 | Clear = (bool)values[key]; 138 | } 139 | else if (Regex.IsMatch("entities", pattern, RegexOptions.IgnoreCase) ) 140 | { 141 | Entities = (bool)values[key]; 142 | } 143 | else if (Regex.IsMatch("separator", pattern, RegexOptions.IgnoreCase) ) 144 | { 145 | Separator = values[key].ToString(); 146 | } 147 | else if (Regex.IsMatch("persist", pattern, RegexOptions.IgnoreCase) ) 148 | { 149 | PersistentColor = (bool)values[key]; 150 | } 151 | else 152 | { 153 | throw new ArgumentException("Unknown key '" + key + "' in " + values.GetType().Name + ". Allowed values are BackgroundColor (or bg), ForegroundColor (or fg), and Object (also called Content or Text), or Separator, Clear, and Entities"); 154 | } 155 | } 156 | } 157 | // Make sure we can output plain text 158 | public Text(string text) : this() 159 | { 160 | Object = text; 161 | } 162 | 163 | // Make sure we support the default ctor 164 | public Text() { } 165 | 166 | public override string ToString() 167 | { 168 | return GetString(ForegroundColor, BackgroundColor, Object, Separator.ToString(), Clear, Entities, PersistentColor); 169 | } 170 | 171 | public static string GetString(RgbColor foreground, RgbColor background, object @object, string separator = " ", bool clear = false, bool entities = true, bool persistentColor = true) 172 | { 173 | var output = new StringBuilder(); 174 | // There was a bug in Conhost where an advanced 48;2 RGB code followed by a console code wouldn't render the RGB value 175 | // So we try to put the ConsoleColor first, if it's there ... 176 | if (null != foreground) 177 | { 178 | if (foreground.Mode == ColorMode.ConsoleColor) 179 | { 180 | output.Append(foreground.ToVtEscapeSequence(false)); 181 | if (null != background) 182 | { 183 | output.Append(background.ToVtEscapeSequence(true)); 184 | } 185 | } 186 | else 187 | { 188 | if (null != background) 189 | { 190 | output.Append(background.ToVtEscapeSequence(true)); 191 | } 192 | output.Append(foreground.ToVtEscapeSequence(false)); 193 | } 194 | } 195 | else if (null != background) 196 | { 197 | output.Append(background.ToVtEscapeSequence(true)); 198 | } 199 | 200 | // set the color back after each item if Object is an array 201 | if (persistentColor) { 202 | separator = output.ToString() + separator; 203 | } 204 | 205 | if (null != @object) 206 | { 207 | //var scriptBlock = @object as ScriptBlock; 208 | //var text = (string)LanguagePrimitives.ConvertTo(scriptBlock != null ? scriptBlock.Invoke() : @object, typeof(string)); 209 | var text = ConvertToString(@object, separator); 210 | 211 | if (!string.IsNullOrEmpty(text)) 212 | { 213 | output.Append(text); 214 | } 215 | } 216 | 217 | if (clear) 218 | { 219 | if (null != background) 220 | { 221 | // clear background 222 | output.Append("\u001B[49m"); 223 | } 224 | if (null != foreground) 225 | { 226 | // clear foreground 227 | output.Append("\u001B[39m"); 228 | } 229 | } 230 | 231 | if (entities) 232 | { 233 | return PoshCode.Pansies.Entities.Decode(output.ToString()); 234 | } 235 | else 236 | { 237 | return output.ToString(); 238 | } 239 | } 240 | 241 | public bool Equals(Text other) 242 | { 243 | return other != null && (Object == other.Object && ForegroundColor == other.ForegroundColor && BackgroundColor == other.BackgroundColor); 244 | } 245 | 246 | 247 | public virtual string ToPsMetadata() 248 | { 249 | var objectString = string.Empty; 250 | if (Object is ScriptBlock script) 251 | { 252 | objectString = "(ScriptBlock '" + script.ToString().Replace("\'", "\'\'") + "')"; 253 | } 254 | else 255 | { 256 | objectString = "\'" + Object.ToString().Replace("\'", "\'\'") + "\'"; 257 | } 258 | 259 | return (ForegroundColor != null ? "\nForegroundColor='" + ForegroundColor.ToString() + "'" : "") + 260 | (BackgroundColor != null ? "\nBackgroundColor='" + BackgroundColor.ToString() + "'" : "") + 261 | "\nSeparator='" + Separator + "'" + 262 | "\nClear=" + (Clear ? 1 : 0) + 263 | "\nEntities=" + (Entities ? 1 : 0) + 264 | "\nPersist=" + (PersistentColor ? 1 : 0) + 265 | "\nObject=" + objectString; 266 | } 267 | 268 | public virtual void FromPsMetadata(string metadata) 269 | { 270 | var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); 271 | var languageMode = ps.Runspace.SessionStateProxy.LanguageMode; 272 | Hashtable data; 273 | try 274 | { 275 | ps.Runspace.SessionStateProxy.LanguageMode = PSLanguageMode.RestrictedLanguage; 276 | ps.AddScript("@{ " + metadata + "}", true); 277 | data = ps.Invoke().FirstOrDefault(); 278 | 279 | FromDictionary(data); 280 | } 281 | finally 282 | { 283 | ps.Runspace.SessionStateProxy.LanguageMode = languageMode; 284 | } 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /Source/Pansies.format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | Text 9 | 10 | PoshCode.Pansies.Text 11 | Microsoft.PowerShell.Console.Text 12 | 13 | 14 | 15 | Color 16 | 17 | PoshCode.Pansies.RgbColor 18 | PoshCode.Pansies.ColorSpaces.ColorSpace 19 | 20 | 21 | 22 | 23 | 24 | Text 25 | 26 | Text 27 | 28 | 29 | 30 | 31 | 15 32 | 33 | 34 | 35 | 15 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | $_.BackgroundColor.ToVtEscapeSequence($true) + " $([char]27)[0m " + $_.BackgroundColor.ToString() 51 | 52 | 53 | 54 | 55 | $_.ForegroundColor.ToVtEscapeSequence($true) + " $([char]27)[0m " + $_.ForegroundColor.ToString() 56 | 57 | 58 | 59 | 60 | ("$(if ($_.Object -is [ScriptBlock]) { "{{ {0} }}" } else { "{0}" })" -f ([System.Management.Automation.LanguagePrimitives]::ConvertTo($_.Object, [string]).Trim())) + "$([char]27)[0m" 61 | 62 | 63 | 64 | 65 | $_.ToString() + "$([char]27)[0m" 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Color 75 | 76 | Color 77 | 78 | 79 | 80 | 81 | 14 82 | 83 | 84 | 85 | 9 86 | Left 87 | 88 | 89 | 90 | 20 91 | Left 92 | 93 | 94 | 95 | Left 96 | 97 | 25 98 | 99 | 100 | 11 101 | Left 102 | 103 | 104 | 105 | 12 106 | Left 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | ([PoshCode.Pansies.RgbColor]$_).ToVtEscapeSequence($true) + " $([char]27)[0m " + 116 | $(if ($_ -is [PoshCode.Pansies.RgbColor]) { $_.Mode } else { $_.GetType().Name }) 117 | 118 | 119 | 120 | "#{0:X6}" -f ([PoshCode.Pansies.RgbColor]$_).RGB 121 | 122 | 123 | ([PoshCode.Pansies.RgbColor]$_).X11ColorName 124 | 125 | 126 | ([PoshCode.Pansies.ColorSpaces.ColorSpace]$_).ToString() 127 | 128 | 129 | ([PoshCode.Pansies.RgbColor]$_).XTerm256Index 130 | 131 | 132 | ([PoshCode.Pansies.RgbColor]$_).ConsoleColor 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | Color 141 | 142 | Color 143 | 144 | 145 | 146 | 147 | 148 | 149 | ([PoshCode.Pansies.RgbColor]$_).ToVtEscapeSequence($true) + " $([char]27)[0m" + ("#{0:X6}" -f ([PoshCode.Pansies.RgbColor]$_).RGB) 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /Source/Pansies.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | 3 | # Script module or binary module file associated with this manifest. 4 | RootModule = 'Pansies.psm1' 5 | 6 | # Version number of this module. 7 | ModuleVersion = '2.11.0' 8 | 9 | # Supported PSEditions 10 | # CompatiblePSEditions = @() 11 | 12 | # ID used to uniquely identify this module 13 | GUID = '6c376de1-1baf-4d52-9666-d46f6933bc16' 14 | 15 | # Author of this module 16 | Author = 'Joel Bennett' 17 | 18 | # Company or vendor of this module 19 | CompanyName = 'HuddledMasses.org' 20 | 21 | # Copyright statement for this module 22 | Copyright = '(c) 2017 Joel Bennett. All rights reserved.' 23 | 24 | # Description of the functionality provided by this module 25 | Description = 'A PowerShell module for handling color and cursor positioning via ANSI escape sequences' 26 | 27 | # Assemblies that must be loaded prior to importing this module 28 | # RequiredAssemblies = 29 | 30 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 31 | # ScriptsToProcess = @() 32 | 33 | # Type files (.ps1xml) to be loaded when importing this module 34 | # TypesToProcess = @() 35 | 36 | # Format files (.ps1xml) to be loaded when importing this module 37 | FormatsToProcess = @("Pansies.format.ps1xml") 38 | 39 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 40 | NestedModules = @( "lib/Pansies.dll" ) 41 | 42 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 43 | FunctionsToExport = @() 44 | 45 | # A default Prefix for for Cmdlets to export 46 | # DefaultCommandPrefix = "Pansies" 47 | 48 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 49 | CmdletsToExport = @('New-Text', 'New-Hyperlink', 'Write-Host', 'Get-Gradient', 'Get-Complement', 'Get-ColorWheel') 50 | 51 | # Variables to export from this module 52 | VariablesToExport = 'RgbColorCompleter' 53 | 54 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 55 | AliasesToExport = 'Text', 'Url' 56 | 57 | # List of all files packaged with this module 58 | # FileList = @() 59 | 60 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 61 | PrivateData = @{ 62 | PSData = @{ 63 | # ModuleBuilder will set the pre-release value 64 | Prerelease = "dev" 65 | 66 | # Tags applied to this module. These help with module discovery in online galleries. 67 | Tags = @("ANSI", "EscapeSequences", "VirtualTerminal", "Color") 68 | 69 | # A URL to the license for this module. 70 | LicenseUri = 'https://github.com/PoshCode/Pansies/blob/master/LICENSE' 71 | 72 | # A URL to the main website for this project. 73 | ProjectUri = 'https://github.com/PoshCode/Pansies' 74 | 75 | # A URL to an icon representing this module. 76 | IconUri = 'https://github.com/PoshCode/Pansies/blob/resources/Pansies_32.gif?raw=true' 77 | 78 | # ReleaseNotes of this module 79 | ReleaseNotes = ' 80 | 2.11.0 Added a ColorCompleterAttribute (on PowerShell 7 only) 81 | On Windows PowerShell, you can still use [ArgumentCompleter([PoshCode.Pansies.Palettes.X11Palette])] 82 | Additionally, on module load, will register the ArgumentCompleter for all commands with RGBColor parameters 83 | 84 | Updated the NerdFont characters and code points to deal with the migration of the MDI characters in 2.3.0+ 85 | ' 86 | 87 | } # End of PSData hashtable 88 | 89 | } # End of PrivateData hashtable 90 | 91 | # HelpInfo URI of this module 92 | # HelpInfoURI = '' 93 | 94 | # Minimum version of the Windows PowerShell engine required by this module 95 | PowerShellVersion = '5.1' 96 | CompatiblePSEditions = @('Core','Desktop') 97 | 98 | } 99 | 100 | -------------------------------------------------------------------------------- /Source/Pansies.psm1: -------------------------------------------------------------------------------- 1 | Import-Module $PSScriptRoot\lib\Pansies.dll 2 | 3 | if(-not $IsLinux) { 4 | [PoshCode.Pansies.Console.WindowsHelper]::EnableVirtualTerminalProcessing() 5 | } 6 | 7 | # dot source the functions 8 | (Join-Path $PSScriptRoot Private\*.ps1 -Resolve -ErrorAction SilentlyContinue).ForEach{ . $_ } 9 | (Join-Path $PSScriptRoot Public\*.ps1 -Resolve).ForEach{ . $_ } 10 | -------------------------------------------------------------------------------- /Source/Private/_init.ps1: -------------------------------------------------------------------------------- 1 | using namespace PoshCode.Pansies 2 | using namespace PoshCode.Pansies.Palettes 3 | using namespace System.Collections.Generic 4 | using namespace System.Collections 5 | using namespace ColorMine.ColorSpaces 6 | using namespace System.Management.Automation 7 | using namespace System.Management.Automation.Language 8 | 9 | # On first import, if HostPreference doesn't exist, set it and strongly type it 10 | if (!(Test-Path Variable:HostPreference) -or $null -eq $HostPreference) { 11 | [System.Management.Automation.ActionPreference]$global:HostPreference = "Continue" 12 | } 13 | 14 | Set-Variable HostPreference -Description "Dictates the action taken when a host message is delivered" -Visibility Public -Scope Global 15 | 16 | if(-not $IsLinux -and -not $IsMacOS) { 17 | [PoshCode.Pansies.NativeMethods]::EnableVirtualTerminalProcessing() 18 | } 19 | 20 | if(Get-Command Add-MetadataConverter -ErrorAction Ignore) { 21 | Add-MetadataConverter @{ 22 | RgbColor = { [PoshCode.Pansies.RgbColor]$args[0] } 23 | [PoshCode.Pansies.RgbColor] = { "RgbColor '$_'" } 24 | } 25 | } 26 | 27 | $Accelerators = @{ 28 | "RGBColor" = [PoshCode.Pansies.RgbColor] 29 | "Entities" = [PoshCode.Pansies.Entities] 30 | } 31 | 32 | # IArgumentCompleterFactory only available on PS7+ 33 | if ("System.Management.Automation.IArgumentCompleterFactory" -as [type]) { 34 | Add-Type @" 35 | using System.Management.Automation; 36 | using PoshCode.Pansies.Palettes; 37 | namespace PoshCode.Pansies { 38 | public class ColorCompleterAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory { 39 | public ColorCompleterAttribute(){} 40 | public IArgumentCompleter Create() { 41 | return new X11Palette(); 42 | } 43 | } 44 | } 45 | "@ -ReferencedAssemblies ([psobject].Assembly), ([PoshCode.Pansies.RgbColor].Assembly), "netstandard" -CompilerOptions "-NoWarn:1701" 46 | 47 | $Accelerators["ColorCompleterAttribute"] = [PoshCode.Pansies.ColorCompleterAttribute] 48 | 49 | } 50 | 51 | $xlr8r = [psobject].assembly.gettype("System.Management.Automation.TypeAccelerators") 52 | $Accelerators.GetEnumerator().ForEach({ 53 | $Name = $_.Key 54 | $Type = $_.Value 55 | if ($xlr8r::AddReplace) { 56 | $xlr8r::AddReplace( $Name, $Type) 57 | } else { 58 | $null = $xlr8r::Remove( $Name ) 59 | $xlr8r::Add( $Name, $Type) 60 | } 61 | trap [System.Management.Automation.MethodInvocationException] { 62 | if ($xlr8r::get.keys -contains $Name) { 63 | if ($xlr8r::get[$Name] -ne $Type) { 64 | Write-Error "Cannot add accelerator [$Name] for [$($Type.FullName)]n [$Name] is already defined as [$($xlr8r::get[$Name].FullName)]" 65 | } 66 | Continue; 67 | } 68 | throw 69 | } 70 | }) 71 | 72 | $script:X11Palette = [X11Palette]::new() 73 | $RgbColorCompleter = { 74 | param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 75 | $script:X11Palette.CompleteArgument($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) 76 | } 77 | 78 | $global:PansiesColorCompleterRegistration = Register-EngineEvent -SourceIdentifier PowerShell.OnIdle { 79 | foreach ($command in Get-Command -ParameterType RgbColor) { 80 | foreach ($parameter in $command.Parameters.Values.Where{ $_.ParameterType -eq [RgbColor] }) { 81 | Register-ArgumentCompleter -CommandName $command.Name -ParameterName $parameter.Name -ScriptBlock $RgbColorCompleter 82 | } 83 | } 84 | Stop-Job $global:PansiesColorCompleterRegistration # This removes the event 85 | Remove-Variable PansiesColorCompleterRegistration -Scope global 86 | } 87 | 88 | Export-ModuleMember -Variable RgbColorCompleter -Function *-* -Cmdlet * -Alias * 89 | -------------------------------------------------------------------------------- /build.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleManifest = "./Source/Pansies.psd1" 3 | # The rest of the paths are relative to the manifest 4 | OutputDirectory = ".." 5 | VersionedOutputDirectory = $true 6 | } 7 | --------------------------------------------------------------------------------