diff --git a/.github/workflows/build-test-publish.yml b/.github/workflows/build-test-publish.yml index 0ab41f7b..fd3f4b8b 100644 --- a/.github/workflows/build-test-publish.yml +++ b/.github/workflows/build-test-publish.yml @@ -1,11 +1,11 @@ name: Publish to PSGallery env: - PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} + PSGALLERY_API_KEY: ${{ secrets.PSGALLERY_API_KEY }} on: push: branches: - - main - - prerelease + - main + - prerelease permissions: contents: write @@ -14,56 +14,102 @@ jobs: publish-to-psgallery: name: Publish runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/main' && github.repository_owner == 'ShaunLawrie' steps: - name: Check out repository code uses: actions/checkout@v3 - name: Version Bump shell: pwsh + env: + GH_TOKEN: ${{ github.token }} run: | $ErrorActionPreference = "Stop" & .\PwshSpectreConsole\Build.ps1 $env:PSModulePath = @($env:PSModulePath, ".\PwshSpectreConsole\") -join ":" + Invoke-Pester -CI -ExcludeTag "ExcludeCI" $version = Get-Module PwshSpectreConsole -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version if($null -eq $version) { throw "Failed to load version" } - $newVersion = [version]::new($version.Major, $version.Minor + 1, 0) - Write-Host "Bumping version from $version to $newVersion" - Update-ModuleManifest -Path .\PwshSpectreConsole\PwshSpectreConsole.psd1 -ModuleVersion $newVersion - git config --global user.name 'Shaun Lawrie (via GitHub Actions)' - git config --global user.email 'shaun.r.lawrie@gmail.com' - git add PwshSpectreConsole/PwshSpectreConsole.psd1 - git commit -m "[skip ci] Bump version to $newVersion" - git push + $onlineVersion = Find-Module -Name PwshSpectreConsole -RequiredVersion $version -ErrorAction SilentlyContinue + $newVersion = [version]::new($version.Major, $version.Minor, $version.Build) + if($null -eq $onlineVersion) { + Write-Warning "Online version doesn't exist, this version $newVersion will be published without a version bump" + } else { + $newVersion = [version]::new($version.Major, $version.Minor, $version.Build + 1) + Write-Host "Bumping version from $version to $newVersion" + Update-ModuleManifest -Path .\PwshSpectreConsole\PwshSpectreConsole.psd1 -ModuleVersion $newVersion + git config --global user.name 'Shaun Lawrie (via GitHub Actions)' + git config --global user.email 'shaun.r.lawrie@gmail.com' + git add PwshSpectreConsole/PwshSpectreConsole.psd1 + git commit -m "[skip ci] Bump version to $newVersion" + git push + } Import-Module .\PwshSpectreConsole\PwshSpectreConsole.psd1 -Force Publish-Module -Name PwshSpectreConsole -Exclude "Build.ps1" -NugetApiKey $env:PSGALLERY_API_KEY - + gh release create "v$newVersion" --target main --generate-notes + - name: Upload Snapshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: Snapshots + path: PwshSpectreConsole.Tests/@snapshots/*.txt + publish-prerelease-to-psgallery: name: Publish Prerelease runs-on: ubuntu-latest - if: github.ref == 'refs/heads/prerelease' + if: github.ref == 'refs/heads/prerelease' && github.repository_owner == 'ShaunLawrie' steps: - name: Check out repository code uses: actions/checkout@v3 - name: Version Bump and Publish shell: pwsh + env: + GH_TOKEN: ${{ github.token }} run: | $ErrorActionPreference = "Stop" & ./PwshSpectreConsole/Build.ps1 $env:PSModulePath = @($env:PSModulePath, ".\PwshSpectreConsole\") -join ":" + Invoke-Pester -CI -ExcludeTag "ExcludeCI" $version = Get-Module PwshSpectreConsole -ListAvailable | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version if($null -eq $version) { throw "Failed to load version" } - $newVersion = [version]::new($version.Major, $version.Minor, $version.Build + 1) - Write-Host "Bumping version from $version to $newVersion" - Update-ModuleManifest -Path .\PwshSpectreConsole\PwshSpectreConsole.psd1 -ModuleVersion $newVersion - git config --global user.name 'Shaun Lawrie (via GitHub Actions)' - git config --global user.email 'shaun.r.lawrie@gmail.com' - git add PwshSpectreConsole/PwshSpectreConsole.psd1 - git commit -m "[skip ci] Bump version to $newVersion" - git push + $onlineVersions = Find-Module -Name PwshSpectreConsole -AllowPrerelease -AllVersions + + $latestStableVersion = $onlineVersions | Where-Object { $_.Version -notlike "*prerelease*" } | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version + $latestStableVersion = [version]$latestStableVersion + $latestPrereleaseVersion = $onlineVersions | Where-Object { $_.Version -like "*prerelease*" } | Sort-Object -Property Version -Descending | Select-Object -First 1 -ExpandProperty Version + $latestPrereleaseTag = $latestPrereleaseVersion.Split("-prerelease")[1] # format is like -prerelease6, output here is just 6 + $latestPrereleaseVersion = [version]$latestPrereleaseVersion.Split("-prerelease")[0] + + # Generate a new prerelease name, psgallery only allows characters 'a-zA-Z0-9' and a hyphen ('-') at the beginning of the prerelease string + $newPrereleaseTag = "prerelease" + (([int]$latestPrereleaseTag) + 1) + + # Prerelease will always be at least one minor version above the latest published stable version so when it's merged to main the minor version will get bumped + # To bump a major version the manifest would be edited manually to vnext.0.0 before merging to main + $newVersion = [version]::new($latestStableVersion.Major, $latestStableVersion.Minor + 1, 0) + + if($newVersion -eq $oldVersion) { + Write-Host "Version is not being bumped in prerelease" + } else { + Write-Host "Bumping version from $version to $newVersion" + Update-ModuleManifest -Path .\PwshSpectreConsole\PwshSpectreConsole.psd1 -ModuleVersion $newVersion + git config --global user.name 'Shaun Lawrie (via GitHub Actions)' + git config --global user.email 'shaun.r.lawrie@gmail.com' + git add PwshSpectreConsole/PwshSpectreConsole.psd1 + git commit -m "[skip ci] Bump version to $newVersion" + git push + } # Mark as prerelease - Update-ModuleManifest -Path .\PwshSpectreConsole\PwshSpectreConsole.psd1 -PrivateData @{ Prerelease = 'prerelease' } + Update-ModuleManifest -Path .\PwshSpectreConsole\PwshSpectreConsole.psd1 -PrivateData @{ Prerelease = "$newPrereleaseTag" } # Publish pre-release version Import-Module .\PwshSpectreConsole\PwshSpectreConsole.psd1 -Force Publish-Module -Name PwshSpectreConsole -Exclude "Build.ps1" -NugetApiKey $env:PSGALLERY_API_KEY -AllowPrerelease + + # Create a gh release for it + gh release create "v$newVersion-$newPrereleaseTag" --target prerelease --generate-notes --prerelease + - name: Upload Snapshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: Snapshots + path: PwshSpectreConsole.Tests/@snapshots/*.txt diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 42bb7a39..464e3f7e 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -12,6 +12,7 @@ jobs: deploy: runs-on: ubuntu-latest name: Deploy + if: github.repository_owner == 'ShaunLawrie' steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.github/workflows/unit-test-only.yml b/.github/workflows/unit-test-only.yml new file mode 100644 index 00000000..9df8d098 --- /dev/null +++ b/.github/workflows/unit-test-only.yml @@ -0,0 +1,30 @@ +name: Run Unit Tests +on: + push: + +permissions: + contents: write + +jobs: + unit-test: + name: Unit Test + runs-on: ubuntu-latest + if: github.repository_owner != 'ShaunLawrie' || !(github.ref == 'refs/heads/main' && github.ref == 'refs/heads/prerelease') + steps: + - name: Check out repository code + uses: actions/checkout@v4 + - name: Unit Test + shell: pwsh + run: | + $ErrorActionPreference = "Stop" + & .\PwshSpectreConsole\Build.ps1 + $env:PSModulePath = @($env:PSModulePath, ".\PwshSpectreConsole\") -join ":" + $PSVersionTable | Out-Host + Get-Module Pester -ListAvailable | Out-Host + Invoke-Pester -CI -ExcludeTag "ExcludeCI" + - name: Upload Snapshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: Snapshots + path: PwshSpectreConsole.Tests/@snapshots/*.txt diff --git a/.gitignore b/.gitignore index d19a6b65..f0ca34b6 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ pnpm-debug.log* # Exclude packages folder PwshSpectreConsole/packages +PwshSpectreConsole.Tests/packages # Exclude working folder Working/ diff --git a/PwshSpectreConsole.Docs/UpdateDocs.ps1 b/PwshSpectreConsole.Docs/UpdateDocs.ps1 index 38ca392d..c9881188 100644 --- a/PwshSpectreConsole.Docs/UpdateDocs.ps1 +++ b/PwshSpectreConsole.Docs/UpdateDocs.ps1 @@ -16,7 +16,7 @@ if($null -eq $module) { $module | Save-MarkdownHelp -OutputPath "$PSScriptRoot\src\content\docs\reference\" -IncludeYamlHeader -YamlHeaderInformationType Metadata -ExcludeFile "*.gif", "*.png" -$new = @("New-SpectreChartItem.md", "Get-SpectreDemoColors.md", "Get-SpectreDemoEmoji.md") +$new = @("New-SpectreChartItem.md", "Get-SpectreDemoColors.md", "Get-SpectreDemoEmoji.md", "Format-SpectreJson.md") $experimental = @("Get-SpectreImageExperimental.md", "Invoke-SpectreScriptBlockQuietly.md") $newTag = @" @@ -46,7 +46,7 @@ $groups = @( $docs = Get-ChildItem "$PSScriptRoot\src\content\docs\reference\" -Filter "*.md" -Recurse foreach($doc in $docs) { - if($remove -contains $doc.Name) { + if($remove -contains $doc.Name -or $doc.Name -notlike "*-*") { Remove-Item $doc.FullName continue } @@ -68,13 +68,13 @@ foreach($doc in $docs) { New-Item -ItemType Directory -Path "$PSScriptRoot\src\content\docs\reference\$($group.Name)" -Force | Out-Null $content = Get-Content $doc.FullName -Raw Remove-Item $doc.FullName - $content = $content -replace '```PowerShell', '```powershell' -replace '(?m)^.+\n^[\-]{10,99}', '' + $content = $content -replace '```PowerShell', '```powershell' -replace '(?m)^.+\n^[\-]{10,99}', '' -replace "`r", "" if($experimental -contains $doc.Name) { $content = $content -replace '(?s)^---', "---`n$experimentalTag" } elseif($new -contains $doc.Name) { $content = $content -replace '(?s)^---', "---`n$newTag" } - $content | Out-File $outLocation + $content | Out-File $outLocation -NoNewline } # Build the docs site diff --git a/PwshSpectreConsole.Docs/public/json.png b/PwshSpectreConsole.Docs/public/json.png new file mode 100644 index 00000000..b474ce19 Binary files /dev/null and b/PwshSpectreConsole.Docs/public/json.png differ diff --git a/PwshSpectreConsole.Docs/public/table.png b/PwshSpectreConsole.Docs/public/table.png index 16dd71e8..3ad7ef18 100644 Binary files a/PwshSpectreConsole.Docs/public/table.png and b/PwshSpectreConsole.Docs/public/table.png differ diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Config/Set-SpectreColors.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Config/Set-SpectreColors.md index 9bf3bb28..137fbe56 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Config/Set-SpectreColors.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Config/Set-SpectreColors.md @@ -4,11 +4,18 @@ title: Set-SpectreColors + + + + ### Synopsis Sets the accent color and default value color for Spectre Console. + + --- + ### Description This function sets the accent color and default value color for Spectre Console. The accent color is used for highlighting important information, while the default value color is used for displaying default values. @@ -19,8 +26,11 @@ An example of the accent color is the highlight used in `Read-SpectreSelection`: An example of the default value color is the default value displayed in `Read-SpectreText`: ![Default value color example](/defaultcolor.png) + + --- + ### Examples Sets the accent color to Red and the default value color to Yellow. @@ -33,27 +43,47 @@ Sets the accent color to Green and keeps the default value color as Grey. Set-SpectreColors -AccentColor Green ``` + --- + ### Parameters #### **AccentColor** + The accent color to set. Must be a valid Spectre Console color name. Defaults to "Blue". + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **DefaultValueColor** + The default value color to set. Must be a valid Spectre Console color name. Defaults to "Grey". + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + + + --- + ### Syntax ```powershell Set-SpectreColors [[-AccentColor] ] [[-DefaultValueColor] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoColors.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoColors.md index f41f3eef..3f208742 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoColors.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoColors.md @@ -8,19 +8,29 @@ title: Get-SpectreDemoColors + + + + ### Synopsis Retrieves a list of Spectre Console colors and displays them with their corresponding markup. ![Spectre color demo](/colors.png) + + --- + ### Description The Get-SpectreDemoColors function retrieves a list of Spectre Console colors and displays them with their corresponding markup. It also provides information on how to use the colors as parameters for commands or in Spectre Console markup. + + --- + ### Examples Displays a list of Spectre Console colors and their corresponding markup. @@ -28,10 +38,11 @@ Displays a list of Spectre Console colors and their corresponding markup. PS> Get-SpectreDemoColors ``` + --- + ### Syntax ```powershell Get-SpectreDemoColors [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoEmoji.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoEmoji.md index e5f2d514..9bd9bd7d 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoEmoji.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Get-SpectreDemoEmoji.md @@ -8,18 +8,28 @@ title: Get-SpectreDemoEmoji + + + + ### Synopsis Retrieves a collection of emojis available in Spectre Console. ![Example emojis](/emoji.png) + + --- + ### Description The Get-SpectreDemoEmoji function retrieves a collection of emojis available in Spectre Console. It displays the general emojis, faces, and provides information on how to use emojis in Spectre Console markup. + + --- + ### Examples Retrieves and displays the collection of emojis available in Spectre Console. @@ -27,17 +37,21 @@ Retrieves and displays the collection of emojis available in Spectre Console. Get-SpectreDemoEmoji ``` + --- + ### Notes Emoji support is dependent on the operating system, terminal, and font support. For more information on Spectre Console markup and emojis, refer to the following links: - Spectre Console Markup: https://spectreconsole.net/markup - Spectre Console Emojis: https://spectreconsole.net/appendix/emojis + + --- + ### Syntax ```powershell Get-SpectreDemoEmoji [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Start-SpectreDemo.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Start-SpectreDemo.md index b33391ce..19b87490 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Start-SpectreDemo.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Demo/Start-SpectreDemo.md @@ -4,18 +4,28 @@ title: Start-SpectreDemo + + + + ### Synopsis Runs a demo of the PwshSpectreConsole module. ![Spectre demo animation](/demo.gif) + + --- + ### Description This function runs a demo of the PwshSpectreConsole module, showcasing some of its features. It displays various examples of Spectre.Console functionality wrapped in PowerShell functions, such as text entry, select lists, multi-select lists, and panels. + + --- + ### Examples Runs the PwshSpectreConsole demo. @@ -23,10 +33,11 @@ Runs the PwshSpectreConsole demo. PS C:\> Start-SpectreDemo ``` + --- + ### Syntax ```powershell Start-SpectreDemo [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBarChart.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBarChart.md index edaca37c..3965f676 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBarChart.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBarChart.md @@ -4,65 +4,111 @@ title: Format-SpectreBarChart + + + + ### Synopsis Formats and displays a bar chart using the Spectre Console module. ![Example bar chart](/barchart.png) + + --- + ### Description This function takes an array of data and displays it as a bar chart using the Spectre Console module. The chart can be customized with a title and width. + + --- + ### Examples This example uses the new helper for generating chart items New-SpectreChartItem and shows the various ways of passing color values in ```powershell $data = @() $data += New-SpectreChartItem -Label "Apples" -Value 10 -Color "Green" -$data += New-SpectreChartItem -Label "Oranges" -Value 5 -Color "Orange" +$data += New-SpectreChartItem -Label "Oranges" -Value 5 -Color "DarkOrange" $data += New-SpectreChartItem -Label "Bananas" -Value 2.2 -Color "#FFFF00" Format-SpectreBarChart -Data $data -Title "Fruit Sales" -Width 50 ``` + --- + ### Parameters #### **Data** + An array of objects containing the data to be displayed in the chart. Each object should have a Label, Value, and Color property. + + + + + |Type |Required|Position|PipelineInput | |---------|--------|--------|--------------| |`[Array]`|true |1 |true (ByValue)| + + #### **Title** + The title to be displayed above the chart. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **Width** + The width of the chart in characters. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |3 |false | + + #### **HideValues** + Hides the values from being displayed on the chart. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell Format-SpectreBarChart [-Data] [[-Title] ] [[-Width] ] [-HideValues] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBreakdownChart.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBreakdownChart.md index c5719990..655466f8 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBreakdownChart.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreBreakdownChart.md @@ -4,18 +4,28 @@ title: Format-SpectreBreakdownChart + + + + ### Synopsis Formats data into a breakdown chart. ![Example breakdown chart](/breakdownchart.png) + + --- + ### Description This function takes an array of data and formats it into a breakdown chart using Spectre.Console.BreakdownChart. The chart can be customized with a specified width and color. + + --- + ### Examples This example uses the new helper for generating chart items New-SpectreChartItem and the various ways of passing color values in. @@ -28,41 +38,77 @@ $data += New-SpectreChartItem -Label "Bananas" -Value 2.2 -Color "#FFFF00" Format-SpectreBreakdownChart -Data $data -Width 50 ``` + --- + ### Parameters #### **Data** + An array of data to be formatted into a breakdown chart. + + + + + |Type |Required|Position|PipelineInput | |---------|--------|--------|--------------| |`[Array]`|true |1 |true (ByValue)| + + #### **Width** + The width of the chart. Defaults to the width of the console. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |2 |false | + + #### **HideTags** + Hides the tags on the chart. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + #### **HideTagValues** + Hides the tag values on the chart. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell Format-SpectreBreakdownChart [-Data] [[-Width] ] [-HideTags] [-HideTagValues] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreJson.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreJson.md new file mode 100644 index 00000000..2c2924ff --- /dev/null +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreJson.md @@ -0,0 +1,203 @@ +--- +sidebar: + badge: + text: New + variant: tip +title: Format-SpectreJson +--- + + + + + + + +### Synopsis +Formats an array of objects into a Spectre Console Json. +Thanks to [trackd](https://github.com/trackd) for adding this. +![Spectre json example](/json.png) + + + +--- + + +### Description + +This function takes an array of objects and converts them into Json using the Spectre Console Json Library. + + + +--- + + +### Examples +This example formats an array of objects into a table with a double border and the accent color of the script. + +```powershell +$data = @( + [pscustomobject]@{ + Name = "John" + Age = 25 + City = "New York" + IsEmployed = $true + Salary = 10 + Hobbies = @("Reading", "Swimming") + Address = @{ + Street = "123 Main St" + ZipCode = $null + } + }, + [pscustomobject]@{ + Name = "Jane" + Age = 30 + City = "Los Angeles" + IsEmployed = $false + Salary = $null + Hobbies = @("Painting", "Hiking") + Address = @{ + Street = "456 Elm St" + ZipCode = "90001" + } + } +) +Format-SpectreJson -Data $data -Title "Employee Data" -Border "Rounded" -Color "Green" +``` + + +--- + + +### Parameters +#### **Data** + +The array of objects to be formatted into Json. + + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|--------------| +|`[Object]`|true |1 |true (ByValue)| + + + +#### **Depth** + + + + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Int32]`|false |2 |false | + + + +#### **Title** + +The title of the Json. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |3 |false | + + + +#### **Border** + +The border style of the Json. Default is "Rounded". + + + +Valid Values: + +* Ascii +* Double +* Heavy +* None +* Rounded +* Square + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |4 |false | + + + +#### **Color** + +The color of the Json border. Default is the accent color of the script. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |5 |false | + + + +#### **Width** + +The width of the Json panel. + + + + + + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Int32]`|false |6 |false | + + + +#### **Height** + +The height of the Json panel. + + + + + + +|Type |Required|Position|PipelineInput| +|---------|--------|--------|-------------| +|`[Int32]`|false |7 |false | + + + +#### **Expand** + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[Switch]`|false |named |false | + + + + + +--- + + +### Syntax +```powershell +Format-SpectreJson [-Data] [[-Depth] ] [[-Title] ] [[-Border] ] [[-Color] ] [[-Width] ] [[-Height] ] [-Expand] [] +``` diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectrePanel.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectrePanel.md index 7b23cb3f..ff4955c7 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectrePanel.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectrePanel.md @@ -4,18 +4,28 @@ title: Format-SpectrePanel + + + + ### Synopsis Formats a string as a Spectre Console panel with optional title, border, and color. ![Spectre panel example](/panel.png) + + --- + ### Description This function takes a string and formats it as a Spectre Console panel with optional title, border, and color. The resulting panel can be displayed in the console using the Write-Host command. + + --- + ### Examples This example displays a panel with the title "My Panel", a rounded border, and a red border color. @@ -23,25 +33,47 @@ This example displays a panel with the title "My Panel", a rounded border, and a Format-SpectrePanel -Data "Hello, world!" -Title "My Panel" -Border "Rounded" -Color "Red" ``` + --- + ### Parameters #### **Data** + The string to be formatted as a panel. + + + + + |Type |Required|Position|PipelineInput | |----------|--------|--------|--------------| |`[String]`|true |1 |true (ByValue)| + + #### **Title** + The title to be displayed at the top of the panel. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **Border** + The type of border to be displayed around the panel. + + + Valid Values: * Ascii @@ -51,42 +83,83 @@ Valid Values: * Rounded * Square + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + #### **Expand** + Switch parameter that specifies whether the panel should be expanded to fill the available space. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + #### **Color** + The color of the panel border. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + #### **Width** + The width of the panel. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |5 |false | + + #### **Height** + The height of the panel. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |6 |false | + + + + --- + ### Syntax ```powershell Format-SpectrePanel [-Data] [[-Title] ] [[-Border] ] [-Expand] [[-Color] ] [[-Width] ] [[-Height] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTable.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTable.md index 14b5a72d..f94530c7 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTable.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTable.md @@ -4,18 +4,28 @@ title: Format-SpectreTable + + + + ### Synopsis -Formats an array of objects into a Spectre Console table. +Formats an array of objects into a Spectre Console table. Thanks to [trackd](https://github.com/trackd) and [fmotion1](https://github.com/fmotion1) for the updates to support markdown and color in the table contents. ![Example table](/table.png) + + --- + ### Description This function takes an array of objects and formats them into a table using the Spectre Console library. The table can be customized with a border style and color. + + --- + ### Examples This example formats an array of objects into a table with a double border and the accent color of the script. @@ -27,18 +37,47 @@ $data = @( Format-SpectreTable -Data $data ``` + --- + ### Parameters +#### **Property** + +The list of properties to select for the table from the input data. + + + + + + +|Type |Required|Position|PipelineInput| +|------------|--------|--------|-------------| +|`[String[]]`|false |1 |false | + + + #### **Data** + The array of objects to be formatted into a table. -|Type |Required|Position|PipelineInput | -|---------|--------|--------|--------------| -|`[Array]`|true |1 |true (ByValue)| + + + + + +|Type |Required|Position|PipelineInput | +|----------|--------|--------|--------------| +|`[Object]`|true |named |true (ByValue)| + + #### **Border** + The border style of the table. Default is "Double". + + + Valid Values: * Ascii @@ -60,42 +99,98 @@ Valid Values: * SimpleHeavy * Square + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[String]`|false |2 |false | +|`[String]`|false |named |false | + + #### **Color** + The color of the table border. Default is the accent color of the script. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[String]`|false |3 |false | +|`[String]`|false |named |false | + + #### **Width** + The width of the table. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| -|`[Int32]`|false |4 |false | +|`[Int32]`|false |named |false | + + #### **HideHeaders** + Hides the headers of the table. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + #### **Title** + The title of the table. + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |named |false | + + + +#### **AllowMarkup** + +Allow Spectre markup in the table elements e.g. [green]message[/]. + + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| -|`[String]`|false |5 |false | +|`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell -Format-SpectreTable [-Data] [[-Border] ] [[-Color] ] [[-Width] ] [-HideHeaders] [[-Title] ] [] +Format-SpectreTable [[-Property] ] -Data [-Border ] [-Color ] [-Width ] [-HideHeaders] [-Title ] [-AllowMarkup] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTree.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTree.md index 2b268d6c..d1981b6e 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTree.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/Format-SpectreTree.md @@ -4,17 +4,27 @@ title: Format-SpectreTree + + + + ### Synopsis Formats a hashtable as a tree using Spectre Console. + + --- + ### Description This function takes a hashtable and formats it as a tree using Spectre Console. The hashtable should have a 'Label' key and a 'Children' key. The 'Label' key should contain the label for the root node of the tree, and the 'Children' key should contain an array of hashtables representing the child nodes of the root node. Each child hashtable should have a 'Label' key and a 'Children' key, following the same structure as the root node. + + --- + ### Examples This example formats a hashtable as a tree with a heavy border and green color. @@ -44,16 +54,26 @@ $data = @{ Format-SpectreTree -Data $data -Border "Heavy" -Color "Green" ``` + --- + ### Parameters #### **Data** + The hashtable to format as a tree. + + + + + |Type |Required|Position|PipelineInput | |-------------|--------|--------|--------------| |`[Hashtable]`|true |1 |true (ByValue)| + + #### **Guide** Valid Values: @@ -63,21 +83,38 @@ Valid Values: * DoubleLine * Line + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **Color** + The color to use for the tree. This can be a Spectre Console color name or a hex color code. Default is the accent color defined in the script. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + + + --- + ### Syntax ```powershell Format-SpectreTree [-Data] [[-Guide] ] [[-Color] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/New-SpectreChartItem.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/New-SpectreChartItem.md index 77203c1f..8ec7461d 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/New-SpectreChartItem.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Formatting/New-SpectreChartItem.md @@ -8,17 +8,27 @@ title: New-SpectreChartItem + + + + ### Synopsis Creates a new SpectreChartItem object. + + --- + ### Description The New-SpectreChartItem function creates a new SpectreChartItem object with the specified label, value, and color for use in Format-SpectreBarChart and Format-SpectreBreakdownChart. + + --- + ### Examples > EXAMPLE 1 @@ -27,34 +37,62 @@ New-SpectreChartItem -Label "Sales" -Value 1000 -Color "green" This example creates a new SpectreChartItem object with a label of "Sales", a value of 1000, and a green color. ``` + --- + ### Parameters #### **Label** + The label for the chart item. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|true |1 |false | + + #### **Value** + The value for the chart item. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Double]`|true |2 |false | + + #### **Color** + The color for the chart item. Must be a valid Spectre color as name, hex or a Spectre.Console.Color object. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|true |3 |false | + + + + --- + ### Syntax ```powershell New-SpectreChartItem [-Label] [-Value] [-Color] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImage.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImage.md index 68b92cc5..5e81074e 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImage.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImage.md @@ -4,17 +4,27 @@ title: Get-SpectreImage + + + + ### Synopsis Displays an image in the console using Spectre.Console.CanvasImage. + + --- + ### Description Displays an image in the console using Spectre.Console.CanvasImage. The image can be resized to a maximum width if desired. + + --- + ### Examples Displays the image located at "C:\Images\myimage.png" with a maximum width of 80 characters. @@ -22,27 +32,47 @@ Displays the image located at "C:\Images\myimage.png" with a maximum width of 80 Get-SpectreImage -ImagePath "C:\Images\myimage.png" -MaxWidth 80 ``` + --- + ### Parameters #### **ImagePath** + The path to the image file to be displayed. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **MaxWidth** + The maximum width of the image. If not specified, the image will be displayed at its original size. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |2 |false | + + + + --- + ### Syntax ```powershell Get-SpectreImage [[-ImagePath] ] [[-MaxWidth] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImageExperimental.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImageExperimental.md index 58802f3f..9f9880e1 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImageExperimental.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Images/Get-SpectreImageExperimental.md @@ -8,20 +8,30 @@ title: Get-SpectreImageExperimental + + + + ### Synopsis Displays an image in the console using block characters and ANSI escape codes. :::caution This is experimental. ::: + + --- + ### Description This function loads an image from a file and displays it in the console using block characters and ANSI escape codes. The image is scaled to fit within the specified maximum width while maintaining its aspect ratio. If the image is an animated GIF, each frame is displayed in sequence with a configurable delay between frames. + + --- + ### Examples Displays the image "MyImage.png" in the console with a maximum width of 80 characters. @@ -34,44 +44,80 @@ Displays the animated GIF "MyAnimation.gif" in the console with a maximum width PS C:\> Get-SpectreImageExperimental -ImagePath "C:\Images\MyAnimation.gif" -MaxWidth 80 -Repeat ``` + --- + ### Parameters #### **ImagePath** + The path to the image file to display. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **Width** + The width of the image in characters. The image is scaled to fit within this width while maintaining its aspect ratio. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |2 |false | + + #### **LoopCount** + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |3 |false | + + #### **Resampler** + The resampling algorithm to use when scaling the image. Valid values are "Bicubic" and "NearestNeighbor". The default value is "Bicubic". + + + Valid Values: * Bicubic * NearestNeighbor + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + + + --- + ### Syntax ```powershell Get-SpectreImageExperimental [[-ImagePath] ] [[-Width] ] [[-LoopCount] ] [[-Resampler] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Add-SpectreJob.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Add-SpectreJob.md index b25a583d..780f3884 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Add-SpectreJob.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Add-SpectreJob.md @@ -4,20 +4,30 @@ title: Add-SpectreJob + + + + ### Synopsis Adds a Spectre job to a list of jobs. :::note This is only used inside `Invoke-SpectreCommandWithProgress` where the Spectre ProgressContext object is exposed. ::: + + --- + ### Description This function adds a Spectre job to the list of jobs you want to wait for with Wait-SpectreJobs. + + --- + ### Examples This is an example of how to use the Add-SpectreJob function to add two jobs to a jobs list that can be passed to Wait-SpectreJobs. @@ -33,35 +43,63 @@ Invoke-SpectreCommandWithProgress -Title "Waiting" -ScriptBlock { } ``` + --- + ### Parameters #### **Context** + The Spectre context to add the job to. The context object is only available inside Wait-SpectreJobs. [https://spectreconsole.net/api/spectre.console/progresscontext/](https://spectreconsole.net/api/spectre.console/progresscontext/) + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Object]`|true |1 |false | + + #### **JobName** + The name of the job to add. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|true |2 |false | + + #### **Job** + The PowerShell job to add to the context. + + + + + |Type |Required|Position|PipelineInput| |-------|--------|--------|-------------| |`[Job]`|true |3 |false | + + + + --- + ### Syntax ```powershell Add-SpectreJob [-Context] [-JobName] [-Job] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithProgress.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithProgress.md index 11f82822..dd30ac75 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithProgress.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithProgress.md @@ -4,18 +4,28 @@ title: Invoke-SpectreCommandWithProgress + + + + ### Synopsis Invokes a Spectre command with a progress bar. + + --- + ### Description This function takes a script block as a parameter and executes it while displaying a progress bar. The context and task objects are defined at [https://spectreconsole.net/api/spectre.console/progresscontext/](https://spectreconsole.net/api/spectre.console/progresscontext/). The context requires at least one task to be added for progress to be displayed. The task object is used to update the progress bar by calling the Increment() method or other methods defined in Spectre console [https://spectreconsole.net/api/spectre.console/progresstask/](https://spectreconsole.net/api/spectre.console/progresstask/). + + --- + ### Examples This example will display a progress bar while the script block is executing. @@ -37,20 +47,32 @@ Invoke-SpectreCommandWithProgress -ScriptBlock { } ``` + --- + ### Parameters #### **ScriptBlock** + The script block to execute. + + + + + |Type |Required|Position|PipelineInput| |---------------|--------|--------|-------------| |`[ScriptBlock]`|true |1 |false | + + + + --- + ### Syntax ```powershell Invoke-SpectreCommandWithProgress [-ScriptBlock] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithStatus.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithStatus.md index 4c2df885..3d180628 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithStatus.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreCommandWithStatus.md @@ -4,17 +4,27 @@ title: Invoke-SpectreCommandWithStatus + + + + ### Synopsis Invokes a script block with a Spectre status spinner. + + --- + ### Description This function starts a Spectre status spinner with the specified title and spinner type, and invokes the specified script block. The spinner will continue to spin until the script block completes. + + --- + ### Examples Starts a Spectre status spinner with the "dots" spinner type, a yellow color, and the title "Waiting for process to complete". The spinner will continue to spin for 5 seconds. @@ -22,18 +32,32 @@ Starts a Spectre status spinner with the "dots" spinner type, a yellow color, an Invoke-SpectreCommandWithStatus -ScriptBlock { Start-Sleep -Seconds 5 } -Spinner dots -Title "Waiting for process to complete" -Color yellow ``` + --- + ### Parameters #### **ScriptBlock** + The script block to invoke. + + + + + |Type |Required|Position|PipelineInput| |---------------|--------|--------|-------------| |`[ScriptBlock]`|true |1 |false | + + #### **Spinner** + The type of spinner to display. Valid values are "dots", "dots2", "dots3", "dots4", "dots5", "dots6", "dots7", "dots8", "dots9", "dots10", "dots11", "dots12", "line", "line2", "pipe", "simpleDots", "simpleDotsScrolling", "star", "star2", "flip", "hamburger", "growVertical", "growHorizontal", "balloon", "balloon2", "noise", "bounce", "boxBounce", "boxBounce2", "triangle", "arc", "circle", "squareCorners", "circleQuarters", "circleHalves", "squish", "toggle", "toggle2", "toggle3", "toggle4", "toggle5", "toggle6", "toggle7", "toggle8", "toggle9", "toggle10", "toggle11", "toggle12", "toggle13", "arrow", "arrow2", "arrow3", "bouncingBar", "bouncingBall", "smiley", "monkey", "hearts", "clock", "earth", "moon", "runner", "pong", "shark", "dqpb", "weather", "christmas", "grenade", "point", "layer", "betaWave", "pulse", "noise2", "gradient", "christmasTree", "santa", "box", "simpleDotsDown", "ballotBox", "checkbox", "radioButton", "spinner", "lineSpinner", "lineSpinner2", "pipeSpinner", "simpleDotsSpinner", "ballSpinner", "balloonSpinner", "noiseSpinner", "bouncingBarSpinner", "smileySpinner", "monkeySpinner", "heartsSpinner", "clockSpinner", "earthSpinner", "moonSpinner", "auto", "random". + + + Valid Values: * Aesthetic @@ -112,28 +136,53 @@ Valid Values: * Triangle * Weather + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **Title** + The title to display above the spinner. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|true |3 |false | + + #### **Color** + The color of the spinner. Valid values can be found with Get-SpectreDemoColors. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + + + --- + ### Syntax ```powershell Invoke-SpectreCommandWithStatus [-ScriptBlock] [[-Spinner] ] [-Title] [[-Color] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreScriptBlockQuietly.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreScriptBlockQuietly.md index ce456263..d26d8467 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreScriptBlockQuietly.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Invoke-SpectreScriptBlockQuietly.md @@ -8,6 +8,10 @@ title: Invoke-SpectreScriptBlockQuietly + + + + ### Synopsis This is a test function for invoking a script block in a background job inside Invoke-SpectreCommandWithProgress to help with https://github.com/ShaunLawrie/PwshSpectreConsole/issues/7 Some commands cause output that interferes with the progress bar, this function is an attempt to suppress that output when all other attempts have failed. @@ -15,14 +19,20 @@ Some commands cause output that interferes with the progress bar, this function This is experimental. ::: + + --- + ### Description This function invokes a script block in a background job and returns the output. It also provides an option to suppress the output even more if there is garbage being printed to stderr if using Level = Quieter. + + --- + ### Examples This example invokes the git command in a background job and suppresses the output completely even though it would have written to stderr and thrown an error. @@ -35,31 +45,54 @@ Invoke-SpectreScriptBlockQuietly -Level Quieter -Command { } ``` + --- + ### Parameters #### **Command** + The script block to be invoked. + + + + + |Type |Required|Position|PipelineInput| |---------------|--------|--------|-------------| |`[ScriptBlock]`|false |1 |false | + + #### **Level** + Suppresses the output by varying amounts. + + + Valid Values: * Quiet * Quieter + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + + + --- + ### Syntax ```powershell Invoke-SpectreScriptBlockQuietly [[-Command] ] [[-Level] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Wait-SpectreJobs.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Wait-SpectreJobs.md index b7f4f6e3..1aaca28b 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Wait-SpectreJobs.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Progress/Wait-SpectreJobs.md @@ -4,21 +4,31 @@ title: Wait-SpectreJobs + + + + ### Synopsis Waits for Spectre jobs to complete. :::note This is only used inside `Invoke-SpectreCommandWithProgress` where the Spectre ProgressContext object is exposed. ::: + + --- + ### Description This function waits for Spectre jobs to complete by checking the progress of each job and updating the corresponding task value. Adapted from https://key2consulting.com/powershell-how-to-display-job-progress/ + + --- + ### Examples Waits for two jobs to complete @@ -34,35 +44,63 @@ Invoke-SpectreCommandWithProgress -Title "Waiting" -ScriptBlock { } ``` + --- + ### Parameters #### **Context** + The Spectre progress context object. [https://spectreconsole.net/api/spectre.console/progresscontext/](https://spectreconsole.net/api/spectre.console/progresscontext/) + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Object]`|true |1 |false | + + #### **Jobs** + An array of Spectre jobs which are decorated PowerShell jobs. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Array]`|true |2 |false | + + #### **TimeoutSeconds** + The maximum number of seconds to wait for the jobs to complete. Defaults to 60 seconds. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |3 |false | + + + + --- + ### Syntax ```powershell Wait-SpectreJobs [-Context] [-Jobs] [[-TimeoutSeconds] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreConfirm.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreConfirm.md index 9ceae713..93f68923 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreConfirm.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreConfirm.md @@ -4,17 +4,27 @@ title: Read-SpectreConfirm + + + + ### Synopsis Displays a simple confirmation prompt with the option of selecting yes or no and returns a boolean representing the answer. + + --- + ### Description Displays a simple confirmation prompt with the option of selecting yes or no. Additional options are provided to display either a success or failure response message in addition to the boolean return value. + + --- + ### Examples This example displays a simple prompt. The user can select either yes or no [Y/n]. A different message is displayed based on the user's selection. The prompt uses the AnsiConsole.MarkupLine convenience method to support colored text and other supported markup. @@ -27,52 +37,96 @@ $readSpectreConfirmSplat = @{ Read-SpectreConfirm @readSpectreConfirmSplat ``` + --- + ### Parameters #### **Prompt** + The prompt to display to the user. The default value is "Do you like cute animals?". + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **DefaultAnswer** + The default answer to the prompt if the user just presses enter. The default value is "y". + + + Valid Values: * y * n * none + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **ConfirmSuccess** + The text and markup to display if the user chooses yes. If left undefined, nothing will display. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + #### **ConfirmFailure** + The text and markup to display if the user chooses no. If left undefined, nothing will display. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + #### **Color** + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |5 |false | + + + + --- + ### Syntax ```powershell Read-SpectreConfirm [[-Prompt] ] [[-DefaultAnswer] ] [[-ConfirmSuccess] ] [[-ConfirmFailure] ] [[-Color] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelection.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelection.md index 5579c1f0..7dd1bd31 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelection.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelection.md @@ -4,17 +4,27 @@ title: Read-SpectreMultiSelection + + + + ### Synopsis Displays a multi-selection prompt using Spectre Console and returns the selected choices. + + --- + ### Description This function displays a multi-selection prompt using Spectre Console and returns the selected choices. The prompt allows the user to select one or more choices from a list of options. The function supports customizing the title, choices, choice label property, color, and page size of the prompt. + + --- + ### Examples Displays a multi-selection prompt with the title "Select your favourite fruits", the list of fruits, the "Name" property as the label for each fruit, the color green for highlighting the selected fruits, and 3 fruits per page. @@ -22,54 +32,107 @@ Displays a multi-selection prompt with the title "Select your favourite fruits", Read-SpectreMultiSelection -Title "Select your favourite fruits" -Choices @("apple", "banana", "orange", "pear", "strawberry") -Color "Green" -PageSize 3 ``` + --- + ### Parameters #### **Title** + The title of the prompt. Defaults to "What are your favourite [color]?". + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **Choices** + The list of choices to display in the selection prompt. ChoiceLabelProperty is required if the choices are complex objects rather than an array of strings. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Array]`|false |2 |false | + + #### **ChoiceLabelProperty** + If the object is complex then the property of the choice object to use as the label in the selection prompt is required. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + #### **Color** + The color to use for highlighting the selected choices. Defaults to the accent color of the script. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + #### **PageSize** + The number of choices to display per page. Defaults to 5. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |5 |false | + + #### **AllowEmpty** +Allow the multi-selection to be submitted without any options chosen. + + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell Read-SpectreMultiSelection [[-Title] ] [[-Choices] ] [[-ChoiceLabelProperty] ] [[-Color] ] [[-PageSize] ] [-AllowEmpty] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelectionGrouped.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelectionGrouped.md index 3523d79a..ae3de910 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelectionGrouped.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreMultiSelectionGrouped.md @@ -4,17 +4,27 @@ title: Read-SpectreMultiSelectionGrouped + + + + ### Synopsis Displays a multi-selection prompt with grouped choices and returns the selected choices. + + --- + ### Description Displays a multi-selection prompt with grouped choices and returns the selected choices. The prompt allows the user to select one or more choices from a list of options. The choices can be grouped into categories, and the user can select choices from each category. + + --- + ### Examples This example displays a multi-selection prompt with two groups of choices: "Primary Colors" and "Secondary Colors". The prompt uses the "Name" property of each choice as the label. The user can select one or more choices from each group. @@ -31,54 +41,107 @@ Read-SpectreMultiSelectionGrouped -Title "Select your favorite colors" -Choices ) ``` + --- + ### Parameters #### **Title** + The title of the prompt. The default value is "What are your favourite [color]?". + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **Choices** + An array of choice groups. Each group is a hashtable with two keys: "Name" and "Choices". The "Name" key is a string that represents the name of the group, and the "Choices" key is an array of strings that represents the choices in the group. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Array]`|false |2 |false | + + #### **ChoiceLabelProperty** + The name of the property to use as the label for each choice. If this parameter is not specified, the choices are displayed as strings. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + #### **Color** + The color of the selected choices. The default value is the accent color of the script. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + #### **PageSize** + The number of choices to display per page. The default value is 10. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |5 |false | + + #### **AllowEmpty** +Allow the multi-selection to be submitted without any options chosen. + + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell Read-SpectreMultiSelectionGrouped [[-Title] ] [[-Choices] ] [[-ChoiceLabelProperty] ] [[-Color] ] [[-PageSize] ] [-AllowEmpty] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectrePause.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectrePause.md index f979161e..7360d61e 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectrePause.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectrePause.md @@ -4,17 +4,27 @@ title: Read-SpectrePause + + + + ### Synopsis Pauses the script execution and waits for user input to continue. + + --- + ### Description The Read-SpectrePause function pauses the script execution and waits for user input to continue. It displays a message prompting the user to press the enter key to continue. If the end of the console window is reached, the function clears the message and moves the cursor up to the previous line. + + --- + ### Examples This example pauses the script execution and displays the message "Press any key to continue...". The function waits for the user to press a key before continuing. @@ -22,27 +32,47 @@ This example pauses the script execution and displays the message "Press any key Read-SpectrePause -Message "Press any key to continue..." ``` + --- + ### Parameters #### **Message** + The message to display to the user. The default message is "[]Press [[/] to continue[/]". + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **NoNewline** + Indicates whether to write a newline character before displaying the message. By default, a newline character is written. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell Read-SpectrePause [[-Message] ] [-NoNewline] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreSelection.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreSelection.md index ff3d06be..4603a8bf 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreSelection.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreSelection.md @@ -4,17 +4,27 @@ title: Read-SpectreSelection + + + + ### Synopsis Displays a selection prompt using Spectre Console. + + --- + ### Description This function displays a selection prompt using Spectre Console. The user can select an option from the list of choices provided. The function returns the selected option. + + --- + ### Examples This command displays a selection prompt with the title "Select your favorite color" and the choices "Red", "Green", and "Blue". The active selection is colored in green. @@ -22,48 +32,92 @@ This command displays a selection prompt with the title "Select your favorite co Read-SpectreSelection -Title "Select your favorite color" -Choices @("Red", "Green", "Blue") -Color "Green" ``` + --- + ### Parameters #### **Title** + The title of the selection prompt. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **Choices** + The list of choices to display in the selection prompt. ChoiceLabelProperty is required if the choices are complex objects rather than an array of strings. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Array]`|false |2 |false | + + #### **ChoiceLabelProperty** + If the object is complex then the property of the choice object to use as the label in the selection prompt is required. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + #### **Color** + The color of the selected option in the selection prompt. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |4 |false | + + #### **PageSize** + The number of choices to display per page in the selection prompt. + + + + + |Type |Required|Position|PipelineInput| |---------|--------|--------|-------------| |`[Int32]`|false |5 |false | + + + + --- + ### Syntax ```powershell Read-SpectreSelection [[-Title] ] [[-Choices] ] [[-ChoiceLabelProperty] ] [[-Color] ] [[-PageSize] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreText.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreText.md index 6b4eef80..bec1b011 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreText.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Prompts/Read-SpectreText.md @@ -4,20 +4,30 @@ title: Read-SpectreText + + + + ### Synopsis Prompts the user with a question and returns the user's input. :::caution I would advise against this and instead use `Read-Host` because the Spectre Console prompt doesn't have access to the PowerShell session history. This means that you can't use the up and down arrow keys to navigate through your previous commands. ::: + + --- + ### Description This function uses Spectre Console to prompt the user with a question and returns the user's input. The function takes two parameters: $Question and $DefaultAnswer. $Question is the question to prompt the user with, and $DefaultAnswer is the default answer if the user does not provide any input. + + --- + ### Examples This will prompt the user with the question "What's your name?" and return the user's input. If the user does not provide any input, the function will return "Prefer not to say". @@ -25,34 +35,77 @@ This will prompt the user with the question "What's your name?" and return the u Read-SpectreText -Question "What's your name?" -DefaultAnswer "Prefer not to say" ``` + --- + ### Parameters #### **Question** + The question to prompt the user with. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **DefaultAnswer** + The default answer if the user does not provide any input. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + +#### **AnswerColor** + +The color of the user's answer input. The default behaviour uses the standard terminal text color. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |3 |false | + + + #### **AllowEmpty** + If specified, the user can provide an empty answer. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell -Read-SpectreText [[-Question] ] [[-DefaultAnswer] ] [-AllowEmpty] [] +Read-SpectreText [[-Question] ] [[-DefaultAnswer] ] [[-AnswerColor] ] [-AllowEmpty] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Get-SpectreEscapedText.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Get-SpectreEscapedText.md index e750b72c..80f67ed3 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Get-SpectreEscapedText.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Get-SpectreEscapedText.md @@ -4,19 +4,29 @@ title: Get-SpectreEscapedText + + + + ### Synopsis Escapes text for use in Spectre Console. [ShaunLawrie/PwshSpectreConsole/issues/5](https://github.com/ShaunLawrie/PwshSpectreConsole/issues/5) + + --- + ### Description This function escapes text for use where Spectre Console accepts markup. It is intended to be used as a helper function for other functions that output text to the console using Spectre Console which contains special characters that need escaping. See [https://spectreconsole.net/markup](https://spectreconsole.net/markup) for more information about the markup language used in Spectre Console. + + --- + ### Examples This example shows some data that requires escaping being embedded in a string passed to Format-SpectrePanel. @@ -25,20 +35,32 @@ $data = "][[][]]][[][][][" Format-SpectrePanel -Title "Unescaped data" -Data "I want escaped $($data | Get-SpectreEscapedText) [yellow]and[/] [red]unescaped[/] data" ``` + --- + ### Parameters #### **Text** + The text to be escaped. + + + + + |Type |Required|Position|PipelineInput | |----------|--------|--------|--------------| |`[String]`|true |1 |true (ByValue)| + + + + --- + ### Syntax ```powershell Get-SpectreEscapedText [-Text] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreFigletText.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreFigletText.md index bee8a064..5c8e4182 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreFigletText.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreFigletText.md @@ -4,57 +4,127 @@ title: Write-SpectreFigletText + + + + ### Synopsis Writes a Spectre Console Figlet text to the console. + + --- + ### Description This function writes a Spectre Console Figlet text to the console. The text can be aligned to the left, right, or centered, and can be displayed in a specified color. + + --- + ### Examples Displays the text "Hello Spectre!" in the center of the console, in red color. ```powershell Write-SpectreFigletText -Text "Hello Spectre!" -Alignment "Centered" -Color "Red" ``` +Displays the text "Woah!" using a custom figlet font. + +```powershell +Write-SpectreFigletText -Text "Whoa?!" -FigletFontPath "C:\Users\shaun\Downloads\3d.flf" + ██ ██ ██ ████ ██ +░██ ░██░██ ██░░██░██ +░██ █ ░██░██ ██████ ██████ ░██ ░██░██ +░██ ███ ░██░██████ ██░░░░██ ░░░░░░██ ░░ ██ ░██ +░██ ██░██░██░██░░░██░██ ░██ ███████ ██ ░██ +░████ ░░████░██ ░██░██ ░██ ██░░░░██ ░░ ░░ +░██░ ░░░██░██ ░██░░██████ ░░████████ ██ ██ +░░ ░░ ░░ ░░ ░░░░░░ ░░░░░░░░ ░░ ░░ +``` + --- + ### Parameters #### **Text** + The text to display in the Figlet format. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |1 |false | + + #### **Alignment** + The alignment of the text. Valid values are "Left", "Right", and "Centered". The default value is "Left". + + + Valid Values: * Left * Right * Center + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **Color** + The color of the text. The default value is the accent color of the script. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + +#### **FigletFontPath** + +The path to the Figlet font file to use. If this parameter is not specified, the default built-in Figlet font is used. +The figlet font format is usually *.flf, see https://spectreconsole.net/widgets/figlet for more. + + + + + + +|Type |Required|Position|PipelineInput| +|----------|--------|--------|-------------| +|`[String]`|false |4 |false | + + + + + --- + ### Syntax ```powershell -Write-SpectreFigletText [[-Text] ] [[-Alignment] ] [[-Color] ] [] +Write-SpectreFigletText [[-Text] ] [[-Alignment] ] [[-Color] ] [[-FigletFontPath] ] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreHost.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreHost.md index 11be6f79..5669eb79 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreHost.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreHost.md @@ -5,19 +5,29 @@ description: The Write-SpectreHost function writes a message to the console usin + + + + ### Synopsis Writes a message to the console using Spectre Console markup. + + --- + ### Description The Write-SpectreHost function writes a message to the console using Spectre Console. It supports ANSI markup and can optionally append a newline character to the end of the message. The markup language is defined at [https://spectreconsole.net/markup](https://spectreconsole.net/markup) Supported emoji are defined at [https://spectreconsole.net/appendix/emojis](https://spectreconsole.net/appendix/emojis) + + --- + ### Examples This example writes the message "Hello, world!" to the console with the word world flashing blue with an underline followed by an emoji throwing a shaka. @@ -25,27 +35,47 @@ This example writes the message "Hello, world!" to the console with the word wor Write-SpectreHost -Message "Hello, [blue underline rapidblink]world[/]! :call_me_hand:" ``` + --- + ### Parameters #### **Message** + The message to write to the console. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|true |1 |false | + + #### **NoNewline** + If specified, the message will not be followed by a newline character. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[Switch]`|false |named |false | + + + + --- + ### Syntax ```powershell Write-SpectreHost [-Message] [-NoNewline] [] ``` - diff --git a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreRule.md b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreRule.md index 04d579e3..8f34637f 100644 --- a/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreRule.md +++ b/PwshSpectreConsole.Docs/src/content/docs/reference/Writing/Write-SpectreRule.md @@ -4,17 +4,27 @@ title: Write-SpectreRule + + + + ### Synopsis Writes a Spectre horizontal-rule to the console. + + --- + ### Description The Write-SpectreRule function writes a Spectre horizontal-rule to the console with the specified title, alignment, and color. + + --- + ### Examples This example writes a Spectre rule with the title "My Rule", centered alignment, and red color. @@ -22,39 +32,70 @@ This example writes a Spectre rule with the title "My Rule", centered alignment, Write-SpectreRule -Title "My Rule" -Alignment Center -Color Red ``` + --- + ### Parameters #### **Title** + The title of the rule. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|true |1 |false | + + #### **Alignment** + The alignment of the text in the rule. Valid values are Left, Center, and Right. The default value is Left. + + + Valid Values: * Left * Right * Center + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |2 |false | + + #### **Color** + The color of the rule. The default value is the accent color of the script. + + + + + |Type |Required|Position|PipelineInput| |----------|--------|--------|-------------| |`[String]`|false |3 |false | + + + + --- + ### Syntax ```powershell Write-SpectreRule [-Title] [[-Alignment] ] [[-Color] ] [] ``` - diff --git a/PwshSpectreConsole.Tests/@init/Start-SpectreDemo.tests.ps1 b/PwshSpectreConsole.Tests/@init/Start-SpectreDemo.tests.ps1 new file mode 100644 index 00000000..00323695 --- /dev/null +++ b/PwshSpectreConsole.Tests/@init/Start-SpectreDemo.tests.ps1 @@ -0,0 +1,22 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +try { + Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +} catch { + Write-Warning "Failed to import PwshSpectreConsole module, rebuilding..." + & "$PSScriptRoot\..\..\PwshSpectreConsole\build.ps1" +} + +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force + +if (-not ([System.AppDomain]::CurrentDomain.GetAssemblies().FullName | Where-Object { $_ -like "*Spectre.Console.Testing*" })) { + Add-Type -Path "$PSScriptRoot\..\packages\Spectre.Console.Testing\lib\netstandard2.0\Spectre.Console.Testing.dll" +} + +Describe "Start-SpectreDemo" { + InModuleScope "PwshSpectreConsole" { + + It "Should have a demo function available, we're just testing the module was loaded correctly" { + Get-Command "Start-SpectreDemo" | Should -Not -BeNullOrEmpty + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/@snapshots/.gitignore b/PwshSpectreConsole.Tests/@snapshots/.gitignore new file mode 100644 index 00000000..07c6c9c1 --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/.gitignore @@ -0,0 +1 @@ +*.snapshot.compare.txt \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/@snapshots/Format-SpectreBarChart.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreBarChart.snapshot.txt new file mode 100644 index 00000000..5788ef9c --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreBarChart.snapshot.txt @@ -0,0 +1,3 @@ +Test 1 █████████████████████ 10  +Test 2 █████████████████████████████████████████████ 20  +Test 3 █████████████████████████████████████████████████████████████████████ 30 diff --git a/PwshSpectreConsole.Tests/@snapshots/Format-SpectreBreakdownChart.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreBreakdownChart.snapshot.txt new file mode 100644 index 00000000..3a514c85 --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreBreakdownChart.snapshot.txt @@ -0,0 +1,2 @@ +████████████████████████████████████████████████████████████████████████████████ +■ Test 1 10 ■ Test 2 20 ■ Test 3 30 diff --git a/PwshSpectreConsole.Tests/@snapshots/Format-SpectreJson.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreJson.snapshot.txt new file mode 100644 index 00000000..6c1ea6cd --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreJson.snapshot.txt @@ -0,0 +1,25 @@ +╔═Test title═════════════════════════════════════════════════════════════════╗ +║ { ║ +║ "Name": "John", ║ +║ "Age": 25, ║ +║ "City": "New York", ║ +║ "IsEmployed": true, ║ +║ "Salary": 10, ║ +║ "Hobbies": [ ║ +║ "Reading", ║ +║ "Swimming" ║ +║ ], ║ +║ "Address": { ║ +║ "Street": "123 Main St", ║ +║ "City": "New York", ║ +║ "Deep": { ║ +║ "Nested": "System.Collections.Hashtable" ║ +║ }, ║ +║ "State": "NY", ║ +║ "Zip": "10001" ║ +║ } ║ +║ } ║ +║ ║ +║ ║ +║ ║ +╚════════════════════════════════════════════════════════════════════════════╝ diff --git a/PwshSpectreConsole.Tests/@snapshots/Format-SpectrePanel.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Format-SpectrePanel.snapshot.txt new file mode 100644 index 00000000..b072a92e --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Format-SpectrePanel.snapshot.txt @@ -0,0 +1,3 @@ +╭─Test title───────────╮ +│ This is a test panel │ +╰──────────────────────╯ diff --git a/PwshSpectreConsole.Tests/@snapshots/Format-SpectreTable.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreTable.snapshot.txt new file mode 100644 index 00000000..0b7d097a --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreTable.snapshot.txt @@ -0,0 +1,7 @@ +╭────────┬───────┬────────────╮ +│ Name │ Value │ Color │ +├────────┼───────┼────────────┤ +│ Test 1 │ 10 │ Turquoise2 │ +│ Test 2 │ 20 │ #ff0000 │ +│ Test 3 │ 30 │ Turquoise2 │ +╰────────┴───────┴────────────╯ diff --git a/PwshSpectreConsole.Tests/@snapshots/Format-SpectreTree.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreTree.snapshot.txt new file mode 100644 index 00000000..be540438 --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Format-SpectreTree.snapshot.txt @@ -0,0 +1,7 @@ +Root +┣━━ Child 1 +┃ ┗━━ Grandchild 1 +┃ ┣━━ Great Grandchild 1 +┃ ┣━━ Great Grandchild 2 +┃ ┗━━ Great Grandchild 3 +┗━━ Child 2 diff --git a/PwshSpectreConsole.Tests/@snapshots/Write-SpectreCalendar.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreCalendar.snapshot.txt new file mode 100644 index 00000000..bdb25727 --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreCalendar.snapshot.txt @@ -0,0 +1,17 @@ + July 2024 +╭─────┬─────┬─────┬─────┬─────┬─────┬─────╮ +│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ +├─────┼─────┼─────┼─────┼─────┼─────┼─────┤ +│ │ 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 │ │ │ │ +│ │ │ │ │ │ │ │ +╰─────┴─────┴─────┴─────┴─────┴─────┴─────╯ +╭─────────────┬──────┬───────┬─────╮ +│ Description │ Year │ Month │ Day │ +├─────────────┼──────┼───────┼─────┤ +│ Event 1 │ 2022 │ 3 │ 10 │ +│ Event 2 │ 2022 │ 3 │ 20 │ +╰─────────────┴──────┴───────┴─────╯ diff --git a/PwshSpectreConsole.Tests/@snapshots/Write-SpectreFigletText.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreFigletText.snapshot.txt new file mode 100644 index 00000000..f13137da --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreFigletText.snapshot.txt @@ -0,0 +1,6 @@ +  __ _ _ _ _  +  / _| (_) __ _ | | ___ | |_ | |_  +  | |_ | | / _` | | | / _ \ | __| | __| +  | _| | | | (_| | | | | __/ | |_ | |_  +  |_| |_| \__, | |_| \___| \__| \__| +  |___/  diff --git a/PwshSpectreConsole.Tests/@snapshots/Write-SpectreHost.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreHost.snapshot.txt new file mode 100644 index 00000000..5c81229c --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreHost.snapshot.txt @@ -0,0 +1 @@ +Hello, World! 😎 Yay! diff --git a/PwshSpectreConsole.Tests/@snapshots/Write-SpectreRule.snapshot.txt b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreRule.snapshot.txt new file mode 100644 index 00000000..646c1e0e --- /dev/null +++ b/PwshSpectreConsole.Tests/@snapshots/Write-SpectreRule.snapshot.txt @@ -0,0 +1 @@ +───────────────────────────────────────────────────────── yo, this is a test rule ────────────────────────────────────────────────────────── diff --git a/PwshSpectreConsole.Tests/TestHelpers.psm1 b/PwshSpectreConsole.Tests/TestHelpers.psm1 new file mode 100644 index 00000000..dfb2ab6f --- /dev/null +++ b/PwshSpectreConsole.Tests/TestHelpers.psm1 @@ -0,0 +1,258 @@ +using namespace Spectre.Console + +function Get-RandomColor { + $type = 1 # Get-Random -Minimum 0 -Maximum 2 + switch ($type) { + 0 { + $colors = [Spectre.Console.Color] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name + return $colors[$(Get-Random -Minimum 0 -Maximum $colors.Count)] + } + 1 { + $hex = @() + for ($i = 0; $i -lt 3; $i++) { + $value = Get-Random -Minimum 0 -Maximum 255 + $hex += [byte]$value + } + return "#" + [System.Convert]::ToHexString($hex) + } + } +} + +function Get-RandomList { + param( + [int] $MinItems = 2, + [int] $MaxItems = 10, + [scriptblock] $Generator = { + Get-RandomString + } + ) + $items = @() + $count = Get-Random -Minimum $MinItems -Maximum $MaxItems + for ($i = 0; $i -lt $count; $i++) { + $items += $Generator.Invoke() + } + return $items + +} + +function Get-RandomString { + param ( + [int] $MinimumLength = 10, + [int] $MaximumLength = 20 + ) + $length = Get-Random -Minimum $MinimumLength -Maximum $MaximumLength + $chars = [char[]]([char]'a'..[char]'z' + [char]'A'..[char]'Z' + [char]'0'..[char]'9') + $string = "" + for ($i = 0; $i -lt $length; $i++) { + $string += $chars[$(Get-Random -Minimum 0 -Maximum $chars.Count)] + } + return $string +} + +function Get-RandomBoxBorder { + $lookup = [BoxBorder] | Get-Member -Static -MemberType Properties | Select-Object -ExpandProperty Name + return $lookup[$(Get-Random -Minimum 0 -Maximum $lookup.Count)] +} + +function Get-RandomJustify { + $lookup = [Justify].GetEnumNames() + return $lookup[$(Get-Random -Minimum 0 -Maximum $lookup.Count)] +} + +function Get-RandomSpinner { + $lookup = [Spinner+Known] | Get-Member -Static -MemberType Properties | Select-Object -ExpandProperty Name + return $lookup[$(Get-Random -Minimum 0 -Maximum $lookup.Count)] +} + +function Get-RandomTreeGuide { + $lookup = [TreeGuide] | Get-Member -Static -MemberType Properties | Select-Object -ExpandProperty Name + return $lookup[$(Get-Random -Minimum 0 -Maximum $lookup.Count)] +} + +function Get-RandomTableBorder { + $lookup = [TableBorder] | Get-Member -Static -MemberType Properties | Select-Object -ExpandProperty Name + return $lookup[$(Get-Random -Minimum 0 -Maximum $lookup.Count)] +} + +function Get-RandomChartItem { + return New-SpectreChartItem -Label (Get-RandomString) -Value (Get-Random -Minimum -100 -Maximum 100) -Color (Get-RandomColor) +} + +function Get-RandomTree { + param( + [hashtable] $Root, + [int] $MinChildren = 1, + [int] $MaxChildren = 3, + [int] $MaxDepth = 5, + [int] $CurrentDepth = 0 + ) + + if ($CurrentDepth -gt $MaxDepth) { + return $Root + } + + $CurrentDepth++ + + if ($null -eq $Root) { + $Root = @{ + Label = Get-RandomString + Children = @() + } + } + + $children = Get-Random -Minimum $MinChildren -Maximum $MaxChildren + for ($i = 0; $i -lt $children; $i++) { + $newChild = @{ + Label = Get-RandomString + Children = @() + } + $newTree = Get-RandomTree -Root $newChild -MaxChildren $MaxChildren -MaxDepth $MaxDepth -CurrentDepth $CurrentDepth + $Root.Children += $newTree + } + + return $Root +} + +function Get-RandomBool { + return [bool](Get-Random -Minimum 0 -Maximum 2) +} + +function Get-RandomChoice { + param( + [string[]] $Choices + ) + return $Choices[(Get-Random -Minimum 0 -Maximum $Choices.Count)] +} + +function Get-SpectreRenderable { + param( + [Parameter(Mandatory)] + [Spectre.Console.Rendering.Renderable]$RenderableObject + ) + try { + $writer = [System.IO.StringWriter]::new() + $output = [Spectre.Console.AnsiConsoleOutput]::new($writer) + $settings = [Spectre.Console.AnsiConsoleSettings]::new() + $settings.Out = $output + $console = [Spectre.Console.AnsiConsole]::Create($settings) + $console.Write($RenderableObject) + $writer.ToString() + } + finally { + $writer.Dispose() + } +} + +function Get-AnsiEscapeSequence { + <# + could be useful for debugging + #> + param( + [Parameter(Mandatory, ValueFromPipeline)] + [String] $String + ) + process { + $Escaped = $String.EnumerateRunes() | ForEach-Object { + if ($_.Value -le 0x1f) { + [Text.Rune]::new($_.Value + 0x2400) + } + else { + $_ + } + } | Join-String + [PSCustomObject]@{ + Escaped = $Escaped + Original = $String + Clean = [System.Management.Automation.Host.PSHostUserInterface]::GetOutputString($String, $false) + } + } +} +function StripAnsi { + process { + [System.Management.Automation.Host.PSHostUserInterface]::GetOutputString($_, $false) + } +} + +function Get-PSStyleRandom { + param( + [Switch] $Foreground, + [Switch] $Background, + [Switch] $Decoration, + [Switch] $RGBForeground, + [Switch] $RGBBackground + ) + $Style = Switch ($PSBoundParameters.Keys) { + 'Foreground' { + $fg = ($PSStyle.Foreground | Get-Member -MemberType Property | Get-Random).Name + $PSStyle.Foreground.$fg + } + 'Background' { + $bg = ($PSStyle.Background | Get-Member -MemberType Property | Get-Random).Name + $PSStyle.Background.$bg + } + 'Decoration' { + $deco = ($PSStyle | Get-Member -MemberType Property | Where-Object { $_.Definition -match '^string' -And $_.Name -notmatch 'off$|Reset' } | Get-Random).Name + $PSStyle.$deco + } + 'RGBForeground' { + $r = Get-Random -min 0 -max 255 + $g = Get-Random -min 0 -max 255 + $b = Get-Random -min 0 -max 255 + $PSStyle.Foreground.FromRgb($r, $g, $b) + } + 'RGBBackground' { + $r = Get-Random -min 0 -max 255 + $g = Get-Random -min 0 -max 255 + $b = Get-Random -min 0 -max 255 + $PSStyle.Background.FromRgb($r, $g, $b) + } + } + return $Style | Join-String +} +Function Get-SpectreColorSample { + $spectreColors = [Spectre.Console.Color] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name + foreach ($c in $spectreColors) { + $color = [Spectre.Console.Color]::$c + $renderable = [Spectre.Console.Text]::new("Hello, $c", [Spectre.Console.Style]::new($color)) + $SpectreString = Get-SpectreRenderable $renderable + [PSCustomObject]@{ + Color = $c + String = $SpectreString + # Object = $color + # Debug = Get-AnsiEscapeSequence $SpectreString + } + } +} +function Get-SpectreTableRowData { + param( + [int]$Count = 5, + [Switch]$Markup + ) + if ($null -eq $count) { + $count = 5 + } + 1..$Count | ForEach-Object { + if ($Markup) { + return '[{0}] {1} [/]' -f (Get-RandomColor), (Get-RandomString) + } + Get-RandomString + } +} + +function Assert-OutputMatchesSnapshot { + param ( + [string] $SnapshotName, + [string] $Output + ) + $snapShotComparisonPath = "$PSScriptRoot\@snapshots\$SnapshotName.snapshot.compare.txt" + $snapShotPath = "$PSScriptRoot\@snapshots\$SnapshotName.snapshot.txt" + $compare = $Output -replace "`r", "" + Set-Content -Path $snapShotComparisonPath -Value $compare -NoNewline + $snapshot = Get-Content -Path $snapShotPath -Raw + if($compare -ne $snapshot) { + Write-Host "Expected to match snapshot:`n`n$snapshot" + Write-Host "But the output was:`n`n$compare" + Write-Host "You can diff the snapshot files at:`n - $snapShotPath`n - $snapShotComparisonPath" + throw "Snapshot comparison failed" + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/formatting/ConvertTo-SpectreDecoration.tests.ps1 b/PwshSpectreConsole.Tests/formatting/ConvertTo-SpectreDecoration.tests.ps1 new file mode 100644 index 00000000..c9fef710 --- /dev/null +++ b/PwshSpectreConsole.Tests/formatting/ConvertTo-SpectreDecoration.tests.ps1 @@ -0,0 +1,38 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "ConvertTo-SpectreDecoration" { + InModuleScope "PwshSpectreConsole" { + It "Test PSStyle Decorations" { + $PSStyleColor = Get-PSStyleRandom -Decoration + $string = 'Hello, world!, hello universe!' + $sample = "{0}{1}{2}" -f $PSStyleColor, $string, $PSStyle.Reset + $test = Get-SpectreRenderable (ConvertTo-SpectreDecoration $sample) + $test | Should -Be $sample + } + It "Test PSStyle Foreground RGB Colors" -Tag "ExcludeCI" { + # testing something + $PSStyleColor = Get-PSStyleRandom -RGBForeground + $string = 'Hello, world!, hello universe!' + $sample = "{0}{1}{2}" -f $PSStyleColor, $string, $PSStyle.Reset + $test = Get-SpectreRenderable (ConvertTo-SpectreDecoration $sample) + $test | Should -Be $sample + } + It "Test PSStyle Background RGB Colors" -Tag "ExcludeCI" { + $PSStyleColor = Get-PSStyleRandom -RGBBackground + $string = 'Hello, world!, hello universe!' + $sample = "{0}{1}{2}" -f $PSStyleColor, $string, $PSStyle.Reset + $test = Get-SpectreRenderable (ConvertTo-SpectreDecoration $sample) + $test | Should -Be $sample + } + It "Test Spectre Colors" { + # this might work because the colors are generated from CI so shouldnt get us codes we cant render. + $sample = Get-SpectreColorSample + foreach ($item in $sample) { + $test = Get-SpectreRenderable (ConvertTo-SpectreDecoration $item.String) + $test | Should -Be $item.String + } + } + } +} diff --git a/PwshSpectreConsole.Tests/formatting/Format-SpectreBarChart.tests.ps1 b/PwshSpectreConsole.Tests/formatting/Format-SpectreBarChart.tests.ps1 index f0c222d8..bf309658 100644 --- a/PwshSpectreConsole.Tests/formatting/Format-SpectreBarChart.tests.ps1 +++ b/PwshSpectreConsole.Tests/formatting/Format-SpectreBarChart.tests.ps1 @@ -1,32 +1,89 @@ -# Import the module to be tested/ -BeforeAll { - Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psm1" -Force -} +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force Describe "Format-SpectreBarChart" { InModuleScope "PwshSpectreConsole" { - Context "WhenThingsHappen" { - BeforeEach { - Mock Write-AnsiConsole {} - Mock Convert-ToSpectreColor {} + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + $testWidth = Get-Random -Minimum 10 -Maximum 100 + $testTitle = "Test Chart $([guid]::NewGuid())" + $testData = @() + for($i = 0; $i -lt (Get-Random -Minimum 3 -Maximum 10); $i++) { + $testData += New-SpectreChartItem -Label (Get-RandomString) -Value (Get-Random -Minimum -100 -Maximum 100) -Color (Get-RandomColor) } - It "Should create a bar chart with correct width" { - $data = @( - @{ Label = "Apples"; Value = 10; Color = "Green" }, - @{ Label = "Oranges"; Value = 5; Color = "Yellow" }, - @{ Label = "Bananas"; Value = 3; Color = "Red" } - ) - Format-SpectreBarChart -Data $data -Title "Fruit Sales" -Width 50 - Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Rendering.Renderable] + $RenderableObject.Width | Should -Be $testWidth + $RenderableObject.Label | Should -Be $testTitle + $RenderableObject.Data.Count | Should -Be $testData.Count + + $testConsole.Write($RenderableObject) + } + + Mock Get-HostWidth { + return $testWidth } + } + + It "Should create a bar chart with correct width" { + Format-SpectreBarChart -Data $testData -Title $testTitle -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle piped input correctly" { + $testData | Format-SpectreBarChart -Title $testTitle -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle single input correctly" { + $testData = New-SpectreChartItem -Label (Get-RandomString) -Value (Get-Random -Minimum -100 -Maximum 100) -Color (Get-RandomColor) + Format-SpectreBarChart -Data $testData -Title $testTitle -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle no title" { + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Rendering.Renderable] + $RenderableObject.Width | Should -Be $testWidth + $RenderableObject.Label | Should -Be $null + $RenderableObject.Data.Count | Should -Be $testData.Count + + $testConsole.Write($RenderableObject) + } + Format-SpectreBarChart -Data $testData -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle no width and default to host width" { + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Rendering.Renderable] + $RenderableObject.Width | Should -Be $testWidth + $RenderableObject.Label | Should -Be $null + $RenderableObject.Data.Count | Should -Be $testData.Count + + $testConsole.Write($RenderableObject) + } + Format-SpectreBarChart -Data $testData + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } - It "Should handle single item data correctly" { - $data = @{ Label = "Apples"; Value = 10; Color = "Green" } - Format-SpectreBarChart -Data $data -Title "Fruit Sales" -Width 50 - Assert-MockCalled Write-AnsiConsole -Times 1 -Exactly + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) } + $testWidth = 120 + Write-Debug "Setting test width to $testWidth" + $testData = @( + (New-SpectreChartItem -Label "Test 1" -Value 10 -Color "Turquoise2"), + (New-SpectreChartItem -Label "Test 2" -Value 20 -Color "#ff0000"), + (New-SpectreChartItem -Label "Test 3" -Value 30 -Color "Turquoise2") + ) + Format-SpectreBarChart -Data $testData + { Assert-OutputMatchesSnapshot -SnapshotName "Format-SpectreBarChart" -Output $testConsole.Output } | Should -Not -Throw } } } \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/formatting/Format-SpectreBreakdownChart.tests.ps1 b/PwshSpectreConsole.Tests/formatting/Format-SpectreBreakdownChart.tests.ps1 new file mode 100644 index 00000000..a0c68670 --- /dev/null +++ b/PwshSpectreConsole.Tests/formatting/Format-SpectreBreakdownChart.tests.ps1 @@ -0,0 +1,73 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Format-SpectreBreakdownChart" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + $testWidth = Get-Random -Minimum 10 -Maximum 100 + $testData = @() + for($i = 0; $i -lt (Get-Random -Minimum 3 -Maximum 10); $i++) { + $testData += Get-RandomChartItem + } + + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Rendering.Renderable] + $RenderableObject.Width | Should -Be $testWidth + $RenderableObject.Data.Count | Should -Be $testData.Count + + $testConsole.Write($RenderableObject) + } + + Mock Get-HostWidth { + return $testWidth + } + } + + It "Should create a bar chart with correct width" { + Format-SpectreBreakdownChart -Data $testData -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle piped input correctly" { + $testData | Format-SpectreBreakdownChart -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle single input correctly" { + $testData = New-SpectreChartItem -Label (Get-RandomString) -Value (Get-Random -Minimum -100 -Maximum 100) -Color (Get-RandomColor) + Format-SpectreBreakdownChart -Data $testData -Width $testWidth + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should handle no width and default to host width" { + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Rendering.Renderable] + $RenderableObject.Width | Should -Be $testWidth + $RenderableObject.Label | Should -Be $null + $RenderableObject.Data.Count | Should -Be $testData.Count + + $testConsole.Write($RenderableObject) + } + Format-SpectreBreakdownChart -Data $testData + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + $testWidth = 120 + Write-Debug "Setting test width to $testWidth" + $testData = @( + (New-SpectreChartItem -Label "Test 1" -Value 10 -Color "Turquoise2"), + (New-SpectreChartItem -Label "Test 2" -Value 20 -Color "Turquoise2"), + (New-SpectreChartItem -Label "Test 3" -Value 30 -Color "Turquoise2") + ) + Format-SpectreBreakdownChart -Data $testData + { Assert-OutputMatchesSnapshot -SnapshotName "Format-SpectreBreakdownChart" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/formatting/Format-SpectreJson.tests.ps1 b/PwshSpectreConsole.Tests/formatting/Format-SpectreJson.tests.ps1 new file mode 100644 index 00000000..e952edd1 --- /dev/null +++ b/PwshSpectreConsole.Tests/formatting/Format-SpectreJson.tests.ps1 @@ -0,0 +1,138 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Format-SpectreJson" { + InModuleScope "PwshSpectreConsole" { + + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + $testData = @( + [pscustomobject]@{ + Name = "John" + Age = 25 + City = "New York" + IsEmployed = $true + Salary = 10 + Hobbies = @("Reading", "Swimming") + Address = [pscustomobject]@{ + Street = "123 Main St" + City = "New York" + Deep = @{ + Nested = @{ + Value = @{ + That = @{ + Is = @{ + Nested = @{ + Again = "Hello" + } + } + } + } + } + } + State = "NY" + Zip = "10001" + } + } + ) + $testData | Out-Null + $testConsole | Out-Null + $testBorder = Get-RandomBoxBorder + $testColor = Get-RandomColor + $testTitle = Get-RandomString + $testExpand = Get-RandomBool + $testWidth = Get-Random -Minimum 5 -Maximum 100 + $testHeight = Get-Random -Minimum 5 -Maximum 100 + Write-Debug $testBorder + Write-Debug $testColor + Write-Debug $testTitle + Write-Debug $testExpand + Write-Debug $testWidth + Write-Debug $testHeight + + Mock Get-HostWidth { return 100 } + Mock Get-HostHeight { return 100 } + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + } + + It "tries to render a panel which somewhat implies that the json parsing worked" { + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Panel] + $RenderableObject.Header.Text | Should -Be $testTitle + if($testBorder -ne "None") { + $RenderableObject.Border.GetType().Name | Should -BeLike "*$testBorder*" + } + $RenderableObject.BorderStyle.Foreground.ToMarkup() | Should -Be $testColor + $RenderableObject.Width | Should -Be $testWidth + $RenderableObject.Height | Should -Be $testHeight + $RenderableObject.Expand | Should -Be $testExpand + + $testConsole.Write($RenderableObject) + } + + Format-SpectreJson -Title $testTitle -Border $testBorder -Color $testColor -Height $testHeight -Width $testWidth -Expand:$testExpand -Data $testData + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "tries to render json when noborder is specified" { + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Json.JsonText] + } + + Format-SpectreJson -NoBorder -Data $testData + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Simple scalar array test" { + { + $numbers = Get-Random -Minimum 30 -Maximum 50 + 1..$numbers | Format-SpectreJson -Border None + $json = $testConsole.Output | StripAnsi + ($json.trim() -split "\r?\n").count | Should -Be ($numbers + 2) # 10 items + 2 braces + } | Should -Not -Throw + } + + It "Simple String test" { + { + $numbers = Get-Random -Minimum 30 -Maximum 50 + 1..$numbers | ConvertTo-Json | Format-SpectreJson -Border None + $json = $testConsole.Output | StripAnsi + ($json.trim() -split "\r?\n").count | Should -Be ($numbers + 2) # 10 items + 2 braces + } | Should -Not -Throw + } + + It "Should take json string input" { + $data = @( + [pscustomobject]@{Name = "John"; Age = 25; City = "New York" }, + [pscustomobject]@{Name = "Jane"; Age = $null; City = "Los Angeles" } + ) + $data | ConvertTo-Json | Format-SpectreJson -Border None + $roundtrip = $testConsole.Output | StripAnsi | ConvertFrom-Json + (Compare-Object -ReferenceObject $data -DifferenceObject $roundtrip -Property Name, Age, City -CaseSensitive -IncludeEqual).SideIndicator | Should -Be @('==','==') + } + + It "Should roundtrip json string input" { + $ht = @{} + Get-RandomList -MinItems 30 -MaxItems 50 | ForEach-Object { + $ht[$_] = Get-RandomString + } + $data = [pscustomobject]$ht + $data | ConvertTo-Json | Format-SpectreJson -Border None + $roundtrip = $testConsole.Output | StripAnsi | ConvertFrom-Json + $roundtrip.psobject.properties.name | Should -Be $data.psobject.properties.name + $roundtrip.psobject.properties.value | Should -Be $data.psobject.properties.value + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + Format-SpectreJson -Title "Test title" -Border "Double" -Color "SpringGreen3" -Height 25 -Width 78 -Data $testData + { Assert-OutputMatchesSnapshot -SnapshotName "Format-SpectreJson" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/formatting/Format-SpectrePanel.tests.ps1 b/PwshSpectreConsole.Tests/formatting/Format-SpectrePanel.tests.ps1 new file mode 100644 index 00000000..e2e26ea8 --- /dev/null +++ b/PwshSpectreConsole.Tests/formatting/Format-SpectrePanel.tests.ps1 @@ -0,0 +1,54 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Format-SpectrePanel" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + [Spectre.Console.Testing.TestConsoleExtensions]::Width($testConsole, 80) + $testTitle = Get-RandomString -MinimumLength 5 -MaximumLength 10 + $testBorder = Get-RandomBoxBorder + $testExpand = $false + $testColor = Get-RandomColor + + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Panel] + $RenderableObject.Header.Text | Should -Be $testTitle + $RenderableObject.Expand | Should -Be $testExpand + $RenderableObject.BorderStyle.Foreground.ToMarkup() | Should -Be $testColor + if($testBorder -ne "None") { + $RenderableObject.Border.GetType().Name | Should -BeLike "*$testBorder*" + } + + $testConsole.Write($RenderableObject) + } + } + + It "Should create a panel" { + $randomString = Get-RandomString + Format-SpectrePanel -Data $randomString -Title $testTitle -Border $testBorder -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + $testConsole.Output | Should -BeLike "*$randomString*" + $testConsole.Output | Should -BeLike "*$testTitle*" + } + + It "Should create an expanded panel" { + $testExpand = $true + $randomString = Get-RandomString + Format-SpectrePanel -Data $randomString -Title $testTitle -Border $testBorder -Expand:$testExpand -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + $testConsole.Output | Should -BeLike "*$randomString*" + $testConsole.Output | Should -BeLike "*$testTitle*" + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + Format-SpectrePanel -Data "This is a test panel" -Title "Test title" -Border "Rounded" -Color "Turquoise2" | Out-Null + { Assert-OutputMatchesSnapshot -SnapshotName "Format-SpectrePanel" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/formatting/Format-SpectreTable.tests.ps1 b/PwshSpectreConsole.Tests/formatting/Format-SpectreTable.tests.ps1 new file mode 100644 index 00000000..c638a9a7 --- /dev/null +++ b/PwshSpectreConsole.Tests/formatting/Format-SpectreTable.tests.ps1 @@ -0,0 +1,203 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Format-SpectreTable" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + [Spectre.Console.Testing.TestConsoleExtensions]::Width($testConsole, 140) + $testData = $null + $testBorder = Get-RandomBoxBorder + $testColor = Get-RandomColor + + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Table] + $RenderableObject.Rows.Count | Should -Be $testData.Count + if($testBorder -ne "None") { + $RenderableObject.Border.GetType().Name | Should -BeLike "*$testBorder*" + } + if($testColor) { + $RenderableObject.BorderStyle.Foreground.ToMarkup() | Should -Be $testColor + } + + $testConsole.Write($RenderableObject) + } + } + + It "Should create a table when default display members for a command are required" { + $testData = Get-ChildItem "$PSScriptRoot" + Format-SpectreTable -Data $testData -Border $testBorder -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should create a table when default display members for a command are required and input is piped" { + $testData = Get-ChildItem "$PSScriptRoot" + $testData | Format-SpectreTable -Border $testBorder -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should be able to retrieve default display members for command output with format data" { + $testData = Get-ChildItem "$PSScriptRoot" + $defaultDisplayMembers = $testData | Format-Table | Get-TableHeader + if ($IsLinux -or $IsMacOS) { + # Expected @('UnixMode', 'User', 'Group', 'LastWrite…', 'Size', 'Name'), but got @('UnixMode', 'User', 'Group', 'LastWriteTime', 'Size', 'Name'). + # i have no idea whats truncating LastWriteTime + # $defaultDisplayMembers.Properties.GetEnumerator().Name | Should -Be @("UnixMode", "User", "Group", "LastWriteTime", "Size", "Name") + $defaultDisplayMembers.keys | Should -Match 'UnixMode|User|Group|LastWrite|Size|Name' + } + else { + $defaultDisplayMembers.keys | Should -Be @("Mode", "LastWriteTime", "Length", "Name") + } + } + + It "Should not throw and should return null when input does not have format data" { + { + $defaultDisplayMembers = [hashtable]@{ + "Hello" = "World" + } | Get-TableHeader + $defaultDisplayMembers | Should -Be $null + } | Should -Not -Throw + } + + It "Should be able to format ansi strings" { + $rawString = "hello world" + $ansiString = "`e[31mhello `e[46mworld`e[0m" + $result = ConvertTo-SpectreDecoration -String $ansiString + $result.Length | Should -Be $rawString.Length + } + + It "Should be able to format PSStyle strings" { + $rawString = "" + $ansiString = "" + $PSStyle | Get-Member -MemberType Property | Where-Object { $_.Definition -match '^string' -And $_.Name -notmatch 'off$|Reset' } | ForEach-Object { + $name = $_.Name + $rawString += "$name " + $ansiString += "$($PSStyle.$name)$name " + } + $ansiString += "$($PSStyle.Reset)" + $result = ConvertTo-SpectreDecoration -String $ansiString + $result.Length | Should -Be $rawString.Length + } + + It "Should be able to format strings with spectre markup when opted in" { + $rawString = "hello spectremarkup world" + $ansiString = "hello [red]spectremarkup[/] world" + $result = ConvertTo-SpectreDecoration -String $ansiString -AllowMarkup + $result.Length | Should -Be $rawString.Length + } + + It "Should leave spectre markup alone by default" { + $ansiString = "hello [red]spectremarkup[/] world" + $result = ConvertTo-SpectreDecoration -String $ansiString + $result.Length | Should -Be $ansiString.Length + } + + It "Should be able to create a new table cell with spectre markup" { + $rawString = "hello spectremarkup world" + $ansiString = "hello [red]spectremarkup[/] world" + $result = New-TableCell -String $ansiString -AllowMarkup + $result | Should -BeOfType [Spectre.Console.Markup] + $result.Length | Should -Be $rawString.Length + } + + It "Should be able to create a new table cell without spectre markup by default" { + $ansiString = "hello [red]spectremarkup[/] world" + $result = New-TableCell -String $ansiString + $result | Should -BeOfType [Spectre.Console.Text] + $result.Length | Should -Be $ansiString.Length + } + + It "Should be able to create a new table row with spectre markup" { + $entryitem = Get-SpectreTableRowData -Markup + $result = New-TableRow -Entry $entryItem -AllowMarkup + $result -is [array] | Should -Be $true + $result[0] | Should -BeOfType [Spectre.Console.Markup] + $result.Count | Should -Be $entryitem.Count + } + + It "Should be able to create a new table row without spectre markup by default" { + $entryitem = Get-SpectreTableRowData -Markup + $result = New-TableRow -Entry $entryItem + $result -is [array] | Should -Be $true + $result[0] | Should -BeOfType [Spectre.Console.Text] + $result[0].Length | Should -Be $entryItem[0].Length + $result.Count | Should -Be $entryitem.Count + } + + It "Should create a table and display results properly" { + $testBorder = 'Markdown' + $testData = Get-ChildItem "$PSScriptRoot" + $verification = $testdata | Format-Table | Get-TableHeader + Format-SpectreTable -Data $testData -Border $testBorder -Color $testColor + $testResult = $testConsole.Output + $rows = $testResult -split "\r?\n" | Select-Object -Skip 1 -SkipLast 2 + $header = $rows[0] + $properties = $header -split '\|' | StripAnsi | ForEach-Object { + if (-Not [String]::IsNullOrWhiteSpace($_)) { + $_.Trim() + } + } + if ($IsLinux -or $IsMacOS) { + $verification.keys | Should -Match 'UnixMode|User|Group|LastWrite|Size|Name' + } + else { + $verification.keys | Should -Be $properties + } + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should create a table and display ICollection results properly" { + $testData = 1 | Group-Object + $testBorder = 'Markdown' + $testColor = $null + Write-Debug "Setting testcolor to $testColor" + Format-SpectreTable -Data $testData -Border $testBorder -HideHeaders -Property Group + $testResult = $testConsole.Output | StripAnsi + $clean = $testResult -replace '\s+|\|' + $clean | Should -Be '{1}' + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should be able to use calculated properties" { + $testData = Get-Process -Id $pid + $testBorder = 'Markdown' + $testColor = $null + Write-Debug "Setting testcolor to $testColor" + $testData | Format-SpectreTable ProcessName, @{Label="TotalRunningTime"; Expression={(Get-Date) - $_.StartTime}} -Border $testBorder + $testResult = $testConsole.Output + $obj = $testResult -split "\r?\n" | Select-Object -Skip 1 -SkipLast 2 + $deconstructed = $obj -split '\|' | StripAnsi | ForEach-Object { + if (-Not [String]::IsNullOrEmpty($_)) { + $_.Trim() + } + } + $deconstructed[0] | Should -Be 'ProcessName' + $deconstructed[1] | Should -Be 'TotalRunningTime' + $deconstructed[4] | Should -Be 'pwsh' + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + [pscustomobject]@{ + "Name" = "Test 1" + "Value" = 10 + "Color" = "Turquoise2" + }, [pscustomobject]@{ + "Name" = "Test 2" + "Value" = 20 + "Color" = "#ff0000" + }, [pscustomobject]@{ + "Name" = "Test 3" + "Value" = 30 + "Color" = "Turquoise2" + } | Format-SpectreTable -Border "Rounded" -Color "Turquoise2" + + { Assert-OutputMatchesSnapshot -SnapshotName "Format-SpectreTable" -Output $testConsole.Output } | Should -Not -Throw + } + } +} diff --git a/PwshSpectreConsole.Tests/formatting/Format-SpectreTree.tests.ps1 b/PwshSpectreConsole.Tests/formatting/Format-SpectreTree.tests.ps1 new file mode 100644 index 00000000..7dd905db --- /dev/null +++ b/PwshSpectreConsole.Tests/formatting/Format-SpectreTree.tests.ps1 @@ -0,0 +1,67 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Format-SpectreTree" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + [Spectre.Console.Testing.TestConsoleExtensions]::Width($testConsole, 140) + $testGuide = Get-RandomTreeGuide + $testColor = Get-RandomColor + + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Tree] + $RenderableObject.Style.Foreground.ToMarkup() | Should -Be $testColor + $RenderableObject.Guide.GetType().ToString() | Should -BeLike "*$testGuide*" + $RenderableObject.Nodes.Count | Should -BeGreaterThan 0 + + $testConsole.Write($RenderableObject) + } + } + + It "Should create a Tree" { + Get-RandomTree | Format-SpectreTree -Guide $testGuide -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + $testData = @{ + Label = "Root" + Children = @( + @{ + Label = "Child 1" + Children = @( + @{ + Label = "Grandchild 1" + Children = @( + @{ + Label = "Great Grandchild 1" + }, + @{ + Label = "Great Grandchild 2" + }, + @{ + Label = "Great Grandchild 3" + } + ) + } + ) + }, + @{ + Label = "Child 2" + } + ) + } + + $testGuide = "BoldLine" + $testColor = "DeepPink2" + $testData | Format-SpectreTree -Guide $testGuide -Color $testColor + { Assert-OutputMatchesSnapshot -SnapshotName "Format-SpectreTree" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/progress/Invoke-SpectreCommandWithProgress.tests.ps1 b/PwshSpectreConsole.Tests/progress/Invoke-SpectreCommandWithProgress.tests.ps1 new file mode 100644 index 00000000..646d97bc --- /dev/null +++ b/PwshSpectreConsole.Tests/progress/Invoke-SpectreCommandWithProgress.tests.ps1 @@ -0,0 +1,49 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +$script:originalConsole = [Spectre.Console.AnsiConsole]::Console + +Describe "Invoke-SpectreCommandWithProgress" -Tag "integration" { + InModuleScope "PwshSpectreConsole" { + + BeforeEach { + $writer = [System.IO.StringWriter]::new() + $output = [Spectre.Console.AnsiConsoleOutput]::new($writer) + $settings = [Spectre.Console.AnsiConsoleSettings]::new() + $settings.Out = $output + [Spectre.Console.AnsiConsole]::Console = [Spectre.Console.AnsiConsole]::Create($settings) + } + + AfterEach { + $settings = [Spectre.Console.AnsiConsoleSettings]::new() + $settings.Out = [Spectre.Console.AnsiConsoleOutput]::new([System.Console]::Out) + [Spectre.Console.AnsiConsole]::Console = [Spectre.Console.AnsiConsole]::Create($settings) + } + + It "executes the scriptblock for the basic case" { + Invoke-SpectreCommandWithProgress -ScriptBlock { + param ( + $Context + ) + $task1 = $Context.AddTask("Completing a single stage process") + Start-Sleep -Milliseconds 500 + $task1.Increment(100) + return 1 + } | Should -Be 1 + } + + It "executes the scriptblock with background jobs" { + Invoke-SpectreCommandWithProgress -ScriptBlock { + param ( + $Context + ) + $jobs = @() + $jobs += Add-SpectreJob -Context $Context -JobName "job 1" -Job (Start-Job { Start-Sleep -Seconds 1 }) + $jobs += Add-SpectreJob -Context $Context -JobName "job 2" -Job (Start-Job { Start-Sleep -Seconds 1 }) + Wait-SpectreJobs -Context $Context -Jobs $jobs + return 1 + } | Should -Be 1 + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/progress/Invoke-SpectreCommandWithStatus.tests.ps1 b/PwshSpectreConsole.Tests/progress/Invoke-SpectreCommandWithStatus.tests.ps1 new file mode 100644 index 00000000..df046db6 --- /dev/null +++ b/PwshSpectreConsole.Tests/progress/Invoke-SpectreCommandWithStatus.tests.ps1 @@ -0,0 +1,51 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Invoke-SpectreCommandWithStatus" -Tag "integration" { + InModuleScope "PwshSpectreConsole" { + + BeforeEach { + $testTitle = Get-RandomString + $testSpinner = Get-RandomSpinner + $testColor = Get-RandomColor + Write-Debug $testTitle + Write-Debug $testSpinner + Write-Debug $testColor + + $writer = [System.IO.StringWriter]::new() + $output = [Spectre.Console.AnsiConsoleOutput]::new($writer) + $settings = [Spectre.Console.AnsiConsoleSettings]::new() + $settings.Out = $output + [Spectre.Console.AnsiConsole]::Console = [Spectre.Console.AnsiConsole]::Create($settings) + } + + AfterEach { + $settings = [Spectre.Console.AnsiConsoleSettings]::new() + $settings.Out = [Spectre.Console.AnsiConsoleOutput]::new([System.Console]::Out) + [Spectre.Console.AnsiConsole]::Console = [Spectre.Console.AnsiConsole]::Create($settings) + } + + It "executes the scriptblock for the basic case" { + Mock Start-AnsiConsoleStatus { + $Title | Should -Be $testTitle + $Spinner.GetType().Name | Should -BeLike "*$testSpinner*" + $SpinnerStyle.Foreground.ToMarkup() | Should -Be $testColor + $ScriptBlock | Should -BeOfType [scriptblock] + + & $ScriptBlock + } + Invoke-SpectreCommandWithStatus -Title $testTitle -Spinner $testSpinner -Color $testColor -ScriptBlock { + return 1 + } | Should -Be 1 + Assert-MockCalled -CommandName "Start-AnsiConsoleStatus" -Times 1 -Exactly + } + + It "executes the scriptblock without mocking" { + Invoke-SpectreCommandWithStatus -Title $testTitle -Spinner $testSpinner -Color $testColor -ScriptBlock { + Start-Sleep -Seconds 1 + return 1 + } | Should -Be 1 + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/prompts/Read-SpectreConfirm.tests.ps1 b/PwshSpectreConsole.Tests/prompts/Read-SpectreConfirm.tests.ps1 new file mode 100644 index 00000000..ac766817 --- /dev/null +++ b/PwshSpectreConsole.Tests/prompts/Read-SpectreConfirm.tests.ps1 @@ -0,0 +1,61 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Read-SpectreConfirm" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $choices = @("y", "n") + $testDefaultAnswer = "y" + Mock Invoke-SpectrePromptAsync { + $Prompt | Should -BeOfType [Spectre.Console.TextPrompt[string]] + (Compare-Object -ReferenceObject $Prompt.Choices -DifferenceObject $choices) | Should -BeNullOrEmpty + if($testColor) { + $Prompt.ChoicesStyle.Foreground.ToMarkup() | Should -Be $testColor + } + return $testDefaultAnswer + } + } + + It "prompts" { + Read-SpectreConfirm -Prompt (Get-RandomString) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "prompts with a default answer" { + $testDefaultAnswer = Get-RandomChoice $choices + $expectedAnswer = ($testDefaultAnswer -eq "y") ? $true : $false + $thisAnswer = Read-SpectreConfirm -Prompt (Get-RandomString) -DefaultAnswer $testDefaultAnswer + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + $thisAnswer | Should -Be $expectedAnswer + } + + It "writes success message" { + $confirmSuccess = Get-RandomString + Mock Write-SpectreHost { + $Message | Should -Be $confirmSuccess + } + Read-SpectreConfirm -Prompt (Get-RandomString) -ConfirmSuccess $confirmSuccess -DefaultAnswer (Get-RandomChoice $choices) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + Assert-MockCalled -CommandName "Write-SpectreHost" -Times 1 -Exactly + } + + It "writes failure message" { + $confirmFailure = Get-RandomString + $testDefaultAnswer = "n" + $testDefaultAnswer | Out-Null + Mock Write-SpectreHost { + $Message | Should -Be $confirmFailure + } + Read-SpectreConfirm -Prompt (Get-RandomString) -ConfirmFailure $confirmFailure -DefaultAnswer (Get-RandomChoice $choices) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + Assert-MockCalled -CommandName "Write-SpectreHost" -Times 1 -Exactly + } + + It "accepts color" { + $testColor = Get-RandomColor + Read-SpectreConfirm -Prompt (Get-RandomString) -Color $testColor + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/prompts/Read-SpectreMultiSelection.tests.ps1 b/PwshSpectreConsole.Tests/prompts/Read-SpectreMultiSelection.tests.ps1 new file mode 100644 index 00000000..be68c512 --- /dev/null +++ b/PwshSpectreConsole.Tests/prompts/Read-SpectreMultiSelection.tests.ps1 @@ -0,0 +1,72 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Read-SpectreMultiSelection" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testTitle = Get-RandomString + $testPageSize = Get-Random -Minimum 1 -Maximum 10 + $testColor = Get-RandomColor + $itemsToBeSelectedNames = $null + Mock Invoke-SpectrePromptAsync { + $Prompt | Should -BeOfType [Spectre.Console.MultiSelectionPrompt[string]] + $Prompt.Title | Should -Be $testTitle + $Prompt.PageSize | Should -Be $testPageSize + $Prompt.HighlightStyle.Foreground.ToMarkup() | Should -Be $testColor + + return $itemsToBeSelectedNames + } + } + + It "prompts and allows selection" { + $itemsToBeSelectedNames = @("toBeSelected") + $choices = (Get-RandomList) + $itemsToBeSelectedNames + Read-SpectreMultiSelection -Title $testTitle -Choices $choices -PageSize $testPageSize -Color $testColor | Should -Be $itemsToBeSelectedNames + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "prompts and allows multiple selection" { + $itemsToBeSelectedNames = @("toBeSelected", "also to be selected") + $choices = $itemsToBeSelectedNames + (Get-RandomList) + Read-SpectreMultiSelection -Title $testTitle -Choices $choices -PageSize $testPageSize -Color $testColor | Should -Be $itemsToBeSelectedNames + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "throws with duplicate labels" { + { Read-SpectreMultiSelection -Title $testTitle -Choices @("same", "same") -PageSize $testPageSize -Color $testColor } | Should -Throw + } + + It "throws with object choices without a ChoiceLabelProperty" { + $choices = @( + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString }, + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString } + ) + { Read-SpectreMultiSelection -Title $testTitle -Choices $choices -PageSize $testPageSize -Color $testColor } | Should -Throw + } + + It "prompts with an object input and allows selection" { + $itemsToBeSelectedNames = @("toBeSelected") + $itemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemsToBeSelectedNames[0]; Other = Get-RandomString } + Read-SpectreMultiSelection -Title $testTitle -ChoiceLabelProperty "ColumnToSelectFrom" -PageSize $testPageSize -Color $testColor -Choices @( + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString }, + $itemToBeSelected, + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString } + ) | Should -Be $itemToBeSelected + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "prompts with an object input and allows multiple selection" { + $itemsToBeSelectedNames = @("toBeSelected", "also to be selected") + $itemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemsToBeSelectedNames[0]; Other = Get-RandomString } + $anotherItemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemsToBeSelectedNames[1]; Other = Get-RandomString } + Read-SpectreMultiSelection -Title $testTitle -ChoiceLabelProperty "ColumnToSelectFrom" -PageSize $testPageSize -Color $testColor -Choices @( + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString }, + $itemToBeSelected, + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString }, + $anotherItemToBeSelected + ) | Should -Be @($itemToBeSelected, $anotherItemToBeSelected) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/prompts/Read-SpectreMultiSelectionGrouped.tests.ps1 b/PwshSpectreConsole.Tests/prompts/Read-SpectreMultiSelectionGrouped.tests.ps1 new file mode 100644 index 00000000..07648213 --- /dev/null +++ b/PwshSpectreConsole.Tests/prompts/Read-SpectreMultiSelectionGrouped.tests.ps1 @@ -0,0 +1,101 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Read-SpectreMultiSelectionGrouped" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testTitle = Get-RandomString + $testPageSize = Get-Random -Minimum 1 -Maximum 10 + $testColor = Get-RandomColor + $itemsToBeSelectedNames = $null + Mock Invoke-SpectrePromptAsync { + $Prompt | Should -BeOfType [Spectre.Console.MultiSelectionPrompt[string]] + $Prompt.Title | Should -Be $testTitle + $Prompt.PageSize | Should -Be $testPageSize + $Prompt.HighlightStyle.Foreground.ToMarkup() | Should -Be $testColor + + return $itemsToBeSelectedNames + } + } + + It "prompts and allows selection" { + $itemsToBeSelectedNames = @("toBeSelected") + $testChoices = @(Get-RandomList -Generator { + return @{ + Name = Get-RandomString + Choices = Get-RandomList + } + }) + $testChoices += @{ + Name = "Group with selection" + Choices = @(Get-RandomList) + $itemsToBeSelectedNames + } + Read-SpectreMultiSelectionGrouped -Title $testTitle -Choices $testChoices -PageSize $testPageSize -Color $testColor | Should -Be $itemsToBeSelectedNames + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "prompts and allows multiple selection" { + $itemsToBeSelectedNames = @("toBeSelected", "also to be selected") + $testChoices = @(Get-RandomList -Generator { + return @{ + Name = Get-RandomString + Choices = "toBeSelected" + (Get-RandomList) + } + }) + $testChoices += @{ + Name = "Group with selection" + Choices = @(Get-RandomList) + "also to be selected" + } + Read-SpectreMultiSelectionGrouped -Title $testTitle -Choices $testChoices -PageSize $testPageSize -Color $testColor | Should -Be $itemsToBeSelectedNames + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "throws with duplicate labels" { + { Read-SpectreMultiSelectionGrouped -Title $testTitle -Choices @("same", "same") -PageSize $testPageSize -Color $testColor } | Should -Throw + } + + It "throws with object choices and no ChoiceLabelProperty" { + $testChoices = Get-RandomList -Generator { + return @{ + Name = Get-RandomString + Choices = (Get-RandomList -Generator { + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString } + }) + } + } + { Read-SpectreMultiSelectionGrouped -Title $testTitle -Choices $testChoices -PageSize $testPageSize -Color $testColor } | Should -Throw + } + + It "prompts with an object input and allows selection" { + $itemsToBeSelectedNames = @("toBeSelected") + $itemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemsToBeSelectedNames[0]; Other = Get-RandomString } + $testChoices = @( + @{ + Name = Get-RandomString + Choices = @(Get-RandomList -Generator { + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString } + }) + $itemToBeSelected + } + ) + Read-SpectreMultiSelectionGrouped -Title $testTitle -ChoiceLabelProperty "ColumnToSelectFrom" -Choices $testChoices -PageSize $testPageSize -Color $testColor | Should -Be $itemToBeSelected + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "prompts with an object input and allows multiple selection" { + $itemsToBeSelectedNames = @("toBeSelected", "also to be selected") + $itemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemsToBeSelectedNames[0]; Other = Get-RandomString } + $anotherItemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemsToBeSelectedNames[1]; Other = Get-RandomString } + $testChoices = @( + @{ + Name = Get-RandomString + Choices = @($itemToBeSelected) + (Get-RandomList -Generator { + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString } + }) + $anotherItemToBeSelected + } + ) + Read-SpectreMultiSelectionGrouped -Title $testTitle -ChoiceLabelProperty "ColumnToSelectFrom" -Choices $testChoices -PageSize $testPageSize -Color $testColor | Should -Be @($itemToBeSelected, $anotherItemToBeSelected) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/prompts/Read-SpectrePause.tests.ps1 b/PwshSpectreConsole.Tests/prompts/Read-SpectrePause.tests.ps1 new file mode 100644 index 00000000..a2ba5ac3 --- /dev/null +++ b/PwshSpectreConsole.Tests/prompts/Read-SpectrePause.tests.ps1 @@ -0,0 +1,32 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Read-SpectrePause" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testMessage = $null + Mock Write-SpectreHost { + if($testMessage) { + $Message | Should -Be $testMessage + } + } + Mock Clear-InputQueue + Mock Set-CursorPosition + Mock Write-Host + Mock Read-Host + } + + It "displays" { + Read-SpectrePause + Assert-MockCalled -CommandName "Read-Host" -Times 1 -Exactly + } + + It "displays a custom message" { + $testMessage = Get-RandomString + Write-Debug $testMessage + Read-SpectrePause -Message $testMessage + Assert-MockCalled -CommandName "Read-Host" -Times 1 -Exactly + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/prompts/Read-SpectreSelection.tests.ps1 b/PwshSpectreConsole.Tests/prompts/Read-SpectreSelection.tests.ps1 new file mode 100644 index 00000000..aaa72319 --- /dev/null +++ b/PwshSpectreConsole.Tests/prompts/Read-SpectreSelection.tests.ps1 @@ -0,0 +1,42 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Read-SpectreSelection" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testTitle = Get-RandomString + $testPageSize = Get-Random -Minimum 1 -Maximum 10 + $testColor = Get-RandomColor + $itemToBeSelectedName = $null + Mock Invoke-SpectrePromptAsync { + $Prompt | Should -BeOfType [Spectre.Console.SelectionPrompt[string]] + $Prompt.Title | Should -Be $testTitle + $Prompt.PageSize | Should -Be $testPageSize + $Prompt.HighlightStyle.Foreground.ToMarkup() | Should -Be $testColor + + return $itemToBeSelectedName + } + } + + It "prompts" { + Read-SpectreSelection -Title $testTitle -Choices (Get-RandomList) -PageSize $testPageSize -Color $testColor + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "throws with duplicate labels" { + { Read-SpectreSelection -Title $testTitle -Choices @("same", "same") -PageSize $testPageSize -Color $testColor } | Should -Throw + } + + It "prompts with an object input" { + $itemToBeSelectedName = Get-RandomString + $itemToBeSelected = [PSCustomObject]@{ ColumnToSelectFrom = $itemToBeSelectedName; Other = Get-RandomString } + Read-SpectreSelection -Title $testTitle -ChoiceLabelProperty "ColumnToSelectFrom" -PageSize $testPageSize -Color $testColor -Choices @( + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString }, + $itemToBeSelected, + [PSCustomObject]@{ ColumnToSelectFrom = Get-RandomString; Other = Get-RandomString } + ) | Should -Be $itemToBeSelected + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/prompts/Read-SpectreText.tests.ps1 b/PwshSpectreConsole.Tests/prompts/Read-SpectreText.tests.ps1 new file mode 100644 index 00000000..2925f525 --- /dev/null +++ b/PwshSpectreConsole.Tests/prompts/Read-SpectreText.tests.ps1 @@ -0,0 +1,38 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Read-SpectreText" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testAnswerColor = $null + Mock Invoke-SpectrePromptAsync { + $Prompt | Should -BeOfType [Spectre.Console.TextPrompt[string]] + if($Prompt.PromptStyle.Foreground) { + $Prompt.PromptStyle.Foreground.ToMarkup() | Should -Be $testAnswerColor + } + } + } + + It "prompts" { + Read-SpectreText -Question (Get-RandomString) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "prompts with a default answer" { + Read-SpectreText -Question (Get-RandomString) -DefaultAnswer (Get-RandomString) + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "can allow an empty answer" { + Read-SpectreText -AllowEmpty + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + + It "can use a colored prompt" { + $testAnswerColor = Get-RandomColor + Read-SpectreText -Question (Get-RandomString) -AnswerColor $testAnswerColor + Assert-MockCalled -CommandName "Invoke-SpectrePromptAsync" -Times 1 -Exactly + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/writing/Get-SpectreEscapedText.tests.ps1 b/PwshSpectreConsole.Tests/writing/Get-SpectreEscapedText.tests.ps1 new file mode 100644 index 00000000..ad372b7d --- /dev/null +++ b/PwshSpectreConsole.Tests/writing/Get-SpectreEscapedText.tests.ps1 @@ -0,0 +1,20 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Get-SpectreEscapedText" { + InModuleScope "PwshSpectreConsole" { + + It "formats a busted string" { + Get-SpectreEscapedText -Text "][[][]]][[][][][" | Should -Be "]][[[[]][[]]]]]][[[[]][[]][[]][[" + } + + It "handles pipelined input" { + "[[][]]][[][][]" | Get-SpectreEscapedText | Should -Be "[[[[]][[]]]]]][[[[]][[]][[]]" + } + + It "leaves emoji alone, unfortunately these aren't escaped in spectre console" { + "[[][]]][[]:zany_face:[][]" | Get-SpectreEscapedText | Should -Be "[[[[]][[]]]]]][[[[]]:zany_face:[[]][[]]" + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/writing/Write-SpectreCalender.tests.ps1 b/PwshSpectreConsole.Tests/writing/Write-SpectreCalender.tests.ps1 new file mode 100644 index 00000000..b22f0abb --- /dev/null +++ b/PwshSpectreConsole.Tests/writing/Write-SpectreCalender.tests.ps1 @@ -0,0 +1,78 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Write-SpectreCalendar" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + + $testBorder = 'Markdown' + $testColor = Get-RandomColor + Write-Debug $testBorder + Write-Debug $testColor + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + } + + It "writes calendar for a date" { + Write-SpectreCalendar -Date "2024-01-01" -Culture "en-us" -Border $testBorder -Color $testColor + $sample = $testConsole.Output + $object = $sample -split '\r?\n' + $object[0] | Should -Match 'January\s+2024' + $rawdays = $object[2] + $days = $rawdays -split '\|' | Get-AnsiEscapeSequence | ForEach-Object { + if (-Not [String]::IsNullOrWhiteSpace($_.Clean)) { + $_.Clean -replace '\s+' + } + } + $answer = (Get-Culture -Name en-us).DateTimeFormat.AbbreviatedDayNames + # $days | Should -Be @('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat') + $days | Should -Be $answer + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "writes calendar for a date with events" { + $events = @{ + '2022-03-10' = 'Event 1' + '2022-03-20' = 'Event 2' + } + Write-SpectreCalendar -Date "2024-03-01" -Events $events -Culture "en-us" -Border Markdown -Color $testColor + $sample = $testConsole.Output + $sample | Should -Match 'March\s+2024' + $sample | Should -Match 'Event 1' + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 2 -Exactly + } + + It "writes calendar for a date with something else going on" { + Write-SpectreCalendar -Date 2024-07-01 -HideHeader -Border Markdown -Color $testColor + $sample = $testConsole.Output + $object = $sample -split '\r?\n' | Select-Object -Skip 1 -SkipLast 3 + $object.count | Should -Be 7 + [string[]]$results = 1..31 + $object | Select-Object -Skip 2 | ForEach-Object { + $_ -split '\|' | Get-AnsiEscapeSequence | ForEach-Object { + if (-Not [String]::IsNullOrWhiteSpace($_.Clean)) { + $_.Clean -replace '\s+' | Should -BeIn $results + } + } + } + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + $events = @{ + '2022-03-10' = 'Event 1' + '2022-03-20' = 'Event 2' + } + $culture = Get-Culture -Name "en-US" + Write-SpectreCalendar -Date 2024-07-01 -Culture $culture -Events $events -Border "Rounded" -Color "SpringGreen3" + { Assert-OutputMatchesSnapshot -SnapshotName "Write-SpectreCalendar" -Output $testConsole.Output } | Should -Not -Throw + } + } +} diff --git a/PwshSpectreConsole.Tests/writing/Write-SpectreFigletText.tests.ps1 b/PwshSpectreConsole.Tests/writing/Write-SpectreFigletText.tests.ps1 new file mode 100644 index 00000000..a43edcf1 --- /dev/null +++ b/PwshSpectreConsole.Tests/writing/Write-SpectreFigletText.tests.ps1 @@ -0,0 +1,45 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Write-SpectreFigletText" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + [Spectre.Console.Testing.TestConsoleExtensions]::Width($testConsole, 180) + $testColor = Get-RandomColor + $testAlignment = Get-RandomJustify + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.FigletText] + $RenderableObject.Justification | Should -Be $testAlignment + $RenderableObject.Color.ToMarkup() | Should -Be $testColor + + $testConsole.Write($RenderableObject) + } + } + + It "writes figlet text" { + Write-SpectreFigletText -Text (Get-RandomString) -Alignment $testAlignment -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + } + + It "throws when the font file isn't found" { + { Write-SpectreFigletText -FigletFontPath "notfound.flf" } | Should -Throw + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 0 -Exactly + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + $testTitle = "f i glett" + $testAlignment = "Center" + $testColor = "DarkSeaGreen1_1" + + Write-SpectreFigletText -Text $testTitle -Alignment $testAlignment -Color $testColor + + { Assert-OutputMatchesSnapshot -SnapshotName "Write-SpectreFigletText" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/writing/Write-SpectreHost.tests.ps1 b/PwshSpectreConsole.Tests/writing/Write-SpectreHost.tests.ps1 new file mode 100644 index 00000000..8c54bc35 --- /dev/null +++ b/PwshSpectreConsole.Tests/writing/Write-SpectreHost.tests.ps1 @@ -0,0 +1,46 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Write-SpectreHost" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + $testMessage = Get-RandomString + $testMessage | Out-Null + Mock Write-SpectreHostInternalMarkup { + $Message | Should -Be $testMessage + [AnsiConsoleExtensions]::Markup($testConsole, $Message) + } + Mock Write-SpectreHostInternalMarkupLine { + $Message | Should -Be $testMessage + [AnsiConsoleExtensions]::MarkupLine($testConsole, $Message) + } + } + + It "writes a message" { + Write-SpectreHost -Message $testMessage + Assert-MockCalled -CommandName "Write-SpectreHostInternalMarkupLine" -Times 1 -Exactly + $testConsole.Output.Split("`n").Count | Should -Be 2 + } + + It "accepts pipeline input" { + $testMessage | Write-SpectreHost + Assert-MockCalled -CommandName "Write-SpectreHostInternalMarkupLine" -Times 1 -Exactly + $testConsole.Output.Split("`n").Count | Should -Be 2 + } + + It "handles nonewline" { + Write-SpectreHost -Message $testMessage -NoNewline + Assert-MockCalled -CommandName "Write-SpectreHostInternalMarkup" -Times 1 -Exactly + $testConsole.Output.Split("`n").Count | Should -Be 1 + } + + It "Should match the snapshot" { + $testMessage = "[#00ff00]Hello[/], [DeepSkyBlue3_1]World![/] :smiling_face_with_sunglasses: Yay!" + Write-SpectreHost $testMessage + { Assert-OutputMatchesSnapshot -SnapshotName "Write-SpectreHost" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole.Tests/writing/Write-SpectreRule.tests.ps1 b/PwshSpectreConsole.Tests/writing/Write-SpectreRule.tests.ps1 new file mode 100644 index 00000000..40b53c9a --- /dev/null +++ b/PwshSpectreConsole.Tests/writing/Write-SpectreRule.tests.ps1 @@ -0,0 +1,42 @@ +Remove-Module PwshSpectreConsole -Force -ErrorAction SilentlyContinue +Import-Module "$PSScriptRoot\..\..\PwshSpectreConsole\PwshSpectreConsole.psd1" -Force +Import-Module "$PSScriptRoot\..\TestHelpers.psm1" -Force + +Describe "Write-SpectreRule" { + InModuleScope "PwshSpectreConsole" { + BeforeEach { + $testConsole = [Spectre.Console.Testing.TestConsole]::new() + $testConsole.EmitAnsiSequences = $true + [Spectre.Console.Testing.TestConsoleExtensions]::Width($testConsole, 140) + $testColor = Get-RandomColor + Write-Debug $testColor + $justification = Get-RandomJustify + Mock Write-AnsiConsole { + $RenderableObject | Should -BeOfType [Spectre.Console.Rule] + $RenderableObject.Justification | Should -Be $justification + + $testConsole.Write($RenderableObject) + } + } + + It "writes a rule" { + $randomString = Get-RandomString + Write-SpectreRule -Title $randomString -Alignment $justification -Color $testColor + Assert-MockCalled -CommandName "Write-AnsiConsole" -Times 1 -Exactly + $testConsole.Output | Should -BeLike "*$randomString*" + } + + It "Should match the snapshot" { + Mock Write-AnsiConsole { + $testConsole.Write($RenderableObject) + } + $testTitle = "yo, this is a test rule" + $justification = "Center" + $testColor = "Red" + + Write-SpectreRule -Title $testTitle -Alignment $justification -Color $testColor + + { Assert-OutputMatchesSnapshot -SnapshotName "Write-SpectreRule" -Output $testConsole.Output } | Should -Not -Throw + } + } +} \ No newline at end of file diff --git a/PwshSpectreConsole/Build.ps1 b/PwshSpectreConsole/Build.ps1 index b49495fb..c0880fdd 100644 --- a/PwshSpectreConsole/Build.ps1 +++ b/PwshSpectreConsole/Build.ps1 @@ -1,15 +1,23 @@ param ( - [string] $Version = "0.47.0" + [string] $Version = "0.48.0" ) function Install-SpectreConsole { param ( [string] $InstallLocation, + [string] $TestingInstallLocation, [string] $Version ) New-Item -Path $InstallLocation -ItemType "Directory" -Force | Out-Null + $libPath = Join-Path $TestingInstallLocation "Spectre.Console.Testing" + New-Item -Path $libPath -ItemType "Directory" -Force | Out-Null + $downloadLocation = Join-Path $libPath "download.zip" + Invoke-WebRequest "https://www.nuget.org/api/v2/package/Spectre.Console.Testing/$Version" -OutFile $downloadLocation -UseBasicParsing + Expand-Archive $downloadLocation $libPath -Force + Remove-Item $downloadLocation + $libPath = Join-Path $InstallLocation "Spectre.Console" New-Item -Path $libPath -ItemType "Directory" -Force | Out-Null $downloadLocation = Join-Path $libPath "download.zip" @@ -34,11 +42,22 @@ function Install-SpectreConsole { Invoke-WebRequest "https://www.nuget.org/api/v2/package/SixLabors.ImageSharp/$imageSharpVersion" -OutFile $downloadLocation -UseBasicParsing Expand-Archive $downloadLocation $libPath -Force Remove-Item $downloadLocation + + $libPath = Join-Path $InstallLocation "Spectre.Console.Json" + New-Item -Path $libPath -ItemType "Directory" -Force | Out-Null + $downloadLocation = Join-Path $libPath "download.zip" + Invoke-WebRequest "https://www.nuget.org/api/v2/package/Spectre.Console.Json/$Version" -OutFile $downloadLocation -UseBasicParsing + Expand-Archive $downloadLocation $libPath -Force + Remove-Item $downloadLocation } Write-Host "Downloading Spectre.Console version $Version" $installLocation = (Join-Path $PSScriptRoot "packages") +$testingInstallLocation = (Join-Path $PSScriptRoot ".." "PwshSpectreConsole.Tests" "packages") if(Test-Path $installLocation) { Remove-Item $installLocation -Recurse -Force } -Install-SpectreConsole -InstallLocation $installLocation -Version $Version \ No newline at end of file +if(Test-Path $testingInstallLocation) { + Remove-Item $testingInstallLocation -Recurse -Force +} +Install-SpectreConsole -InstallLocation $installLocation -TestingInstallLocation $testingInstallLocation -Version $Version diff --git a/PwshSpectreConsole/PwshSpectreConsole.psd1 b/PwshSpectreConsole/PwshSpectreConsole.psd1 index 3dac5c37..f351862f 100644 --- a/PwshSpectreConsole/PwshSpectreConsole.psd1 +++ b/PwshSpectreConsole/PwshSpectreConsole.psd1 @@ -3,7 +3,7 @@ # # Generated by: Shaun Lawrie # -# Generated on: 01/03/2024 +# Generated on: 02/21/2024 # @{ @@ -12,7 +12,7 @@ RootModule = 'PwshSpectreConsole' # Version number of this module. -ModuleVersion = '1.5.0' +ModuleVersion = '1.6.0' # Supported PSEditions # CompatiblePSEditions = @() @@ -57,7 +57,8 @@ PowerShellVersion = '7.0' RequiredAssemblies = '.\packages\Spectre.Console\lib\netstandard2.0\Spectre.Console.dll', '.\packages\Spectre.Console.ImageSharp\lib\netstandard2.0\Spectre.Console.ImageSharp.dll', - '.\packages\SixLabors.ImageSharp\lib\netstandard2.0\SixLabors.ImageSharp.dll' + '.\packages\SixLabors.ImageSharp\lib\netstandard2.0\SixLabors.ImageSharp.dll', + '.\packages\Spectre.Console.Json\lib\netstandard2.0\Spectre.Console.Json.dll' # Script files (.ps1) that are run in the caller's environment prior to importing this module. # ScriptsToProcess = @() @@ -83,7 +84,8 @@ FunctionsToExport = 'Add-SpectreJob', 'Format-SpectreBarChart', 'Start-SpectreDemo', 'Wait-SpectreJobs', 'Write-SpectreFigletText', 'Write-SpectreHost', 'Write-SpectreRule', 'Read-SpectreConfirm', 'New-SpectreChartItem', 'Invoke-SpectreScriptBlockQuietly', - 'Get-SpectreDemoColors', 'Get-SpectreDemoEmoji' + 'Get-SpectreDemoColors', 'Get-SpectreDemoEmoji', 'Format-SpectreJson', + 'Write-SpectreCalendar' # 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. CmdletsToExport = @() @@ -92,7 +94,7 @@ CmdletsToExport = @() VariablesToExport = '*' # 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. -AliasesToExport = @() +AliasesToExport = 'fst', 'fsj' # DSC resources to export from this module # DscResourcesToExport = @() diff --git a/PwshSpectreConsole/PwshSpectreConsole.psm1 b/PwshSpectreConsole/PwshSpectreConsole.psm1 index 1ba47eb6..7dcd18ce 100644 --- a/PwshSpectreConsole/PwshSpectreConsole.psm1 +++ b/PwshSpectreConsole/PwshSpectreConsole.psm1 @@ -1,7 +1,10 @@ using module ".\private\completions\Completers.psm1" +using namespace Spectre.Console -$script:AccentColor = [Spectre.Console.Color]::Blue -$script:DefaultValueColor = [Spectre.Console.Color]::Grey +$script:AccentColor = [Color]::Blue +$script:DefaultValueColor = [Color]::Grey +$script:DefaultTableHeaderColor = [Color]::Default +$script:DefaultTableTextColor = [Color]::Default foreach ($directory in @('private', 'public')) { Get-ChildItem -Path "$PSScriptRoot\$directory\*.ps1" -Recurse | ForEach-Object { diff --git a/PwshSpectreConsole/private/Add-PwshSpectreConsole.VTCodes.ps1 b/PwshSpectreConsole/private/Add-PwshSpectreConsole.VTCodes.ps1 new file mode 100644 index 00000000..61478d25 --- /dev/null +++ b/PwshSpectreConsole/private/Add-PwshSpectreConsole.VTCodes.ps1 @@ -0,0 +1,5 @@ +function Add-PwshSpectreConsole.VTCodes { + if (-Not ('PwshSpectreConsole.VTCodes.Parser' -as [type])) { + Add-Type -Path (Join-Path $PSScriptRoot classes 'PwshSpectreConsole.VTCodes.cs') + } +} diff --git a/PwshSpectreConsole/private/Add-SpectreTreeNode.ps1 b/PwshSpectreConsole/private/Add-SpectreTreeNode.ps1 index de8bbd59..91cc3883 100644 --- a/PwshSpectreConsole/private/Add-SpectreTreeNode.ps1 +++ b/PwshSpectreConsole/private/Add-SpectreTreeNode.ps1 @@ -1,3 +1,5 @@ +using namespace Spectre.Console + <# .SYNOPSIS Recursively adds child nodes to a parent node in a Spectre.Console tree. @@ -17,13 +19,13 @@ See Format-SpectreTree for usage. function Add-SpectreTreeNode { param ( [Parameter(Mandatory)] - [Spectre.Console.IHasTreeNodes] $Node, + [IHasTreeNodes] $Node, [Parameter(Mandatory)] [array] $Children ) foreach($child in $Children) { - $newNode = [Spectre.Console.HasTreeNodeExtensions]::AddNode($Node, $child.Label) + $newNode = [HasTreeNodeExtensions]::AddNode($Node, $child.Label) if($child.Children.Count -gt 0) { Add-SpectreTreeNode -Node $newNode -Children $child.Children } diff --git a/PwshSpectreConsole/private/Add-TableColumns.ps1 b/PwshSpectreConsole/private/Add-TableColumns.ps1 new file mode 100644 index 00000000..ef3a1290 --- /dev/null +++ b/PwshSpectreConsole/private/Add-TableColumns.ps1 @@ -0,0 +1,49 @@ +using namespace Spectre.Console + +function Add-TableColumns { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Table] $table, + [Collections.Specialized.OrderedDictionary] $FormatData, + [String] $Title, + [Color] $Color = [Color]::Default, + [Switch] $Scalar, + [Switch] $Wrap + ) + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + if ($Scalar) { + if ($Title) { + Write-Debug "Adding column with title: $Title" + $table.AddColumn("[$($Color.ToMarkup())]$Title[/]") | Out-Null + } + else { + Write-Debug "Adding column with title: Value" + $table.AddColumn("[$($Color.ToMarkup())]Value[/]") | Out-Null + } + if (-Not $Wrap) { + $table.Columns[-1].NoWrap = $true + } + } + else { + foreach ($key in $FormatData.keys) { + $lookup = $FormatData[$key] + Write-Debug "Adding column from formatdata: $($lookup.GetEnumerator())" + $table.AddColumn("[$($Color.ToMarkup())]$($lookup.Label)[/]") | Out-Null + $table.Columns[-1].Padding = [Padding]::new(1, 0, 1, 0) + if ($lookup.width -gt 0) { + # width 0 is autosize, select the last entry in the column list + $table.Columns[-1].Width = $lookup.Width + } + if ($lookup.Alignment -ne 'undefined') { + $table.Columns[-1].Alignment = [Justify]::$lookup.Alignment + } + if (-Not $Wrap) { + # https://github.com/spectreconsole/spectre.console/issues/1185 + # leaving it in as it will probably get fixed, has no effect on output yet. + $table.Columns[-1].NoWrap = $true + } + } + } + return $table +} diff --git a/PwshSpectreConsole/private/Clear-InputQueue.ps1 b/PwshSpectreConsole/private/Clear-InputQueue.ps1 new file mode 100644 index 00000000..04ad5a32 --- /dev/null +++ b/PwshSpectreConsole/private/Clear-InputQueue.ps1 @@ -0,0 +1,7 @@ +# Required for unit test mocking in Read-SpectrePause +# This drains the input buffer so if the user has pressed enter or any other key, it won't be read by the following prompt +function Clear-InputQueue { + while ([System.Console]::KeyAvailable) { + $null = [System.Console]::ReadKey($true) + } +} \ No newline at end of file diff --git a/PwshSpectreConsole/private/Convert-ToSpectreColor.ps1 b/PwshSpectreConsole/private/Convert-ToSpectreColor.ps1 index e377ec80..6db1f626 100644 --- a/PwshSpectreConsole/private/Convert-ToSpectreColor.ps1 +++ b/PwshSpectreConsole/private/Convert-ToSpectreColor.ps1 @@ -1,43 +1,46 @@ -using module ".\completions\Completers.psm1" +using namespace Spectre.Console <# .SYNOPSIS -Converts a string representation of a color to a Spectre.Console.Color object. +Converts a string representation of a color to a Color object. .DESCRIPTION -This function takes a string representation of a color and converts it to a Spectre.Console.Color object. The input color can be in the form of a named color or a hexadecimal color code. +This function takes a string representation of a color and converts it to a Color object. The input color can be in the form of a named color or a hexadecimal color code. .PARAMETER Color The color to convert. This parameter is mandatory and accepts input from the pipeline. .EXAMPLE -# This example converts the string 'red' to a Spectre.Console.Color object. +# This example converts the string 'red' to a Color object. 'red' | Convert-ToSpectreColor .EXAMPLE -# This example converts the hexadecimal color code '#FF0000' to a Spectre.Console.Color object. +# This example converts the hexadecimal color code '#FF0000' to a Color object. '#FF0000' | Convert-ToSpectreColor .EXAMPLE # This example passes through and returns the original color, it's needed for backwards compatibility with the older way of doing things in this library. -[Spectre.Console.Color]::Salmon1 | Convert-ToSpectreColor +[Color]::Salmon1 | Convert-ToSpectreColor #> function Convert-ToSpectreColor { param ( [Parameter(ValueFromPipeline, Mandatory)] - [ValidateSpectreColor()] - [string] $Color + [object] $Color ) try { + # Just return the console color object + if($Color -is [Color]) { + return $Color + } # Already validated in validation attribute if($Color.StartsWith("#")) { $hexString = $Color -replace '^#', '' $hexBytes = [System.Convert]::FromHexString($hexString) - return [Spectre.Console.Color]::new($hexBytes[0], $hexBytes[1], $hexBytes[2]) + return [Color]::new($hexBytes[0], $hexBytes[1], $hexBytes[2]) } # Validated in attribute as a real color already - return [Spectre.Console.Color]::$Color + return [Color]::$Color } catch { return $script:AccentColor } diff --git a/PwshSpectreConsole/private/ConvertFrom-ConsoleColor.ps1 b/PwshSpectreConsole/private/ConvertFrom-ConsoleColor.ps1 new file mode 100644 index 00000000..876e6dbb --- /dev/null +++ b/PwshSpectreConsole/private/ConvertFrom-ConsoleColor.ps1 @@ -0,0 +1,41 @@ +function ConvertFrom-ConsoleColor { + param( + [int]$Color + ) + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + $consoleColors = @{ + 30 = 'Black' + 31 = 'DarkRed' + 32 = 'DarkGreen' + 33 = 'DarkYellow' + 34 = 'DarkBlue' + 35 = 'DarkMagenta' + 36 = 'DarkCyan' + 37 = 'Gray' + 40 = 'Black' + 41 = 'DarkRed' + 42 = 'DarkGreen' + 43 = 'DarkYellow' + 44 = 'DarkBlue' + 45 = 'DarkMagenta' + 46 = 'DarkCyan' + 47 = 'Gray' + 90 = 'DarkGray' + 91 = 'Red' + 92 = 'Green' + 93 = 'Yellow' + 94 = 'Blue' + 95 = 'Magenta' + 96 = 'Cyan' + 97 = 'White' + 100 = 'DarkGray' + 101 = 'Red' + 102 = 'Green' + 103 = 'Yellow' + 104 = 'Blue' + 105 = 'Magenta' + 106 = 'Cyan' + 107 = 'White' + } + return $consoleColors[$Color] +} diff --git a/PwshSpectreConsole/private/ConvertTo-SpectreDecoration.ps1 b/PwshSpectreConsole/private/ConvertTo-SpectreDecoration.ps1 new file mode 100644 index 00000000..bf9b8373 --- /dev/null +++ b/PwshSpectreConsole/private/ConvertTo-SpectreDecoration.ps1 @@ -0,0 +1,62 @@ +using namespace Spectre.Console + +function ConvertTo-SpectreDecoration { + param( + [Parameter(Mandatory)] + [String]$String, + [switch]$AllowMarkup + ) + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + if (-Not ('PwshSpectreConsole.VTCodes.Parser' -as [type])) { + Add-PwshSpectreConsole.VTCodes + } + $lookup = [PwshSpectreConsole.VTCodes.Parser]::Parse($String) + $ht = @{ + decoration = [Decoration]::None + fg = [Color]::Default + bg = [Color]::Default + } + foreach ($item in $lookup) { + # Write-Debug "Type: $($item.type) Value: $($item.value) Position: $($item.position) Color: $($item.color)" + if ($item.value -eq 'None') { + continue + } + $conversion = switch ($item.type) { + '4bit' { + if ($item.value -gt 0 -and $item.value -le 15) { + [Color]::FromConsoleColor($item.value) + } + else { + # spectre doesn't appear to have a way to convert from 4bit. + # e.g all $PSStyle colors 30-37, 40-47 and 90-97, 100-107 + # this will return the closest color in 8bit. + [Color]::FromConsoleColor((ConvertFrom-ConsoleColor $item.value)) + } + } + '8bit' { + [Color]::FromInt32($item.value) + } + '24bit' { + [Color]::new($item.value.Red, $item.value.Green, $item.value.Blue) + } + 'decoration' { + [Decoration]::Parse([Decoration], $item.Value, $true) + } + } + if ($item.type -eq 'decoration') { + $ht.decoration = $conversion + } + if ($item.position -eq 'foreground') { + $ht.fg = $conversion + } + elseif ($item.position -eq 'background') { + $ht.bg = $conversion + } + } + $String = [System.Management.Automation.Host.PSHostUserInterface]::GetOutputString($String, $false) + Write-Debug "Clean: '$String' deco: '$($ht.decoration)' fg: '$($ht.fg)' bg: '$($ht.bg)'" + if ($AllowMarkup) { + return [Markup]::new($String, [Style]::new($ht.fg, $ht.bg, $ht.decoration)) + } + return [Text]::new($String, [Style]::new($ht.fg, $ht.bg, $ht.decoration)) +} diff --git a/PwshSpectreConsole/private/Get-HostHeight.ps1 b/PwshSpectreConsole/private/Get-HostHeight.ps1 new file mode 100644 index 00000000..4596e3af --- /dev/null +++ b/PwshSpectreConsole/private/Get-HostHeight.ps1 @@ -0,0 +1,4 @@ +# Required for unit test mocking +function Get-HostHeight { + return $Host.UI.RawUI.WindowSize.Height +} \ No newline at end of file diff --git a/PwshSpectreConsole/private/Get-HostWidth.ps1 b/PwshSpectreConsole/private/Get-HostWidth.ps1 new file mode 100644 index 00000000..52a9263d --- /dev/null +++ b/PwshSpectreConsole/private/Get-HostWidth.ps1 @@ -0,0 +1,4 @@ +# Required for unit test mocking +function Get-HostWidth { + return $Host.UI.RawUI.BufferSize.Width +} \ No newline at end of file diff --git a/PwshSpectreConsole/private/Get-SpectreProfile.ps1 b/PwshSpectreConsole/private/Get-SpectreProfile.ps1 new file mode 100644 index 00000000..17ab4cdd --- /dev/null +++ b/PwshSpectreConsole/private/Get-SpectreProfile.ps1 @@ -0,0 +1,24 @@ +using namespace Spectre.Console + +function Get-SpectreProfile { + [CmdletBinding()] + param () + $object = [AnsiConsole]::Profile + return [PSCustomObject]@{ + Enrichers = $object.Enrichers -join ', ' + ColorSystem = $object.Capabilities.ColorSystem + Unicode = $object.Capabilities.Unicode + Ansi = $object.Capabilities.Ansi + Links = $object.Capabilities.Links + Legacy = $object.Capabilities.Legacy + Interactive = $object.Capabilities.Interactive + Terminal = $object.out.IsTerminal + Writer = $object.Out.Writer + Width = $object.Width + Height = $object.Height + Encoding = $object.Encoding.EncodingName + PSStyle = $PSStyle.OutputRendering + ConsoleOutputEncoding = [console]::OutputEncoding + ConsoleInputEncoding = [console]::InputEncoding + } +} diff --git a/PwshSpectreConsole/private/Get-TableHeader.ps1 b/PwshSpectreConsole/private/Get-TableHeader.ps1 new file mode 100644 index 00000000..69ab4f7c --- /dev/null +++ b/PwshSpectreConsole/private/Get-TableHeader.ps1 @@ -0,0 +1,40 @@ +function Get-TableHeader { + <# + ls | ft | Get-TableHeader + https://gist.github.com/Jaykul/9999be71ee68f3036dc2529c451729f4 + #> + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + $FormatStartData + ) + begin { + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + $alignment = @{ + 0 = 'undefined' + 1 = 'Left' + 2 = 'Center' + 3 = 'Right' + } + } + process { + if ($FormatStartData.Gettype().Name -eq 'FormatStartData') { + $properties = [ordered]@{} + $FormatStartData.shapeinfo.tablecolumninfolist | Where-Object { $_ } | ForEach-Object { + $Name = $_.Label ? $_.Label : $_.propertyName + $properties[$Name] = @{ + Label = $Name + Width = $_.width + Alignment = $alignment.Contains($_.alignment) ? $alignment[$_.alignment] : 'undefined' + HeaderMatchesProperty = $_.HeaderMatchesProperty + # PropertyName = $_.propertyName + } + } + if ($properties.Keys.Count -eq 0) { + Write-Debug "No properties found" + return $null + } + return $properties + } + } +} diff --git a/PwshSpectreConsole/private/Invoke-SpectrePromptAsync.ps1 b/PwshSpectreConsole/private/Invoke-SpectrePromptAsync.ps1 index 4c647620..36e224c8 100644 --- a/PwshSpectreConsole/private/Invoke-SpectrePromptAsync.ps1 +++ b/PwshSpectreConsole/private/Invoke-SpectrePromptAsync.ps1 @@ -1,3 +1,5 @@ +using namespace Spectre.Console + function Invoke-SpectrePromptAsync { <# .SYNOPSIS @@ -18,7 +20,7 @@ function Invoke-SpectrePromptAsync { ) $cts = [System.Threading.CancellationTokenSource]::new() try { - $task = $Prompt.ShowAsync([Spectre.Console.AnsiConsole]::Console, $cts.Token) + $task = $Prompt.ShowAsync([AnsiConsole]::Console, $cts.Token) while (-not $task.AsyncWaitHandle.WaitOne(200)) { # Waiting for the async task this way allows ctrl-c interrupts to continue to work within the single-threaded PowerShell world } diff --git a/PwshSpectreConsole/private/New-TableCell.ps1 b/PwshSpectreConsole/private/New-TableCell.ps1 new file mode 100644 index 00000000..43c11c2b --- /dev/null +++ b/PwshSpectreConsole/private/New-TableCell.ps1 @@ -0,0 +1,32 @@ +using namespace Spectre.Console + +function New-TableCell { + [cmdletbinding()] + param( + [Object] $String, + [Color] $Color = [Color]::Default, + [Switch] $AllowMarkup + ) + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + if ([String]::IsNullOrEmpty($String)) { + if ($AllowMarkup) { + return [Markup]::new(' ', [Style]::new($Color)) + } + return [Text]::new(' ', [Style]::new($Color)) + } + if (-Not [String]::IsNullOrEmpty($String.ToString())) { + if ($AllowMarkup) { + Write-Debug "New-TableCell ToString(), Markup, $($String.ToString())" + return [Markup]::new($String.ToString(), [Style]::new($Color)) + } + Write-Debug "New-TableCell ToString(), Text, $($String.ToString())" + return [Text]::new($String.ToString(), [Style]::new($Color)) + } + # just coerce to string. + if ($AllowMarkup) { + Write-Debug "New-TableCell [String], markup, $([String]$String)" + return [Markup]::new([String]$String, [Style]::new($Color)) + } + Write-Debug "New-TableCell [String], Text, $([String]$String)" + return [Text]::new([String]$String, [Style]::new($Color)) +} diff --git a/PwshSpectreConsole/private/New-TableRow.ps1 b/PwshSpectreConsole/private/New-TableRow.ps1 new file mode 100644 index 00000000..941c020c --- /dev/null +++ b/PwshSpectreConsole/private/New-TableRow.ps1 @@ -0,0 +1,32 @@ +function New-TableRow { + param( + [Parameter(Mandatory)] + [Object] $Entry, + [Color] $Color = [Color]::Default, + [Switch] $AllowMarkup, + [Switch] $Scalar + ) + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + $opts = @{ + AllowMarkup = $AllowMarkup + } + if ($scalar) { + New-TableCell -String $Entry -Color $Color @opts + } + else { + # simplified, should be faster. + $detectVT = '\x1b' + $rows = foreach ($cell in $Entry) { + if ([String]::IsNullOrEmpty($cell)) { + New-TableCell -Color $Color @opts + continue + } + if ($cell -match $detectVT) { + ConvertTo-SpectreDecoration -String $cell @opts + continue + } + New-TableCell -String $cell -Color $Color @opts + } + return $rows + } +} diff --git a/PwshSpectreConsole/private/Read-FigletFont.ps1 b/PwshSpectreConsole/private/Read-FigletFont.ps1 new file mode 100644 index 00000000..8ef9e5ee --- /dev/null +++ b/PwshSpectreConsole/private/Read-FigletFont.ps1 @@ -0,0 +1,16 @@ +using namespace Spectre.Console + +# Read in a figlet font or just return the default built-in one +function Read-FigletFont { + param ( + [string] $FigletFontPath + ) + $figletFont = [FigletFont]::Default + if($FigletFontPath) { + if(!(Test-Path $FigletFontPath)) { + throw "The specified Figlet font file '$FigletFontPath' does not exist" + } + $figletFont = [FigletFont]::Load($FigletFontPath) + } + return $figletFont +} \ No newline at end of file diff --git a/PwshSpectreConsole/private/Set-CursorPosition.ps1 b/PwshSpectreConsole/private/Set-CursorPosition.ps1 new file mode 100644 index 00000000..97edc225 --- /dev/null +++ b/PwshSpectreConsole/private/Set-CursorPosition.ps1 @@ -0,0 +1,9 @@ +# Required for unit test mocking in Read-SpectrePause +# Set the cursor to an arbitrary window position +function Set-CursorPosition { + param ( + [int] $X, + [int] $Y + ) + [Console]::SetCursorPosition($X, $Y) +} \ No newline at end of file diff --git a/PwshSpectreConsole/private/Start-AnsiConsoleProgress.ps1 b/PwshSpectreConsole/private/Start-AnsiConsoleProgress.ps1 index e6477a38..addd437e 100644 --- a/PwshSpectreConsole/private/Start-AnsiConsoleProgress.ps1 +++ b/PwshSpectreConsole/private/Start-AnsiConsoleProgress.ps1 @@ -1,3 +1,4 @@ +using namespace Spectre.Console <# .SYNOPSIS @@ -22,7 +23,7 @@ function Start-AnsiConsoleProgress { ) $resultVariableName = "AnsiConsoleProgressResult-$([guid]::NewGuid())" New-Variable -Name $resultVariableName -Scope "Script" - [Spectre.Console.AnsiConsole]::Progress().Start({ + [AnsiConsole]::Progress().Start({ param ( $ctx ) diff --git a/PwshSpectreConsole/private/Start-AnsiConsoleStatus.ps1 b/PwshSpectreConsole/private/Start-AnsiConsoleStatus.ps1 index 32ed6e0e..f7890df9 100644 --- a/PwshSpectreConsole/private/Start-AnsiConsoleStatus.ps1 +++ b/PwshSpectreConsole/private/Start-AnsiConsoleStatus.ps1 @@ -1,17 +1,19 @@ +using namespace Spectre.Console + function Start-AnsiConsoleStatus { param ( [Parameter(Mandatory)] [string] $Title, [Parameter(Mandatory)] - [Spectre.Console.Spinner] $Spinner, + [Spinner] $Spinner, [Parameter(Mandatory)] - [Spectre.Console.Style] $SpinnerStyle, + [Style] $SpinnerStyle, [Parameter(Mandatory)] [scriptblock] $ScriptBlock ) $resultVariableName = "AnsiConsoleStatusResult-$([guid]::NewGuid())" New-Variable -Name $resultVariableName -Scope "Script" - [Spectre.Console.AnsiConsole]::Status().Start($Title, { + [AnsiConsole]::Status().Start($Title, { param ( $ctx ) diff --git a/PwshSpectreConsole/private/Test-IsScalar.ps1 b/PwshSpectreConsole/private/Test-IsScalar.ps1 new file mode 100644 index 00000000..aff093ef --- /dev/null +++ b/PwshSpectreConsole/private/Test-IsScalar.ps1 @@ -0,0 +1,11 @@ +function Test-IsScalar { + [CmdletBinding()] + param( + $Value + ) + Write-Debug "Module: $($ExecutionContext.SessionState.Module.Name) Command: $($MyInvocation.MyCommand.Name) Param: $($PSBoundParameters.GetEnumerator())" + if ($Value -is [System.Collections.IEnumerable] -and $Value -isnot [string]) { + $Value = $Value | Select-Object -First 1 + } + return $Value -is [System.ValueType] -or $Value -is [System.String] +} diff --git a/PwshSpectreConsole/private/Write-AnsiConsole.ps1 b/PwshSpectreConsole/private/Write-AnsiConsole.ps1 index 67caf63d..49cc1ef0 100644 --- a/PwshSpectreConsole/private/Write-AnsiConsole.ps1 +++ b/PwshSpectreConsole/private/Write-AnsiConsole.ps1 @@ -1,12 +1,14 @@ +using namespace Spectre.Console + <# .SYNOPSIS -Writes an object to the console using [Spectre.Console.AnsiConsole]::Write() +Writes an object to the console using [AnsiConsole]::Write() .DESCRIPTION This function is required for mocking ansiconsole in unit tests that write objects to the console. .PARAMETER RenderableObject -The renderable object to write to the console e.g. [Spectre.Console.BarChart] +The renderable object to write to the console e.g. [BarChart] .EXAMPLE Write-SpectreConsoleOutput -Object "Hello, World!" -ForegroundColor Green -BackgroundColor Black @@ -16,7 +18,7 @@ This example writes the string "Hello, World!" to the console with green foregro function Write-AnsiConsole { param( [Parameter(Mandatory)] - [Spectre.Console.Rendering.Renderable] $RenderableObject + [Rendering.Renderable] $RenderableObject ) - [Spectre.Console.AnsiConsole]::Write($RenderableObject) + [AnsiConsole]::Write($RenderableObject) } \ No newline at end of file diff --git a/PwshSpectreConsole/private/Write-SpectreHostInternal.ps1 b/PwshSpectreConsole/private/Write-SpectreHostInternal.ps1 new file mode 100644 index 00000000..442aa80a --- /dev/null +++ b/PwshSpectreConsole/private/Write-SpectreHostInternal.ps1 @@ -0,0 +1,18 @@ +using namespace Spectre.Console + +# Functions required for unit testing write-spectrehost +function Write-SpectreHostInternalMarkup { + param ( + [Parameter(Mandatory)] + [string] $Message + ) + [AnsiConsoleExtensions]::Markup([AnsiConsole]::Console, $Message) +} + +function Write-SpectreHostInternalMarkupLine { + param ( + [Parameter(Mandatory)] + [string] $Message + ) + [AnsiConsoleExtensions]::MarkupLine([AnsiConsole]::Console, $Message) +} \ No newline at end of file diff --git a/PwshSpectreConsole/private/classes/PwshSpectreConsole.VTCodes.cs b/PwshSpectreConsole/private/classes/PwshSpectreConsole.VTCodes.cs new file mode 100644 index 00000000..40b30487 --- /dev/null +++ b/PwshSpectreConsole/private/classes/PwshSpectreConsole.VTCodes.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Management.Automation; + +namespace PwshSpectreConsole.VTCodes +{ + public class VT + { + // objects of this class are returned by the Parser + public class VtCode + { + public object Value { get; set; } // color, decoration, etc. + public string Type { get; set; } // 4bit, 8bit, 24bit, decoration + public string Position { get; set; } // foreground, background + public int Placement { get; set; } // placement in the string + } + public class RGB + { + public int Red { get; set; } + public int Green { get; set; } + public int Blue { get; set; } + public override string ToString() + { + return $"RGB({Red},{Green},{Blue})"; + } + + } + } + public static class DecorationDictionary + { + public static bool TryGetValue(int key, out string value) + { + if (DecorationDict.TryGetValue(key, out string str)) + { + value = str; + return true; + } + value = null; + return false; + } + internal static Dictionary DecorationDict { get; } = new Dictionary() + { + { 0, "None" }, + { 1, "Bold" }, + { 2, "Dim" }, + { 3, "Italic" }, + { 4, "Underline" }, + { 5, "SlowBlink" }, + { 6, "RapidBlink" }, + { 7, "Invert" }, + { 8, "Conceal" }, + { 9, "Strikethrough" }, + { 21, "BoldOff" }, + { 22, "NormalIntensity" }, + { 23, "ItalicOff" }, + { 24, "UnderlineOff" }, + { 25, "BlinkOff" }, + { 27, "InvertOff" }, + { 28, "ConcealOff" }, + { 29, "StrikethroughOff" } + // Add more entries as needed + }; + } + public class Parser + { + private static (string slice, int placement) GetNextSlice(ref ReadOnlySpan inputSpan) + { + var escIndex = inputSpan.IndexOf('\x1B'); + if (escIndex == -1) + { + return (null, 0); + } + // Skip the '[' character after ESC + var sliceStart = escIndex + 2; + if (sliceStart >= inputSpan.Length) + { + return (null, 0); + } + var slice = inputSpan.Slice(sliceStart); + var endIndex = slice.IndexOf('m'); + if (endIndex == -1) + { + return (null, 0); + } + var vtCode = slice.Slice(0, endIndex).ToString(); + var placement = sliceStart + endIndex - vtCode.Length; + inputSpan = inputSpan.Slice(placement); + return (vtCode, placement); + } + private static VT.VtCode New4BitVT(int firstCode, int placement) + { + string pos = (firstCode >= 30 && firstCode <= 37 || firstCode >= 90 && firstCode <= 97) ? "foreground" : "background"; + return new VT.VtCode + { + Value = firstCode, + Type = "4bit", + Position = pos, + Placement = placement + }; + } + private static VT.VtCode New8BitVT(string[] codeParts, int placement, string position) + { + return new VT.VtCode + { + Value = int.Parse(codeParts[2]), + Type = "8bit", + Position = position, + Placement = placement + }; + } + private static VT.VtCode New24BitVT(string[] codeParts, int placement, string position) + { + return new VT.VtCode + { + Value = new VT.RGB + { + Red = int.Parse(codeParts[2]), + Green = int.Parse(codeParts[3]), + Blue = int.Parse(codeParts[4]) + }, + Type = "24bit", + Position = position, + Placement = placement + }; + } + private static VT.VtCode NewDecoVT(int firstCode, int placement) + { + if (DecorationDictionary.TryGetValue(firstCode, out string strDeco)) + { + return new VT.VtCode + { + Value = strDeco, + Type = "decoration", + Position = "", + Placement = placement + }; + } + return null; + } + private static VT.VtCode NewVT(int firstCode, string[] codeParts, int placement) + { + if (firstCode >= 30 && firstCode <= 37 || firstCode >= 40 && firstCode <= 47 || firstCode >= 90 && firstCode <= 97 || firstCode >= 100 && firstCode <= 107) + { + return New4BitVT(firstCode, placement); + } + else if (firstCode == 38 || firstCode == 48) + { + string position = firstCode == 48 ? "background" : "foreground"; + if (codeParts.Length >= 3 && codeParts[1] == "5") + { + return New8BitVT(codeParts, placement, position); + } + else if (codeParts.Length >= 5 && codeParts[1] == "2") + { + return New24BitVT(codeParts, placement, position); + } + } + else + { + return NewDecoVT(firstCode, placement); + } + return null; + } + public static List Parse(string input) + { + ReadOnlySpan inputSpan = input.AsSpan(); + List results = new List(); + + while (!inputSpan.IsEmpty) + { + var (slice, placement) = GetNextSlice(inputSpan: ref inputSpan); + if (slice == null) + { + break; + } + + var codeParts = slice.Split(';'); + if (codeParts.Length > 0) + { + try + { + int firstCode = int.Parse(codeParts[0]); + VT.VtCode _vtCode = NewVT(firstCode, codeParts, placement); + if (_vtCode != null) + { + results.Add(_vtCode); + } + } + catch (FormatException) + { + // Ignore + } + } + } + return results; + } + } +} diff --git a/PwshSpectreConsole/private/completions/Completers.psm1 b/PwshSpectreConsole/private/completions/Completers.psm1 index 22e743f7..099afe75 100644 --- a/PwshSpectreConsole/private/completions/Completers.psm1 +++ b/PwshSpectreConsole/private/completions/Completers.psm1 @@ -8,6 +8,10 @@ class ValidateSpectreColor : ValidateArgumentsAttribute { if ($Color -match '^#[A-Fa-f0-9]{6}$') { return } + # Handle an explicitly defined spectre color object + if ($Color -is [Color]) { + return + } $spectreColors = [Color] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name $result = $spectreColors -contains $Color if ($result -eq $false) { @@ -58,3 +62,18 @@ class SpectreConsoleTreeGuide : IValidateSetValuesGenerator { return $lookup } } +class ColorTransformationAttribute : ArgumentTransformationAttribute { + [object] Transform([EngineIntrinsics]$engine, [object]$inputData) { + if ($InputData -is [Color]) { + return $InputData + } + if ($InputData.StartsWith('#')) { + $hexBytes = [System.Convert]::FromHexString($InputData.Substring(1)) + return [Color]::new($hexBytes[0], $hexBytes[1], $hexBytes[2]) + } + if ($InputData -is [String]) { + return [Color]::$InputData + } + throw [System.ArgumentException]::new("Cannot convert '$InputData' to [Spectre.Console.Color]") + } +} diff --git a/PwshSpectreConsole/private/images/webpreview.png b/PwshSpectreConsole/private/images/webpreview.png new file mode 100644 index 00000000..81890a9c Binary files /dev/null and b/PwshSpectreConsole/private/images/webpreview.png differ diff --git a/PwshSpectreConsole/public/config/Set-SpectreColors.ps1 b/PwshSpectreConsole/public/config/Set-SpectreColors.ps1 index 86690111..a4a1f518 100644 --- a/PwshSpectreConsole/public/config/Set-SpectreColors.ps1 +++ b/PwshSpectreConsole/public/config/Set-SpectreColors.ps1 @@ -1,4 +1,5 @@ using module "..\..\private\completions\Completers.psm1" +using namespace Spectre.Console function Set-SpectreColors { <# @@ -30,13 +31,21 @@ function Set-SpectreColors { #> [Reflection.AssemblyMetadata("title", "Set-SpectreColors")] param ( - [ValidateSpectreColor()] + [ColorTransformationAttribute()] [ArgumentCompletionsSpectreColors()] - [string] $AccentColor = "Blue", - [ValidateSpectreColor()] + [Color] $AccentColor = "Blue", + [ColorTransformationAttribute()] [ArgumentCompletionsSpectreColors()] - [string] $DefaultValueColor = "Grey" + [Color] $DefaultValueColor = "Grey", + [ColorTransformationAttribute()] + [ArgumentCompletionsSpectreColors()] + [Color] $DefaultTableHeaderColor = [Color]::Default, + [ColorTransformationAttribute()] + [ArgumentCompletionsSpectreColors()] + [Color] $DefaultTableTextColor = [Color]::Default ) - $script:AccentColor = $AccentColor | Convert-ToSpectreColor - $script:DefaultValueColor = $DefaultValueColor | Convert-ToSpectreColor + $script:AccentColor = $AccentColor + $script:DefaultValueColor = $DefaultValueColor + $script:DefaultTableHeaderColor = $DefaultTableHeaderColor + $script:DefaultTableTextColor = $DefaultTableTextColor } \ No newline at end of file diff --git a/PwshSpectreConsole/public/demo/Get-SpectreDemoColors.ps1 b/PwshSpectreConsole/public/demo/Get-SpectreDemoColors.ps1 index 7412258d..012165db 100644 --- a/PwshSpectreConsole/public/demo/Get-SpectreDemoColors.ps1 +++ b/PwshSpectreConsole/public/demo/Get-SpectreDemoColors.ps1 @@ -1,3 +1,5 @@ +using namespace Spectre.Console + <# .SYNOPSIS Retrieves a list of Spectre Console colors and displays them with their corresponding markup. @@ -22,7 +24,7 @@ function Get-SpectreDemoColors { Write-SpectreRule "Colors" Write-Host "" - $colors = [Spectre.Console.Color] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name + $colors = [Color] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name $colors = $colors | ForEach-Object { $prefix = ($_ -replace '[_0-9]+', '') $numeric = ($_ -replace '^[^0-9]+', '') @@ -47,7 +49,7 @@ function Get-SpectreDemoColors { $maxLength = $colors | Measure-Object -Maximum -Property Length | Select-Object -ExpandProperty Maximum foreach($color in $colors) { - $total = [Spectre.Console.Color]::$color | Select-Object @{ Name = "Total"; Expression = {$_.R + $_.G + $_.B} } | Select-Object -ExpandProperty Total + $total = [Color]::$color | Select-Object @{ Name = "Total"; Expression = {$_.R + $_.G + $_.B} } | Select-Object -ExpandProperty Total $textColor = "white" if($total -gt 280) { $textColor = "black" diff --git a/PwshSpectreConsole/public/demo/Get-SpectreDemoEmoji.ps1 b/PwshSpectreConsole/public/demo/Get-SpectreDemoEmoji.ps1 index 4646d7a6..539dc7a1 100644 --- a/PwshSpectreConsole/public/demo/Get-SpectreDemoEmoji.ps1 +++ b/PwshSpectreConsole/public/demo/Get-SpectreDemoEmoji.ps1 @@ -1,3 +1,5 @@ +using namespace Spectre.Console + <# .SYNOPSIS Retrieves a collection of emojis available in Spectre Console. @@ -23,7 +25,7 @@ function Get-SpectreDemoEmoji { Write-SpectreRule "`nGeneral" Write-Host "" - $emojiCollection = [Spectre.Console.Emoji+Known] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name + $emojiCollection = [Emoji+Known] | Get-Member -Static -Type Properties | Select-Object -ExpandProperty Name $faces = @() foreach($emoji in $emojiCollection) { $id = ($emoji -creplace '([A-Z])', '_$1' -replace '^_', '').ToLower() diff --git a/PwshSpectreConsole/public/demo/Start-SpectreDemo.ps1 b/PwshSpectreConsole/public/demo/Start-SpectreDemo.ps1 index 7d370884..8cfe484e 100644 --- a/PwshSpectreConsole/public/demo/Start-SpectreDemo.ps1 +++ b/PwshSpectreConsole/public/demo/Start-SpectreDemo.ps1 @@ -1,3 +1,32 @@ +using namespace Spectre.Console + +function Write-SpectreExample { + param ( + [Parameter(ValueFromPipeline)] + [string] $Codeblock, + [string] $Title, + [string] $Description, + [switch] $HideHeader, + [switch] $NoNewline + ) + if($host.UI.RawUI.WindowSize.Width -lt 120) { + Write-SpectreFigletText "Pwsh + Spectre!" + } else { + Write-SpectreFigletText "Welcome to PwshSpectreConsole!" + } + Write-Host "" + + Write-SpectreRule $Title -Color ([Color]::SteelBlue1) + Write-SpectreHost "`n$Description" + if(!$HideHeader) { + Write-CodeblockHeader + } + $Codeblock | Write-Codeblock -SyntaxHighlight -ShowLineNumbers + if(!$NoNewline) { + Write-Host "" + } +} + function Start-SpectreDemo { <# .SYNOPSIS @@ -16,33 +45,6 @@ function Start-SpectreDemo { Clear-Host - function Write-SpectreExample { - param ( - [Parameter(ValueFromPipeline)] - [string] $Codeblock, - [string] $Title, - [string] $Description, - [switch] $HideHeader, - [switch] $NoNewline - ) - if($host.UI.RawUI.WindowSize.Width -lt 120) { - Write-SpectreFigletText "Pwsh + Spectre!" - } else { - Write-SpectreFigletText "Welcome to PwshSpectreConsole!" - } - Write-Host "" - - Write-SpectreRule $Title -Color ([Spectre.Console.Color]::SteelBlue1) - Write-SpectreHost "`n$Description" - if(!$HideHeader) { - Write-CodeblockHeader - } - $Codeblock | Write-Codeblock -SyntaxHighlight -ShowLineNumbers - if(!$NoNewline) { - Write-Host "" - } - } - if($host.UI.RawUI.WindowSize.Width -lt 120) { Write-SpectreFigletText "Pwsh + Spectre!" } else { @@ -50,16 +52,17 @@ function Start-SpectreDemo { } Write-Host "" - Write-SpectreRule "PwshSpectreConsole Intro" -Color ([Spectre.Console.Color]::SteelBlue1) + Write-SpectreRule "PwshSpectreConsole Intro" -Color ([Color]::SteelBlue1) Write-SpectreHost "`nPwshSpectreConsole is an opinionated wrapper for the awesome Spectre.Console library. It's opinionated in that I have not just exposed the internals of Spectre Console to PowerShell but have wrapped them in a way that makes them work better in the PowerShell ecosystem (in my opinion 😜)." Write-SpectreHost "`nSpectre Console is mostly an async library and it leans heavily on types and extension methods in C# which are very verbose to work with in PowerShell so this module hides away some of the complexity." Write-SpectreHost "`nThe module doesn't expose the full feature set of Spectre.Console because the scope of the library is huge and I've focused on the features that I use to enhance my scripts." Write-Host "" - if(![Spectre.Console.AnsiConsole]::Console.Profile.Capabilities.Unicode) { + if(![AnsiConsole]::Console.Profile.Capabilities.Unicode) { Write-Warning "To enable all features of Spectre.Console you need to enable Unicode support in your PowerShell profile by adding the following to your profile at $PROFILE. See https://spectreconsole.net/best-practices for more info.`n`n`$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding`n" } Read-SpectrePause -NoNewline + Clear-Host $example = @' @@ -160,22 +163,22 @@ $( @{ Label = "Apple" Value = 12 - Color = [Spectre.Console.Color]::Green + Color = [Color]::Green }, @{ Label = "Orange" Value = 54 - Color = [Spectre.Console.Color]::Orange1 + Color = [Color]::Orange1 }, @{ Label = "Strawberry" Value = 51 - Color = [Spectre.Console.Color]::Red + Color = [Color]::Red }, @{ Label = "Banana" Value = 33 - Color = [Spectre.Console.Color]::Yellow + Color = [Color]::Yellow } ) | Format-SpectreBarChart '@ @@ -190,22 +193,22 @@ $( @{ Label = "Apple" Value = 12 - Color = [Spectre.Console.Color]::Green + Color = [Color]::Green }, @{ Label = "Strawberry" Value = 15 - Color = [Spectre.Console.Color]::Red + Color = [Color]::Red }, @{ Label = "Orange" Value = 54 - Color = [Spectre.Console.Color]::Orange1 + Color = [Color]::Orange1 }, @{ Label = "Plum" Value = 75 - Color = [Spectre.Console.Color]::Fuchsia + Color = [Color]::Fuchsia } ) | Format-SpectreBreakdownChart '@ @@ -214,22 +217,30 @@ $( Read-SpectrePause Clear-Host + $example = @' +Get-Module PwshSpectreConsole | Select-Object PrivateData | Format-SpectreJson -Expand +'@ + $example | Write-SpectreExample -Title "Json Data" -Description "Spectre Console can format JSON with syntax highlighting thanks to https://github.com/trackd" + Invoke-Expression $example + Read-SpectrePause + Clear-Host + $example = @' Invoke-SpectreCommandWithStatus -Spinner "Dots2" -Title "Showing a spinner..." -ScriptBlock { # Write updates tot the host using Write-SpectreHost Start-Sleep -Seconds 1 - Write-SpectreHost "`n[grey]LOG:[/] Doing some work" + Write-SpectreHost "`n[grey]LOG:[/] Doing some work " Start-Sleep -Seconds 1 - Write-SpectreHost "`n[grey]LOG:[/] Doing some more work" + Write-SpectreHost "`n[grey]LOG:[/] Doing some more work " Start-Sleep -Seconds 1 - Write-SpectreHost "`n[grey]LOG:[/] Done" + Write-SpectreHost "`n[grey]LOG:[/] Done " Start-Sleep -Seconds 1 } '@ $example | Write-SpectreExample -Title "Progress Spinners" -Description "Progress spinners provide visual feedback to users when an operation or task is in progress. They help indicate that the application is working on a request, preventing users from becoming frustrated or assuming the application has stalled." Invoke-Expression $example - Read-SpectrePause -NoNewline + Read-SpectrePause Clear-Host $example = @' @@ -311,7 +322,7 @@ Invoke-SpectreCommandWithProgress -ScriptBlock { $example = @" Get-SpectreImageExperimental "$PSScriptRoot\..\..\private\images\harveyspecter.gif" -LoopCount 2 -Write-SpectreHost "I'm Harvey Specter. Are you after a Specter consult or a Spectre.Console..?" +Write-SpectreHost "I'm Harvey Specter. Are you after a Specter consult or a Spectre.Console?" "@ $example | Write-SpectreExample -Title "View Images" -Description "Images can be rendered in the terminal, given a path to an image Spectre Console will downsample the image to a resolution that will fit within the terminal width or you can choose your own width setting." Invoke-Expression $example diff --git a/PwshSpectreConsole/public/formatting/Format-SpectreBarChart.ps1 b/PwshSpectreConsole/public/formatting/Format-SpectreBarChart.ps1 index 79b0eb5e..d1dca16e 100644 --- a/PwshSpectreConsole/public/formatting/Format-SpectreBarChart.ps1 +++ b/PwshSpectreConsole/public/formatting/Format-SpectreBarChart.ps1 @@ -1,4 +1,5 @@ using module "..\..\private\completions\Completers.psm1" +using namespace Spectre.Console function Format-SpectreBarChart { <# @@ -26,7 +27,7 @@ function Format-SpectreBarChart { $data = @() $data += New-SpectreChartItem -Label "Apples" -Value 10 -Color "Green" - $data += New-SpectreChartItem -Label "Oranges" -Value 5 -Color "Orange" + $data += New-SpectreChartItem -Label "Oranges" -Value 5 -Color "DarkOrange" $data += New-SpectreChartItem -Label "Bananas" -Value 2.2 -Color "#FFFF00" Format-SpectreBarChart -Data $data -Title "Fruit Sales" -Width 50 @@ -36,12 +37,12 @@ function Format-SpectreBarChart { [Parameter(ValueFromPipeline, Mandatory)] [array] $Data, [String] $Title, - [ValidateScript({ $_ -gt 0 -and $_ -le [console]::BufferWidth }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] - [int] $Width = [console]::BufferWidth, + [ValidateScript({ $_ -gt 0 -and $_ -le (Get-HostWidth) }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] + [int] $Width = (Get-HostWidth), [switch] $HideValues ) begin { - $barChart = [Spectre.Console.BarChart]::new() + $barChart = [BarChart]::new() if ($Title) { $barChart.Label = $Title } @@ -53,10 +54,10 @@ function Format-SpectreBarChart { process { if ($Data -is [array]) { foreach ($dataItem in $Data) { - $barChart = [Spectre.Console.BarChartExtensions]::AddItem($barChart, $dataItem.Label, $dataItem.Value, ($dataItem.Color | Convert-ToSpectreColor)) + $barChart = [BarChartExtensions]::AddItem($barChart, $dataItem.Label, $dataItem.Value, ($dataItem.Color | Convert-ToSpectreColor)) } } else { - $barChart = [Spectre.Console.BarChartExtensions]::AddItem($barChart, $Data.Label, $Data.Value, ($Data.Color | Convert-ToSpectreColor)) + $barChart = [BarChartExtensions]::AddItem($barChart, $Data.Label, $Data.Value, ($Data.Color | Convert-ToSpectreColor)) } } end { diff --git a/PwshSpectreConsole/public/formatting/Format-SpectreBreakdownChart.ps1 b/PwshSpectreConsole/public/formatting/Format-SpectreBreakdownChart.ps1 index 374628f0..97c63a26 100644 --- a/PwshSpectreConsole/public/formatting/Format-SpectreBreakdownChart.ps1 +++ b/PwshSpectreConsole/public/formatting/Format-SpectreBreakdownChart.ps1 @@ -1,4 +1,5 @@ using module "..\..\private\completions\Completers.psm1" +using namespace Spectre.Console function Format-SpectreBreakdownChart { <# @@ -7,7 +8,7 @@ function Format-SpectreBreakdownChart { ![Example breakdown chart](/breakdownchart.png) .DESCRIPTION - This function takes an array of data and formats it into a breakdown chart using Spectre.Console.BreakdownChart. The chart can be customized with a specified width and color. + This function takes an array of data and formats it into a breakdown chart using BreakdownChart. The chart can be customized with a specified width and color. .PARAMETER Data An array of data to be formatted into a breakdown chart. @@ -35,13 +36,13 @@ function Format-SpectreBreakdownChart { param ( [Parameter(ValueFromPipeline, Mandatory)] [array] $Data, - [ValidateScript({ $_ -gt 0 -and $_ -le [console]::BufferWidth }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] - [int]$Width = [console]::BufferWidth, + [ValidateScript({ $_ -gt 0 -and $_ -le (Get-HostWidth) }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] + [int]$Width = (Get-HostWidth), [switch]$HideTags, [Switch]$HideTagValues ) begin { - $chart = [Spectre.Console.BreakdownChart]::new() + $chart = [BreakdownChart]::new() $chart.Width = $Width if ($HideTags) { $chart.ShowTags = $false @@ -53,10 +54,10 @@ function Format-SpectreBreakdownChart { process { if($Data -is [array]) { foreach($dataItem in $Data) { - [Spectre.Console.BreakdownChartExtensions]::AddItem($chart, $dataItem.Label, $dataItem.Value, ($dataItem.Color | Convert-ToSpectreColor)) | Out-Null + [BreakdownChartExtensions]::AddItem($chart, $dataItem.Label, $dataItem.Value, ($dataItem.Color | Convert-ToSpectreColor)) | Out-Null } } else { - [Spectre.Console.BreakdownChartExtensions]::AddItem($chart, $Data.Label, $Data.Value, ($Data.Color | Convert-ToSpectreColor)) | Out-Null + [BreakdownChartExtensions]::AddItem($chart, $Data.Label, $Data.Value, ($Data.Color | Convert-ToSpectreColor)) | Out-Null } } end { diff --git a/PwshSpectreConsole/public/formatting/Format-SpectreJson.ps1 b/PwshSpectreConsole/public/formatting/Format-SpectreJson.ps1 new file mode 100644 index 00000000..5e798d43 --- /dev/null +++ b/PwshSpectreConsole/public/formatting/Format-SpectreJson.ps1 @@ -0,0 +1,197 @@ +using module "..\..\private\completions\Completers.psm1" +using namespace Spectre.Console + +function Format-SpectreJson { + <# + .SYNOPSIS + Formats an array of objects into a Spectre Console Json. + Thanks to [trackd](https://github.com/trackd) for adding this. + ![Spectre json example](/json.png) + + .DESCRIPTION + This function takes an array of objects and converts them into Json using the Spectre Console Json Library. + + .PARAMETER Data + The array of objects to be formatted into Json. + + .PARAMETER Depth + The maximum depth of the Json. Default is defined by the version of powershell. + + .PARAMETER NoBorder + If specified, the Json will not be surrounded by a border. + + .PARAMETER Border + The border style of the Json. Default is "Rounded". + + .PARAMETER Color + The color of the Json border. Default is the accent color of the script. + + .PARAMETER Title + The title of the Json. + + .PARAMETER Width + The width of the Json panel. + + .PARAMETER Height + The height of the Json panel. + + .EXAMPLE + # This example formats an array of objects into a table with a double border and the accent color of the script. + $data = @( + [pscustomobject]@{ + Name = "John" + Age = 25 + City = "New York" + IsEmployed = $true + Salary = 10 + Hobbies = @("Reading", "Swimming") + Address = @{ + Street = "123 Main St" + ZipCode = $null + } + }, + [pscustomobject]@{ + Name = "Jane" + Age = 30 + City = "Los Angeles" + IsEmployed = $false + Salary = $null + Hobbies = @("Painting", "Hiking") + Address = @{ + Street = "456 Elm St" + ZipCode = "90001" + } + } + ) + Format-SpectreJson -Data $data -Title "Employee Data" -Border "Rounded" -Color "Green" + #> + [Reflection.AssemblyMetadata("title", "Format-SpectreJson")] + [Alias('fsj')] + param( + [Parameter(ValueFromPipeline, Mandatory)] + [object] $Data, + [int] $Depth, + [string] $Title, + [switch] $NoBorder, + [ValidateSet([SpectreConsoleBoxBorder], ErrorMessage = "Value '{0}' is invalid. Try one of: {1}")] + [string] $Border = "Rounded", + [ColorTransformationAttribute()] + [ArgumentCompletionsSpectreColors()] + [Color] $Color = $script:AccentColor, + [ValidateScript({ $_ -gt 0 -and $_ -le (Get-HostWidth) }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] + [int] $Width, + [ValidateScript({ $_ -gt 0 -and $_ -le (Get-HostHeight) }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console height.")] + [int] $Height, + [switch] $Expand + ) + begin { + $collector = [System.Collections.Generic.List[psobject]]::new() + $splat = @{ + WarningAction = 'Ignore' + ErrorAction = 'Stop' + } + if ($Depth) { + $splat.Depth = $Depth + } + $ht = [ordered]@{} + } + process { + if ($MyInvocation.ExpectingInput) { + if ($data -is [string]) { + if ($data.pschildname) { + if (-Not $ht.contains($data.pschildname)) { + $ht[$data.pschildname] = [System.Text.StringBuilder]::new() + } + return [void]$ht[$data.pschildname].AppendLine($data) + } + # assume we get the entire json in one go a string (e.g -Raw or invoke-webrequest) + try { + $jsonObjects = $data | Out-String | ConvertFrom-Json -AsHashtable @splat + return $collector.add($jsonObjects) + } + catch { + Write-Debug "Failed to convert string to object, $_" + } + } + if ($data -is [System.IO.FileSystemInfo]) { + if ($data.Extension -eq '.json') { + Write-Debug "json file found, reading $($data.FullName)" + try { + $jsonObjects = Get-Content -Raw $data.FullName | ConvertFrom-Json -AsHashtable @splat + return $collector.add($jsonObjects) + } + catch { + Write-Debug "Failed to convert json to object, $_" + } + } + return $collector.add( + [pscustomobject]@{ + Name = $data.Name + FullName = $data.FullName + Type = $data.GetType().Name.TrimEnd('Info') + }) + } + Write-Debug "adding item from pipeline" + return $collector.add($data) + } + foreach ($item in $data) { + Write-Debug "adding item from input" + $collector.add($item) + } + } + end { + if ($ht.keys.count -gt 0) { + foreach ($key in $ht.Keys) { + Write-Debug "converting json stream to object, $key" + try { + $jsonObject = $ht[$key].ToString() | Out-String | ConvertFrom-Json -AsHashtable @splat + $collector.add($jsonObject) + continue + } + catch { + Write-Debug "Failed to convert json to object: $key, $_" + } + } + } + if ($collector.Count -eq 0) { + return + } + try { + $json = [Json.JsonText]::new(($collector | ConvertTo-Json @splat)) + } + catch { + Write-Error "Failed to convert to json, $_" + return + } + $json.BracesStyle = [Style]::new([Color]::Red) + $json.BracketsStyle = [Style]::new([Color]::Green) + $json.ColonStyle = [Style]::new([Color]::Blue) + $json.CommaStyle = [Style]::new([Color]::CadetBlue) + $json.StringStyle = [Style]::new([Color]::Yellow) + $json.NumberStyle = [Style]::new([Color]::Cyan2) + $json.BooleanStyle = [Style]::new([Color]::Teal) + $json.NullStyle = [Style]::new([Color]::Plum1) + + if ($NoBorder) { + Write-AnsiConsole $json + return + } + + $panel = [Panel]::new($json) + $panel.Border = [BoxBorder]::$Border + $panel.BorderStyle = [Style]::new($Color) + if ($Title) { + $panel.Header = [PanelHeader]::new($Title) + } + if ($width) { + $panel.Width = $Width + } + if ($height) { + $panel.Height = $Height + } + if ($Expand) { + $panel.Expand = $Expand + } + Write-AnsiConsole $panel + } +} diff --git a/PwshSpectreConsole/public/formatting/Format-SpectrePanel.ps1 b/PwshSpectreConsole/public/formatting/Format-SpectrePanel.ps1 index 3760369e..93c87166 100644 --- a/PwshSpectreConsole/public/formatting/Format-SpectrePanel.ps1 +++ b/PwshSpectreConsole/public/formatting/Format-SpectrePanel.ps1 @@ -1,4 +1,5 @@ using module "..\..\private\completions\Completers.psm1" +using namespace Spectre.Console function Format-SpectrePanel { <# @@ -37,22 +38,22 @@ function Format-SpectrePanel { [Reflection.AssemblyMetadata("title", "Format-SpectrePanel")] param ( [Parameter(ValueFromPipeline, Mandatory)] - [string] $Data, + [object] $Data, [string] $Title, [ValidateSet([SpectreConsoleBoxBorder], ErrorMessage = "Value '{0}' is invalid. Try one of: {1}")] [string] $Border = "Rounded", [switch] $Expand, - [ValidateSpectreColor()] + [ColorTransformationAttribute()] [ArgumentCompletionsSpectreColors()] - [string] $Color = $script:AccentColor.ToMarkup(), - [ValidateScript({ $_ -gt 0 -and $_ -le [console]::BufferWidth }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] + [Color] $Color = $script:AccentColor, + [ValidateScript({ $_ -gt 0 -and $_ -le (Get-HostWidth) }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console width.")] [int]$Width, - [ValidateScript({ $_ -gt 0 -and $_ -le [console]::WindowHeight }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console height.")] + [ValidateScript({ $_ -gt 0 -and $_ -le (Get-HostHeight) }, ErrorMessage = "Value '{0}' is invalid. Cannot be negative or exceed console height.")] [int]$Height ) - $panel = [Spectre.Console.Panel]::new($Data) + $panel = [Panel]::new($Data) if ($Title) { - $panel.Header = [Spectre.Console.PanelHeader]::new($Title) + $panel.Header = [PanelHeader]::new($Title) } if ($width) { $panel.Width = $Width @@ -61,7 +62,7 @@ function Format-SpectrePanel { $panel.Height = $Height } $panel.Expand = $Expand - $panel.Border = [Spectre.Console.BoxBorder]::$Border - $panel.BorderStyle = [Spectre.Console.Style]::new(($Color | Convert-ToSpectreColor)) + $panel.Border = [BoxBorder]::$Border + $panel.BorderStyle = [Style]::new($Color) Write-AnsiConsole $panel } diff --git a/PwshSpectreConsole/public/formatting/Format-SpectreTable.ps1 b/PwshSpectreConsole/public/formatting/Format-SpectreTable.ps1 index be780406..0c78d93b 100644 --- a/PwshSpectreConsole/public/formatting/Format-SpectreTable.ps1 +++ b/PwshSpectreConsole/public/formatting/Format-SpectreTable.ps1 @@ -1,16 +1,31 @@ using module "..\..\private\completions\Completers.psm1" +using namespace Spectre.Console function Format-SpectreTable { <# .SYNOPSIS - Formats an array of objects into a Spectre Console table. + Formats an array of objects into a Spectre Console table. Thanks to [trackd](https://github.com/trackd) and [fmotion1](https://github.com/fmotion1) for the updates to support markdown and color in the table contents. ![Example table](/table.png) .DESCRIPTION This function takes an array of objects and formats them into a table using the Spectre Console library. The table can be customized with a border style and color. + .PARAMETER Property + Specifies the object properties that appear in the display and the order in which they appear. + Type one or more property names, separated by commas, or use a hash table to display a calculated property. + Wildcards are permitted. + The Property parameter is optional. You can't use the Property and View parameters in the same command. + The value of the Property parameter can be a new calculated property. + The calculated property can be a script block or a hash table. Valid key-value pairs are: + - Name (or Label) `` + - Expression - `` or `