From 58b3e69ac98f897472e0ef620b6d2e824396dbe5 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Wed, 25 Sep 2024 23:08:43 -0400
Subject: [PATCH 01/15] Add PSStyle support

Closes #1
---
 source/ErrorView.format.ps1xml                | 38 +++++++++++++++++++
 source/public/ConvertTo-CategoryErrorView.ps1 |  9 ++++-
 source/public/ConvertTo-DetailedErrorView.ps1 |  6 +--
 source/public/ConvertTo-FullErrorView.ps1     | 28 +++++++++++---
 source/public/ConvertTo-NormalErrorView.ps1   | 15 ++++++--
 .../public/ConvertTo-NormalExceptionView.ps1  | 26 +++++++++++++
 source/public/ConvertTo-SimpleErrorView.ps1   | 13 ++++++-
 source/public/Format-Error.ps1                | 15 ++++----
 source/public/Set-ErrorView.ps1               |  3 +-
 source/public/Write-NativeCommandError.ps1    | 36 +++++++++++-------
 10 files changed, 151 insertions(+), 38 deletions(-)
 create mode 100644 source/public/ConvertTo-NormalExceptionView.ps1

diff --git a/source/ErrorView.format.ps1xml b/source/ErrorView.format.ps1xml
index 4b3a2c7..443e23a 100644
--- a/source/ErrorView.format.ps1xml
+++ b/source/ErrorView.format.ps1xml
@@ -30,5 +30,43 @@
         </CustomEntries>
       </CustomControl>
     </View>
+    <View>
+      <Name>ExceptionInstance</Name>
+      <OutOfBand />
+      <ViewSelectedBy>
+        <TypeName>System.Exception</TypeName>
+      </ViewSelectedBy>
+      <CustomControl>
+        <CustomEntries>
+          <CustomEntry>
+            <CustomItem>
+              <ExpressionBinding>
+                <ScriptBlock>Write-NativeCommandError $_</ScriptBlock>
+              </ExpressionBinding>
+              <ExpressionBinding>
+                <ScriptBlock>
+                  <![CDATA[
+                    if ($_.ErrorRecord) {
+                      $Record = $_.ErrorRecord
+                      if ($formatter = @(Get-Command "ConvertTo-$($ErrorView -replace "View$")ErrorView" -ListImported -ErrorAction Ignore -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord])) {
+                        & ($formatter[0]) -InputObject $Record
+                      } else {
+                        ConvertTo-NormalErrorView $Record
+                      }
+                    } else {
+                      if ($formatter = @(Get-Command "ConvertTo-$($ErrorView -replace "View$")ExceptionView" -ListImported -ErrorAction Ignore -ParameterName InputObject -ParameterType [System.Exception])) {
+                        & ($formatter[0]) -InputObject $_
+                      } else {
+                        ConvertTo-NormalExceptionView $_
+                      }
+                    }
+                  ]]>
+                </ScriptBlock>
+              </ExpressionBinding>
+            </CustomItem>
+          </CustomEntry>
+        </CustomEntries>
+      </CustomControl>
+    </View>
   </ViewDefinitions>
 </Configuration>
diff --git a/source/public/ConvertTo-CategoryErrorView.ps1 b/source/public/ConvertTo-CategoryErrorView.ps1
index ccb619a..c5fb25f 100644
--- a/source/public/ConvertTo-CategoryErrorView.ps1
+++ b/source/public/ConvertTo-CategoryErrorView.ps1
@@ -15,5 +15,12 @@ filter ConvertTo-CategoryErrorView {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
-    $InputObject.CategoryInfo.GetMessage()
+    $resetColor = ''
+    $errorColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $resetColor = "$([char]0x1b)[0m"
+        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+    }
+    $errorColor + $InputObject.CategoryInfo.GetMessage() + $resetColor
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-DetailedErrorView.ps1 b/source/public/ConvertTo-DetailedErrorView.ps1
index ed52bcb..fbc1c58 100644
--- a/source/public/ConvertTo-DetailedErrorView.ps1
+++ b/source/public/ConvertTo-DetailedErrorView.ps1
@@ -32,9 +32,9 @@ function ConvertTo-DetailedErrorView {
         $OutputRoot = [System.Text.StringBuilder]::new()
 
         if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-            $resetColor = $PSStyle.Reset
-            $errorColor = $PSStyle.Formatting.Error
-            $accentColor = $PSStyle.Formatting.FormatAccent
+            $resetColor = "$([char]0x1b)[0m"
+            $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+            $accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
         }
 
         function DetailedErrorView {
diff --git a/source/public/ConvertTo-FullErrorView.ps1 b/source/public/ConvertTo-FullErrorView.ps1
index dbe4ce5..5377ef8 100644
--- a/source/public/ConvertTo-FullErrorView.ps1
+++ b/source/public/ConvertTo-FullErrorView.ps1
@@ -3,7 +3,7 @@ filter ConvertTo-FullErrorView {
         .SYNOPSIS
             Converts an ErrorRecord to a full error view
         .DESCRIPTION
-            The most verbose error view I've got, it shows everything, recursing forever.
+            A simple, verbose error view that just shows everything, recursing forever.
     #>
     [CmdletBinding()]
     param(
@@ -12,19 +12,35 @@ filter ConvertTo-FullErrorView {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
-    $PSStyle.OutputRendering, $Rendering = "Ansi", $PSStyle.OutputRendering
-    $Detail = $InputObject | Format-List * -Force | Out-String
-    $PSStyle.OutputRendering = $Rendering
+    $resetColor = ''
+    $errorColor = ''
+    #$accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        # For Format-List to use color when piped to Out-String, OutputRendering needs to be Ansi
+        $PSStyle.OutputRendering, $Rendering = "Ansi", $PSStyle.OutputRendering
+
+        $resetColor = "$([char]0x1b)[0m"
+        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+        $Detail = $InputObject | Format-List * -Force | Out-String -Width 120
+        $Detail = $Detail -replace "((?:Exception|FullyQualifiedErrorId).*`e\[0m)(.*)", "$($PSStyle.Formatting.ErrorAccent)`$1$($PSStyle.Formatting.Error)`$2$($PSStyle.Reset)"
+    } else {
+        $Detail = $InputObject | Format-List * -Force | Out-String -Width 120
+    }
 
     # NOTE: ErrorViewRecurse is normally false, and only set temporarily by Format-Error -Recurse
     if ($ErrorViewRecurse) {
         $Count = 1
         $Exception = $InputObject.Exception
         while ($Exception = $Exception.InnerException) {
-            $Detail += "`nINNER EXCEPTION $($Count): $($Exception.GetType().FullName)`n`n"
-            $Detail += $Exception | Format-List * -Force | Out-String
+            $Detail += $errorColor + "`nINNER EXCEPTION $($Count): $resetColor$($Exception.GetType().FullName)`n`n"
+            $Detail += $Exception | Format-List * -Force | Out-String -Width 120
             $Count++
         }
     }
+    if ($resetColor) {
+        $PSStyle.OutputRendering = $Rendering
+    }
     $Detail
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalErrorView.ps1 b/source/public/ConvertTo-NormalErrorView.ps1
index 182d84e..cdcd820 100644
--- a/source/public/ConvertTo-NormalErrorView.ps1
+++ b/source/public/ConvertTo-NormalErrorView.ps1
@@ -11,9 +11,18 @@ filter ConvertTo-NormalErrorView {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
+    $resetColor = ''
+    $errorColor = ''
+    #$accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $resetColor = "$([char]0x1b)[0m"
+        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+    }
 
     if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
-        $InputObject.Exception.Message
+        $errorColor + $InputObject.Exception.Message + $resetColor
     } else {
         $myinv = $InputObject.InvocationInfo
         if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
@@ -66,9 +75,9 @@ filter ConvertTo-NormalErrorView {
         }
 
         if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
-            $InputObject.Exception.Message + $posmsg + "`n "
+            $errorColor + $InputObject.Exception.Message + $posmsg + $resetColor + "`n "
         } else {
-            $InputObject.ErrorDetails.Message + $posmsg
+            $errorColor + $InputObject.ErrorDetails.Message + $posmsg + $resetColor
         }
     }
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalExceptionView.ps1 b/source/public/ConvertTo-NormalExceptionView.ps1
new file mode 100644
index 0000000..fea1db0
--- /dev/null
+++ b/source/public/ConvertTo-NormalExceptionView.ps1
@@ -0,0 +1,26 @@
+filter ConvertTo-NormalExceptionView {
+    <#
+        .SYNOPSIS
+            Converts an Exception to a NormalView message string
+        .DESCRIPTION
+            The original default PowerShell ErrorView, updated for VT100
+    #>
+    [CmdletBinding()]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [System.Exception]
+        $InputObject
+    )
+    $resetColor = ''
+    $errorColor = ''
+    #$accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $resetColor = "$([char]0x1b)[0m"
+        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+    }
+
+    $errorColor + $InputObject.Exception.Message + $resetColor
+
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-SimpleErrorView.ps1 b/source/public/ConvertTo-SimpleErrorView.ps1
index 20e4849..97197ff 100644
--- a/source/public/ConvertTo-SimpleErrorView.ps1
+++ b/source/public/ConvertTo-SimpleErrorView.ps1
@@ -4,6 +4,15 @@ function ConvertTo-SimpleErrorView {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
+    $resetColor = ''
+    $errorColor = ''
+    #$accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $resetColor = "$([char]0x1b)[0m"
+        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+    }
 
     if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
         $InputObject.Exception.Message
@@ -46,9 +55,9 @@ function ConvertTo-SimpleErrorView {
         }
 
         if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
-            $InputObject.Exception.Message + $posmsg + "`n "
+            $errorColor + $InputObject.Exception.Message + $posmsg + $resetColor + "`n "
         } else {
-            $InputObject.ErrorDetails.Message + $posmsg
+            $errorColor + $InputObject.ErrorDetails.Message + $posmsg + $resetColor
         }
     }
 }
\ No newline at end of file
diff --git a/source/public/Format-Error.ps1 b/source/public/Format-Error.ps1
index 59a80c8..2e7edf2 100644
--- a/source/public/Format-Error.ps1
+++ b/source/public/Format-Error.ps1
@@ -33,17 +33,18 @@ function Format-Error {
         })]
         $View = "Detailed",
 
-        [Parameter(ParameterSetName="Count", Mandatory)]
+        [Parameter(ParameterSetName="Count")]
         [int]$Newest = 1,
 
         # Error records (e.g. from $Error). Defaults to the most recent error: $Error[0]
         [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName="InputObject", Mandatory)]
         [Alias("ErrorRecord")]
-        [System.Management.Automation.ErrorRecord]$InputObject = $(
-            $e = $Error[0..($Newest-1)]
-            if ($e -is ([System.Management.Automation.ErrorRecord])) { $e }
-            elseif ($e.ErrorRecord -is ([System.Management.Automation.ErrorRecord])) { $e.ErrorRecord }
-            elseif ($Error.Count -eq 0) { Write-Warning "The global `$Error collection is empty" }
+        [PSObject]$InputObject = $(
+            if ($global:Error.Count -eq 0) {
+                Write-Warning "The global `$Error collection is empty"
+            } else {
+                $global:Error[0..($Newest-1)]
+            }
         ),
 
         # Allows ErrorView functions to recurse to InnerException
@@ -51,7 +52,7 @@ function Format-Error {
     )
     begin {
         $ErrorActionPreference = "Continue"
-        $View, $ErrorView = $ErrorView, $View
+        $ErrorView, $View = $View, $ErrorView
         [bool]$Recurse, [bool]$ErrorViewRecurse = [bool]$ErrorViewRecurse, $Recurse
     }
     process {
diff --git a/source/public/Set-ErrorView.ps1 b/source/public/Set-ErrorView.ps1
index cd52ea6..8041415 100644
--- a/source/public/Set-ErrorView.ps1
+++ b/source/public/Set-ErrorView.ps1
@@ -27,7 +27,6 @@ filter Set-ErrorView {
     ).Name -replace "ConvertTo-(\w+)ErrorView", '$1View' | Select-Object -Unique
 
     $ofs = ';'
-    [ScriptBlock]::Create("enum ErrorView { $Names }").Invoke()
-
+    .([ScriptBlock]::Create("enum ErrorView { $Names }"))
     [ErrorView]$global:ErrorView = $View
 }
diff --git a/source/public/Write-NativeCommandError.ps1 b/source/public/Write-NativeCommandError.ps1
index 08b699f..85d98d1 100644
--- a/source/public/Write-NativeCommandError.ps1
+++ b/source/public/Write-NativeCommandError.ps1
@@ -1,39 +1,47 @@
 function Write-NativeCommandError {
     [CmdletBinding()]
     param(
-        [System.Management.Automation.ErrorRecord]
         $InputObject
     )
+    $resetColor = ''
+    $errorColor = ''
+    $accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $resetColor = "$([char]0x1b)[0m"
+        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+        $accentColor = $PSStyle.Formatting.ErrorAccent
+    }
 
     if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") { return }
 
-    $myinv = $InputObject.InvocationInfo
-    if ($myinv -and $myinv.MyCommand) {
-        switch -regex ( $myinv.MyCommand.CommandType ) {
+    $invoc = $InputObject.InvocationInfo
+    if ($invoc -and $invoc.MyCommand) {
+        switch -regex ( $invoc.MyCommand.CommandType ) {
             ([System.Management.Automation.CommandTypes]::ExternalScript) {
-                if ($myinv.MyCommand.Path) {
-                    $myinv.MyCommand.Path + " : "
+                if ($invoc.MyCommand.Path) {
+                    $accentColor + $invoc.MyCommand.Path + " : " + $resetColor
                 }
                 break
             }
             ([System.Management.Automation.CommandTypes]::Script) {
-                if ($myinv.MyCommand.ScriptBlock) {
-                    $myinv.MyCommand.ScriptBlock.ToString() + " : "
+                if ($invoc.MyCommand.ScriptBlock) {
+                    $accentColor + $invoc.MyCommand.ScriptBlock.ToString() + " : " + $resetColor
                 }
                 break
             }
             default {
-                if ($myinv.InvocationName -match '^[&amp;\.]?$') {
-                    if ($myinv.MyCommand.Name) {
-                        $myinv.MyCommand.Name + " : "
+                if ($invoc.InvocationName -match '^[&amp;\.]?$') {
+                    if ($invoc.MyCommand.Name) {
+                        $accentColor + $invoc.MyCommand.Name + " : " + $resetColor
                     }
                 } else {
-                    $myinv.InvocationName + " : "
+                    $accentColor + $invoc.InvocationName + " : " + $resetColor
                 }
                 break
             }
         }
-    } elseif ($myinv -and $myinv.InvocationName) {
-        $myinv.InvocationName + " : "
+    } elseif ($invoc -and $invoc.InvocationName) {
+        $accentColor + $invoc.InvocationName + " : " + $resetColor
     }
 }
\ No newline at end of file

From d08e6e8c9a495188ceab43e2e92e61258e450999 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Wed, 25 Sep 2024 23:35:45 -0400
Subject: [PATCH 02/15] Update with views from 7

---
 ErrorView.code-workspace                      |  14 +
 Reference/default.ps1                         | 335 ++++++++++++++++++
 Reference/detail copy.ps1                     | 183 ++++++++++
 Reference/detail.ps1                          | 183 ++++++++++
 source/ErrorView.format.ps1xml                |  30 ++
 source/prefix.ps1                             |  47 ++-
 source/private/GetListRecursive.ps1           | 160 +++++++++
 source/private/GetYamlRecursive.ps1           | 169 +++++++++
 source/private/WrapString.ps1                 | 119 +++++++
 source/public/ConvertTo-ConciseErrorView.ps1  |  62 ++++
 source/public/ConvertTo-DetailedErrorView.ps1 | 183 +---------
 source/public/ConvertTo-FullErrorView.ps1     |   7 +-
 source/public/ConvertTo-NormalErrorView.ps1   |  23 +-
 .../public/ConvertTo-NormalExceptionView.ps1  |   2 +-
 source/public/ConvertTo-SimpleErrorView.ps1   |  15 +-
 source/public/ConvertTo-YamlErrorView.ps1     |  33 ++
 source/public/Format-Error.ps1                |  23 +-
 source/public/Set-ErrorView.ps1               |   3 +-
 source/public/Write-NativeCommandError.ps1    |  52 +--
 19 files changed, 1394 insertions(+), 249 deletions(-)
 create mode 100644 ErrorView.code-workspace
 create mode 100644 Reference/default.ps1
 create mode 100644 Reference/detail copy.ps1
 create mode 100644 Reference/detail.ps1
 create mode 100644 source/private/GetListRecursive.ps1
 create mode 100644 source/private/GetYamlRecursive.ps1
 create mode 100644 source/private/WrapString.ps1
 create mode 100644 source/public/ConvertTo-ConciseErrorView.ps1
 create mode 100644 source/public/ConvertTo-YamlErrorView.ps1

diff --git a/ErrorView.code-workspace b/ErrorView.code-workspace
new file mode 100644
index 0000000..0f707bf
--- /dev/null
+++ b/ErrorView.code-workspace
@@ -0,0 +1,14 @@
+{
+	"folders": [
+        {
+            "name": "ErrorView",
+            "path": "."
+        },
+        {
+            "path": "../../Tasks"
+        }
+    ],
+  "settings": {
+    "powershell.cwd": "."
+  }
+}
\ No newline at end of file
diff --git a/Reference/default.ps1 b/Reference/default.ps1
new file mode 100644
index 0000000..d4f6ac8
--- /dev/null
+++ b/Reference/default.ps1
@@ -0,0 +1,335 @@
+<#
+if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
+{
+    $myinv = $_.InvocationInfo
+    if ($myinv -and $myinv.MyCommand)
+    {
+        switch -regex ( $myinv.MyCommand.CommandType )
+        {
+            ([System.Management.Automation.CommandTypes]::ExternalScript)
+            {
+                if ($myinv.MyCommand.Path)
+                {
+                    $myinv.MyCommand.Path + ' : '
+                }
+
+                break
+            }
+
+            ([System.Management.Automation.CommandTypes]::Script)
+            {
+                if ($myinv.MyCommand.ScriptBlock)
+                {
+                    $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+                }
+
+                break
+            }
+            default
+            {
+                if ($myinv.InvocationName -match '^[&\.]?$')
+                {
+                    if ($myinv.MyCommand.Name)
+                    {
+                        $myinv.MyCommand.Name + ' : '
+                    }
+                }
+                else
+                {
+                    $myinv.InvocationName + ' : '
+                }
+
+                break
+            }
+        }
+    }
+    elseif ($myinv -and $myinv.InvocationName)
+    {
+        $myinv.InvocationName + ' : '
+    }
+}
+
+
+Set-StrictMode -Off
+$ErrorActionPreference = 'Stop'
+trap { 'Error found in error view definition: ' + $_.Exception.Message }
+$newline = [Environment]::Newline
+
+$resetColor = ''
+$errorColor = ''
+$accentColor = ''
+
+if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+    $resetColor = $PSStyle.Reset
+    $errorColor = $PSStyle.Formatting.Error
+    $accentColor = $PSStyle.Formatting.ErrorAccent
+}
+function Get-ConciseViewPositionMessage {
+
+    # returns a string cut to last whitespace
+    function Get-TruncatedString($string, [int]$length) {
+
+        if ($string.Length -le $length) {
+            return $string
+        }
+
+        return ($string.Substring(0,$length) -split '\s',-2)[0]
+    }
+
+    $posmsg = ''
+    $headerWhitespace = ''
+    $offsetWhitespace = ''
+    $message = ''
+    $prefix = ''
+
+    # The checks here determine if we show line detailed error information:
+    # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+    # - check if invocation is a script or multiple lines in the console
+    # - check that it's not a script module as expectation is that users don't want to see the line of error within a module
+    if ((($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception -is 'System.Management.Automation.ParentContainsErrorRecordException') -or $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1) -and $myinv.ScriptName -notmatch '\.psm1$') {
+        $useTargetObject = $false
+
+        # Handle case where there is a TargetObject and we can show the error at the target rather than the script source
+        if ($_.TargetObject.Line -and $_.TargetObject.LineText) {
+            $posmsg = "${resetcolor}$($_.TargetObject.File)${newline}"
+            $useTargetObject = $true
+        }
+        elseif ($myinv.ScriptName) {
+            if ($env:TERM_PROGRAM -eq 'vscode') {
+                # If we are running in vscode, we know the file:line:col links are clickable so we use this format
+                $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
+            }
+            else {
+                $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
+            }
+        }
+        else {
+            $posmsg = "${newline}"
+        }
+
+        if ($useTargetObject) {
+            $scriptLineNumber = $_.TargetObject.Line
+            $scriptLineNumberLength = $_.TargetObject.Line.ToString().Length
+        }
+        else {
+            $scriptLineNumber = $myinv.ScriptLineNumber
+            $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
+        }
+
+        if ($scriptLineNumberLength -gt 4) {
+            $headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
+        }
+
+        $lineWhitespace = ''
+        if ($scriptLineNumberLength -lt 4) {
+            $lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
+        }
+
+        $verticalBar = '|'
+        $posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
+
+        $highlightLine = ''
+        if ($useTargetObject) {
+            $line = $_.TargetObject.LineText.Trim()
+            $offsetLength = 0
+            $offsetInLine = 0
+        }
+        else {
+            $positionMessage = $myinv.PositionMessage.Split($newline)
+            $line = $positionMessage[1].Substring(1) # skip the '+' at the start
+            $highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
+            $offsetLength = $highlightLine.Trim().Length
+            $offsetInLine = $highlightLine.IndexOf('~')
+        }
+
+        if (-not $line.EndsWith($newline)) {
+            $line += $newline
+        }
+
+        # don't color the whole line
+        if ($offsetLength -lt $line.Length - 1) {
+            $line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
+        }
+
+        $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
+        $offsetWhitespace = ' ' * $offsetInLine
+        $prefix = "${accentColor}${headerWhitespace}     ${verticalBar} ${errorColor}"
+        if ($highlightLine -ne '') {
+            $posMsg += "${prefix}${highlightLine}${newline}"
+        }
+        $message = "${prefix}"
+    }
+
+    if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+        if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+            # need to parse out the relevant part of the pre-rendered positionmessage
+            $message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+        }
+        elseif ($err.Exception) {
+            $message += $err.Exception.Message
+        }
+        elseif ($err.Message) {
+            $message += $err.Message
+        }
+        else {
+            $message += $err.ToString()
+        }
+    }
+    else {
+        $message += $err.ErrorDetails.Message
+    }
+
+    # if rendering line information, break up the message if it's wider than the console
+    if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
+        $prefixLength = "$([char]27)]8;;{0}`a{1}$([char]27)]8;;`a" -f $pwd, $pwd::new($prefix).ContentLength
+        $prefixVtLength = $prefix.Length - $prefixLength
+
+        # replace newlines in message so it lines up correct
+        $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
+
+        $windowWidth = 120
+        if ($Host.UI.RawUI -ne $null) {
+            $windowWidth = $Host.UI.RawUI.WindowSize.Width
+        }
+
+        if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
+            $sb = [Text.StringBuilder]::new()
+            $substring = Get-TruncatedString -string $message -length ($windowWidth + $prefixVTLength)
+            $null = $sb.Append($substring)
+            $remainingMessage = $message.Substring($substring.Length).Trim()
+            $null = $sb.Append($newline)
+            while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
+                $subMessage = $prefix + $remainingMessage
+                $substring = Get-TruncatedString -string $subMessage -length ($windowWidth + $prefixVtLength)
+
+                if ($substring.Length - $prefix.Length -gt 0)
+                {
+                    $null = $sb.Append($substring)
+                    $null = $sb.Append($newline)
+                    $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
+                }
+                else
+                {
+                    break
+                }
+            }
+            $null = $sb.Append($prefix + $remainingMessage.Trim())
+            $message = $sb.ToString()
+        }
+
+        $message += $newline
+    }
+
+    $posmsg += "${errorColor}" + $message
+
+    $reason = 'Error'
+    if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
+        $reason = 'Exception'
+    }
+    # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+    elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore))
+    {
+        $reason = $myinv.MyCommand
+    }
+    # If it's a scriptblock, better to show the command in the scriptblock that had the error
+    elseif ($_.CategoryInfo.Activity) {
+        $reason = $_.CategoryInfo.Activity
+    }
+    elseif ($myinv.MyCommand) {
+        $reason = $myinv.MyCommand
+    }
+    elseif ($myinv.InvocationName) {
+        $reason = $myinv.InvocationName
+    }
+    elseif ($err.CategoryInfo.Category) {
+        $reason = $err.CategoryInfo.Category
+    }
+    elseif ($err.CategoryInfo.Reason) {
+        $reason = $err.CategoryInfo.Reason
+    }
+
+    $errorMsg = 'Error'
+
+    "${errorColor}${reason}: ${posmsg}${resetcolor}"
+}
+
+$myinv = $_.InvocationInfo
+$err = $_
+if (!$myinv -and $_.ErrorRecord -and $_.ErrorRecord.InvocationInfo) {
+    $err = $_.ErrorRecord
+    $myinv = $err.InvocationInfo
+}
+
+if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQualifiedErrorId -eq 'NativeCommandError') {
+    return "${errorColor}$($err.Exception.Message)${resetcolor}"
+}
+
+$myinv = $err.InvocationInfo
+if ($ErrorView -eq 'DetailedView') {
+    $message = Get-Error
+    return "${errorColor}${message}${resetcolor}"
+}
+
+if ($ErrorView -eq 'CategoryView') {
+    $message = $err.CategoryInfo.GetMessage()
+    return "${errorColor}${message}${resetcolor}"
+}
+
+$posmsg = ''
+if ($ErrorView -eq 'ConciseView') {
+    $posmsg = Get-ConciseViewPositionMessage
+}
+elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) {
+    $posmsg = $myinv.PositionMessage
+}
+
+if ($posmsg -ne '') {
+    $posmsg = $newline + $posmsg
+}
+
+if ($err.PSMessageDetails) {
+    $posmsg = ' : ' +  $err.PSMessageDetails + $posmsg
+}
+
+if ($ErrorView -eq 'ConciseView') {
+    if ($err.PSMessageDetails) {
+        $posmsg = "${errorColor}${posmsg}"
+    }
+    return $posmsg
+}
+
+$indent = 4
+
+$errorCategoryMsg = $err.ErrorCategory_Message
+
+if ($null -ne $errorCategoryMsg)
+{
+    $indentString = '+ CategoryInfo          : ' + $err.ErrorCategory_Message
+}
+else
+{
+    $indentString = '+ CategoryInfo          : ' + $err.CategoryInfo
+}
+
+$posmsg += $newline + $indentString
+
+$indentString = "+ FullyQualifiedErrorId : " + $err.FullyQualifiedErrorId
+$posmsg += $newline + $indentString
+
+$originInfo = $err.OriginInfo
+
+if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName))
+{
+    $indentString = "+ PSComputerName        : " + $originInfo.PSComputerName
+    $posmsg += $newline + $indentString
+}
+
+$finalMsg = if ($err.ErrorDetails.Message) {
+    $err.ErrorDetails.Message + $posmsg
+} else {
+    $err.Exception.Message + $posmsg
+}
+
+"${errorColor}${finalMsg}${resetcolor}"
+
+#>
\ No newline at end of file
diff --git a/Reference/detail copy.ps1 b/Reference/detail copy.ps1
new file mode 100644
index 0000000..79e9c36
--- /dev/null
+++ b/Reference/detail copy.ps1	
@@ -0,0 +1,183 @@
+<#
+[int]$maxDepth = 10
+Set-StrictMode -Off
+
+$ellipsis = "`u{2026}"
+$resetColor = ''
+$errorColor = ''
+$accentColor = ''
+
+if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+    $resetColor = $PSStyle.Reset
+    $errorColor = $psstyle.Formatting.Error
+    $accentColor = $PSStyle.Formatting.FormatAccent
+}
+
+function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+    $newline = [Environment]::Newline
+    $output = [System.Text.StringBuilder]::new()
+    $prefix = ' ' * $indent
+
+    $expandTypes = @(
+        'Microsoft.Rest.HttpRequestMessageWrapper'
+        'Microsoft.Rest.HttpResponseMessageWrapper'
+        'System.Management.Automation.InvocationInfo'
+    )
+
+    # if object is an Exception, add an ExceptionType property
+    if ($obj -is [Exception]) {
+        $obj | Add-Member -NotePropertyName Type -NotePropertyValue $obj.GetType().FullName -ErrorAction Ignore
+    }
+
+    # first find the longest property so we can indent properly
+    $propLength = 0
+    foreach ($prop in $obj.PSObject.Properties) {
+        if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
+            $propLength = $prop.Name.Length
+        }
+    }
+
+    $addedProperty = $false
+    foreach ($prop in $obj.PSObject.Properties) {
+
+        # don't show empty properties or our added property for $error[index]
+        if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+            $addedProperty = $true
+            $null = $output.Append($prefix)
+            $null = $output.Append($accentColor)
+            $null = $output.Append($prop.Name)
+            $propNameIndent = ' ' * ($propLength - $prop.Name.Length)
+            $null = $output.Append($propNameIndent)
+            $null = $output.Append(' : ')
+            $null = $output.Append($resetColor)
+
+            $newIndent = $indent + 4
+
+            # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+            if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+                $expandTypes -contains $prop.TypeNameOfValue -or ($prop.TypeNames -ne $null -and $expandTypes -contains $prop.TypeNames[0])) {
+
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                }
+                else {
+                    $null = $output.Append($newline)
+                    $null = $output.Append((Show-ErrorRecord $prop.Value $newIndent ($depth + 1)))
+                }
+            }
+            # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+            elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                }
+                else {
+                    $targetSite = [PSCustomObject]@{
+                        Name = $prop.Value.Name
+                        DeclaringType = $prop.Value.DeclaringType
+                        MemberType = $prop.Value.MemberType
+                        Module = $prop.Value.Module
+                    }
+
+                    $null = $output.Append($newline)
+                    $null = $output.Append((Show-ErrorRecord $targetSite $newIndent ($depth + 1)))
+                }
+            }
+            # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
+            elseif ($prop.Name -eq 'StackTrace') {
+                # for a stacktrace which is usually quite wide with info, we left justify it
+                $null = $output.Append($newline)
+                $null = $output.Append($prop.Value)
+            }
+            # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
+            elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+                $isFirstElement = $true
+                foreach ($key in $prop.Value.Keys) {
+                    if ($isFirstElement) {
+                        $null = $output.Append($newline)
+                    }
+
+                    if ($key -eq 'Authorization') {
+                        $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
+                    }
+                    else {
+                        $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
+                    }
+
+                    $isFirstElement = $false
+                }
+            }
+            # if the object implements IEnumerable and not a string, we try to show each object
+            # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+            elseif (!($prop.Value -is [System.String]) -and $prop.Value.GetType().GetInterface('IEnumerable') -ne $null -and $prop.Name -ne 'Data') {
+
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                }
+                else {
+                    $isFirstElement = $true
+                    foreach ($value in $prop.Value) {
+                        $null = $output.Append($newline)
+                        if (!$isFirstElement) {
+                            $null = $output.Append($newline)
+                        }
+                        $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
+                        $isFirstElement = $false
+                    }
+                }
+            }
+            # Anything else, we convert to string.
+            # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+            else {
+                $value = $null
+                if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $value -ne $null)
+                {
+                    if ($prop.Name -eq 'PositionMessage') {
+                        $value = $value.Insert($value.IndexOf('~'), $errorColor)
+                    }
+                    elseif ($prop.Name -eq 'Message') {
+                        $value = $errorColor + $value
+                    }
+
+                    $isFirstLine = $true
+                    if ($value.Contains($newline)) {
+                        # the 3 is to account for ' : '
+                        $valueIndent = ' ' * ($propLength + 3)
+                        # need to trim any extra whitespace already in the text
+                        foreach ($line in $value.Split($newline)) {
+                            if (!$isFirstLine) {
+                                $null = $output.Append("${newline}${prefix}${valueIndent}")
+                            }
+                            $null = $output.Append($line.Trim())
+                            $isFirstLine = $false
+                        }
+                    }
+                    else {
+                        $null = $output.Append($value)
+                    }
+                }
+            }
+
+            $null = $output.Append($newline)
+        }
+    }
+
+    # if we had added nested properties, we need to remove the last newline
+    if ($addedProperty) {
+        $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+    }
+
+    $output.ToString()
+}
+
+# Add back original typename and remove PSExtendedError
+if ($_.PSObject.TypeNames.Contains('System.Management.Automation.ErrorRecord#PSExtendedError')) {
+    $_.PSObject.TypeNames.Add('System.Management.Automation.ErrorRecord')
+    $null = $_.PSObject.TypeNames.Remove('System.Management.Automation.ErrorRecord#PSExtendedError')
+}
+elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
+    $_.PSObject.TypeNames.Add('System.Exception')
+    $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
+}
+
+Show-ErrorRecord $_
+#>
\ No newline at end of file
diff --git a/Reference/detail.ps1 b/Reference/detail.ps1
new file mode 100644
index 0000000..79e9c36
--- /dev/null
+++ b/Reference/detail.ps1
@@ -0,0 +1,183 @@
+<#
+[int]$maxDepth = 10
+Set-StrictMode -Off
+
+$ellipsis = "`u{2026}"
+$resetColor = ''
+$errorColor = ''
+$accentColor = ''
+
+if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+    $resetColor = $PSStyle.Reset
+    $errorColor = $psstyle.Formatting.Error
+    $accentColor = $PSStyle.Formatting.FormatAccent
+}
+
+function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+    $newline = [Environment]::Newline
+    $output = [System.Text.StringBuilder]::new()
+    $prefix = ' ' * $indent
+
+    $expandTypes = @(
+        'Microsoft.Rest.HttpRequestMessageWrapper'
+        'Microsoft.Rest.HttpResponseMessageWrapper'
+        'System.Management.Automation.InvocationInfo'
+    )
+
+    # if object is an Exception, add an ExceptionType property
+    if ($obj -is [Exception]) {
+        $obj | Add-Member -NotePropertyName Type -NotePropertyValue $obj.GetType().FullName -ErrorAction Ignore
+    }
+
+    # first find the longest property so we can indent properly
+    $propLength = 0
+    foreach ($prop in $obj.PSObject.Properties) {
+        if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
+            $propLength = $prop.Name.Length
+        }
+    }
+
+    $addedProperty = $false
+    foreach ($prop in $obj.PSObject.Properties) {
+
+        # don't show empty properties or our added property for $error[index]
+        if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+            $addedProperty = $true
+            $null = $output.Append($prefix)
+            $null = $output.Append($accentColor)
+            $null = $output.Append($prop.Name)
+            $propNameIndent = ' ' * ($propLength - $prop.Name.Length)
+            $null = $output.Append($propNameIndent)
+            $null = $output.Append(' : ')
+            $null = $output.Append($resetColor)
+
+            $newIndent = $indent + 4
+
+            # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+            if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+                $expandTypes -contains $prop.TypeNameOfValue -or ($prop.TypeNames -ne $null -and $expandTypes -contains $prop.TypeNames[0])) {
+
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                }
+                else {
+                    $null = $output.Append($newline)
+                    $null = $output.Append((Show-ErrorRecord $prop.Value $newIndent ($depth + 1)))
+                }
+            }
+            # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+            elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                }
+                else {
+                    $targetSite = [PSCustomObject]@{
+                        Name = $prop.Value.Name
+                        DeclaringType = $prop.Value.DeclaringType
+                        MemberType = $prop.Value.MemberType
+                        Module = $prop.Value.Module
+                    }
+
+                    $null = $output.Append($newline)
+                    $null = $output.Append((Show-ErrorRecord $targetSite $newIndent ($depth + 1)))
+                }
+            }
+            # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
+            elseif ($prop.Name -eq 'StackTrace') {
+                # for a stacktrace which is usually quite wide with info, we left justify it
+                $null = $output.Append($newline)
+                $null = $output.Append($prop.Value)
+            }
+            # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
+            elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+                $isFirstElement = $true
+                foreach ($key in $prop.Value.Keys) {
+                    if ($isFirstElement) {
+                        $null = $output.Append($newline)
+                    }
+
+                    if ($key -eq 'Authorization') {
+                        $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
+                    }
+                    else {
+                        $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
+                    }
+
+                    $isFirstElement = $false
+                }
+            }
+            # if the object implements IEnumerable and not a string, we try to show each object
+            # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+            elseif (!($prop.Value -is [System.String]) -and $prop.Value.GetType().GetInterface('IEnumerable') -ne $null -and $prop.Name -ne 'Data') {
+
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                }
+                else {
+                    $isFirstElement = $true
+                    foreach ($value in $prop.Value) {
+                        $null = $output.Append($newline)
+                        if (!$isFirstElement) {
+                            $null = $output.Append($newline)
+                        }
+                        $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
+                        $isFirstElement = $false
+                    }
+                }
+            }
+            # Anything else, we convert to string.
+            # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+            else {
+                $value = $null
+                if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $value -ne $null)
+                {
+                    if ($prop.Name -eq 'PositionMessage') {
+                        $value = $value.Insert($value.IndexOf('~'), $errorColor)
+                    }
+                    elseif ($prop.Name -eq 'Message') {
+                        $value = $errorColor + $value
+                    }
+
+                    $isFirstLine = $true
+                    if ($value.Contains($newline)) {
+                        # the 3 is to account for ' : '
+                        $valueIndent = ' ' * ($propLength + 3)
+                        # need to trim any extra whitespace already in the text
+                        foreach ($line in $value.Split($newline)) {
+                            if (!$isFirstLine) {
+                                $null = $output.Append("${newline}${prefix}${valueIndent}")
+                            }
+                            $null = $output.Append($line.Trim())
+                            $isFirstLine = $false
+                        }
+                    }
+                    else {
+                        $null = $output.Append($value)
+                    }
+                }
+            }
+
+            $null = $output.Append($newline)
+        }
+    }
+
+    # if we had added nested properties, we need to remove the last newline
+    if ($addedProperty) {
+        $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+    }
+
+    $output.ToString()
+}
+
+# Add back original typename and remove PSExtendedError
+if ($_.PSObject.TypeNames.Contains('System.Management.Automation.ErrorRecord#PSExtendedError')) {
+    $_.PSObject.TypeNames.Add('System.Management.Automation.ErrorRecord')
+    $null = $_.PSObject.TypeNames.Remove('System.Management.Automation.ErrorRecord#PSExtendedError')
+}
+elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
+    $_.PSObject.TypeNames.Add('System.Exception')
+    $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
+}
+
+Show-ErrorRecord $_
+#>
\ No newline at end of file
diff --git a/source/ErrorView.format.ps1xml b/source/ErrorView.format.ps1xml
index 443e23a..028b7b5 100644
--- a/source/ErrorView.format.ps1xml
+++ b/source/ErrorView.format.ps1xml
@@ -68,5 +68,35 @@
         </CustomEntries>
       </CustomControl>
     </View>
+    <View>
+      <Name>InformationRecord</Name>
+      <OutOfBand />
+      <ViewSelectedBy>
+        <TypeName>System.Management.Automation.InformationRecord</TypeName>
+      </ViewSelectedBy>
+      <CustomControl>
+        <CustomEntries>
+          <CustomEntry>
+            <CustomItem>
+              <ExpressionBinding>
+                <ScriptBlock>
+                  $_.MessageData | Format-List * | Out-String
+                </ScriptBlock>
+              </ExpressionBinding>
+              <ExpressionBinding>
+                <ScriptBlock>
+                  "Tags: " + @($_.Tags -join ", ")
+                </ScriptBlock>
+              </ExpressionBinding>
+              <ExpressionBinding>
+                <ScriptBlock>
+                  $_ | Select-Object * -ExcludeProperty Tags, MessageData | Format-List * -Force | Out-String
+                </ScriptBlock>
+              </ExpressionBinding>
+            </CustomItem>
+          </CustomEntry>
+        </CustomEntries>
+      </CustomControl>
+    </View>
   </ViewDefinitions>
 </Configuration>
diff --git a/source/prefix.ps1 b/source/prefix.ps1
index 9f73337..aef18c4 100644
--- a/source/prefix.ps1
+++ b/source/prefix.ps1
@@ -1,8 +1,53 @@
 [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Justification = 'ErrorView is all about the ErrorView global variable')]
+[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Seriously. Stop complaining about ErrorView')]
 param(
     $global:ErrorView = "Simple"
 )
 
 # We need to _overwrite_ the ErrorView
 # So -PrependPath, instead of FormatsToProcess
-Update-FormatData -PrependPath $PSScriptRoot\ErrorView.format.ps1xml
\ No newline at end of file
+Update-FormatData -PrependPath $PSScriptRoot\ErrorView.format.ps1xml
+
+Set-StrictMode -Off
+
+# Borrowed this one from https://github.com/chalk/ansi-regex
+$script:AnsiEscapes = [Regex]::new("([\u001b\u009b][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?(?:\u001b\u005c|\u0007))|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~])))", "Compiled");
+
+# starting with an escape character and then...
+# ESC ] <anything> <ST> - where ST is either 1B 5C or 7 (BEL, aka `a)
+# ESC [ non-letters letter (or ~, =, @, >)
+# ESC ( <any character>
+# ESC O P
+# ESC O Q
+# ESC O R
+# ESC O S
+# $script:AnsiEscapes = [Regex]::new("\x1b[\(\)%`"&\.\/*+.-][@-Z]|\x1b\].*?(?:\u001b\u005c|\u0007|^)|\x1b\[\P{L}*[@-_A-Za-z^`\{\|\}~]|\x1b#\d|\x1b[!-~]", [System.Text.RegularExpressions.RegexOptions]::Compiled);
+
+
+
+
+
+$script:ellipsis = [char]0x2026
+$script:newline = [Environment]::Newline
+$script:resetColor = ''
+$script:errorColor = ''
+$script:accentColor = ''
+$script:errorAccentColor = ''
+$script:LineColors = @(
+    "`e[38;2;255;255;255m"
+    "`e[38;2;179;179;179m"
+)
+
+if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+    if ($PSStyle) {
+        $script:resetColor = $PSStyle.Reset
+        $script:errorColor = $PSStyle.Formatting.Error
+        $script:accentColor = $PSStyle.Formatting.FormatAccent
+        $script:errorAccentColor = $PSStyle.Formatting.ErrorAccent
+    } else {
+        $script:resetColor = "$([char]27)[0m"
+        $script:errorColor = "$([char]27)[31m"
+        $script:accentColor = "$([char]27)[32;1m"
+        $script:errorAccentColor = "$([char]27)[31;1m"
+    }
+}
\ No newline at end of file
diff --git a/source/private/GetListRecursive.ps1 b/source/private/GetListRecursive.ps1
new file mode 100644
index 0000000..a6cdd05
--- /dev/null
+++ b/source/private/GetListRecursive.ps1
@@ -0,0 +1,160 @@
+function GetListRecursive {
+    <#
+        .SYNOPSIS
+            Internal implementation of the Detailed error view to support recursion and indentation
+    #>
+    [CmdletBinding()]
+    param(
+        $InputObject,
+        [int]$indent = 0,
+        [int]$depth = 1
+    )
+    Write-Information "ENTER GetListRecursive END $($InputObject.GetType().FullName) $indent $depth" -Tags 'Trace', 'Enter', 'GetListRecursive'
+    Write-Information (Get-PSCallStack) -Tags 'Trace', 'StackTrace', 'GetListRecursive'
+    $output = [System.Text.StringBuilder]::new()
+    $prefix = ' ' * $indent
+
+    $expandTypes = @(
+        'Microsoft.Rest.HttpRequestMessageWrapper'
+        'Microsoft.Rest.HttpResponseMessageWrapper'
+        'System.Management.Automation.InvocationInfo'
+    )
+
+    # The built-in DetailedView aligns all the ":" characters, so we need to find the longest property name
+    $propLength = 0
+    foreach ($prop in $InputObject.PSObject.Properties) {
+        if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
+            $propLength = $prop.Name.Length
+        }
+    }
+
+    $addedProperty = $false
+    foreach ($prop in $InputObject.PSObject.Properties) {
+        # don't show empty properties or our added property for $error[index]
+        if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+            $addedProperty = $true
+            $null = $output.Append($prefix)
+            $null = $output.Append($accentColor)
+            $null = $output.Append($prop.Name)
+            $null = $output.Append(' ',($propLength - $prop.Name.Length))
+            $null = $output.Append(' : ')
+            $null = $output.Append($resetColor)
+
+            $newIndent = $indent + 2
+
+            # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+            if ($prop.Value -is [Exception] -or
+                $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+                $expandTypes -contains $prop.TypeNameOfValue -or
+                        ($null -ne $prop.TypeNames -and $expandTypes -contains $prop.TypeNames[0])) {
+
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                } else {
+                    if ($prop.Value -is [Exception]) {
+                        $null = $output.Append($newline)
+                        $null = $output.Append((
+                            GetListRecursive ([PSCustomObject]@{
+                                                "Type" = $errorAccentColor + $prop.Value.GetType().FullName + $resetColor
+                                            }) $newIndent ($depth + 1)
+                        ))
+                    }
+                    $null = $output.Append($newline)
+                    $null = $output.Append((GetListRecursive $prop.Value $newIndent ($depth + 1)))
+                }
+            } elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+                # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                } else {
+                    $targetSite = [PSCustomObject]@{
+                        Name          = $prop.Value.Name
+                        DeclaringType = $prop.Value.DeclaringType
+                        MemberType    = $prop.Value.MemberType
+                        Module        = $prop.Value.Module
+                    }
+
+                    $null = $output.Append($newline)
+                    $null = $output.Append((GetListRecursive $targetSite $newIndent ($depth + 1)))
+                }
+            } elseif ($prop.Name -eq 'StackTrace') {
+                # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
+                # for a stacktrace which is usually quite wide with info, we left justify it
+                $null = $output.Append($newline)
+                $null = $output.Append($prop.Value)
+            } elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+                # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
+                $isFirstElement = $true
+                foreach ($key in $prop.Value.Keys) {
+                    if ($isFirstElement) {
+                        $null = $output.Append($newline)
+                    }
+
+                    if ($key -eq 'Authorization') {
+                        $null = $output.Append("${prefix}    ${accentColor}${key}: ${resetColor}${ellipsis}${newline}")
+                    } else {
+                        $null = $output.Append("${prefix}    ${accentColor}${key}: ${resetColor}$($prop.Value[$key])${newline}")
+                    }
+
+                    $isFirstElement = $false
+                }
+            } elseif (!($prop.Value -is [System.String]) -and $null -ne $prop.Value.GetType().GetInterface('IEnumerable') -and $prop.Name -ne 'Data') {
+                # if the object implements IEnumerable and not a string, we try to show each object
+                # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+
+                if ($depth -ge $maxDepth) {
+                    $null = $output.Append($ellipsis)
+                } else {
+                    $isFirstElement = $true
+                    foreach ($value in $prop.Value) {
+                        $null = $output.Append($newline)
+                        if (!$isFirstElement) {
+                            $null = $output.Append($newline)
+                        }
+                        $null = $output.Append((GetListRecursive $value $newIndent ($depth + 1)))
+                        $isFirstElement = $false
+                    }
+                }
+            } else {
+                # Anything else, we convert to string.
+                # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+                $value = $null
+                if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $null -ne $value) {
+                    $value = $value.Trim()
+                    if ($prop.Name -eq 'PositionMessage') {
+                        $value = $value.Insert($value.IndexOf('~'), $errorColor)
+                    } elseif ($prop.Name -eq 'Message') {
+                        $value = $errorColor + $value
+                    }
+
+                    $isFirstLine = $true
+                    if ($value.Contains($newline)) {
+                        # the 3 is to account for ' : '
+                        # $valueIndent = ' ' * ($prop.Name.Length + 2)
+                        $valueIndent = ' ' * ($propLength + 3)
+                        # need to trim any extra whitespace already in the text
+                        foreach ($line in $value.Split($newline)) {
+                            if (!$isFirstLine) {
+                                $null = $output.Append("${newline}${prefix}${valueIndent}")
+                            }
+                            $null = $output.Append($line.Trim())
+                            $isFirstLine = $false
+                        }
+                    } else {
+                        $null = $output.Append($value)
+                    }
+                }
+            }
+
+            $null = $output.Append($newline)
+        }
+    }
+
+    # if we had added nested properties, we need to remove the last newline
+    if ($addedProperty) {
+        $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+    }
+
+    $output.ToString()
+    Write-Information "EXIT GetListRecursive END $($InputObject.GetType().FullName) $indent $depth (of $maxDepth)" -Tags 'Trace', 'Enter', 'GetListRecursive'
+}
diff --git a/source/private/GetYamlRecursive.ps1 b/source/private/GetYamlRecursive.ps1
new file mode 100644
index 0000000..ac3880e
--- /dev/null
+++ b/source/private/GetYamlRecursive.ps1
@@ -0,0 +1,169 @@
+function GetYamlRecursive {
+    <#
+        .SYNOPSIS
+            Creates a description of an ErrorRecord that looks like valid Yaml
+        .DESCRIPTION
+            This produces valid Yaml output from ErrorRecord you pass to it, recursively.
+    #>
+    [CmdletBinding()]
+    param(
+        # The object that you want to convert to YAML
+        [Parameter(Mandatory, ValueFromPipeline)]
+        $InputObject,
+
+        # Optionally, a limit on the depth to recurse properties (defaults to 16)
+        [parameter()]
+        [int]$depth = 16,
+
+        # If set, include empty and null properties in the output
+        [switch]$IncludeEmpty,
+
+        # Recursive use only. Handles indentation for formatting
+        [parameter(DontShow)]
+        [int]$NestingLevel = 0,
+
+        # use OuterXml instead of treating XmlDocuments like objects
+        [parameter(DontShow)]
+        [switch]$XmlAsXml
+    )
+    process {
+        $wrap = [Console]::BufferWidth - 1 - ($NestingLevel * 2)
+        @(
+            if ($Null -eq $InputObject) { return 'null' } # if it is null return null
+            if ($NestingLevel -eq 0 -and $local:__hasoutput) { '---' } # if we have output before, add a yaml separator
+            $__hasoutput = $true
+            $padding = "`n$('  ' * $NestingLevel)" # lets just create our left-padding for the block
+            $Recurse = @{
+                'Depth'        = $depth - 1
+                'NestingLevel' = $NestingLevel + 1
+                'XmlAsXml'     = $XmlAsXml
+            }
+            $Wrap =
+
+            try {
+                switch ($InputObject) {
+                    # prevent these values being expanded
+                    <# if ($Type -in @( 'guid',
+                            , 'datatable', 'List`1','SqlDataReader', 'datarow', 'type',
+                            'MemberTypes', 'RuntimeModule', 'RuntimeType', 'ErrorCategoryInfo', 'CommandInfo', 'CmdletInfo' )) {
+                    #>
+                    { $InputObject -is [scriptblock] } {
+                        "{$($InputObject.ToString())}"
+                        break
+                    }
+                    { $InputObject -is [type] } {
+                        "'[$($InputObject.FullName)]'"
+                        break
+                    }
+                    { $InputObject -is [System.Xml.XmlDocument] -or $InputObject -is [System.Xml.XmlElement] } {
+                        "|"
+                        $InputObject.OuterXml | WrapString $Wrap $padding -Colors:$LineColors
+                        break
+                    }
+                    { $InputObject -is [datetime] -or $InputObject -is [datetimeoffset] } {
+                        # s=SortableDateTimePattern (based on ISO 8601) using local time
+                        $InputObject.ToString('s')
+                        break
+                    }
+                    { $InputObject -is [timespan] -or $InputObject -is [version] -or $InputObject -is [uri] } {
+                        # s=SortableDateTimePattern (based on ISO 8601) using local time
+                        "'$InputObject'"
+                        break
+                    }
+                    # yaml case for booleans
+                    { $InputObject -is [bool] } {
+                        if ($InputObject) { 'true' } else { 'false' }
+                        break
+                    }
+                    # If we're going to go over our depth, just output like it's a value type
+                    # ValueTypes are just output with no possibility of wrapping or recursion
+                    { $InputObject -is [Enum] -or $InputObject.GetType().BaseType -eq [ValueType] -or $depth -eq 1 } {
+                        "$InputObject"
+                        break
+                    }
+                    # 'PSNoteProperty' {
+                    #     # Write-Verbose "$($padding)Show $($property.Name)"
+                    #     GetYamlRecursive -InputObject $InputObject.Value @Recurse }
+                    { $InputObject -is [System.Collections.IDictionary] } {
+                        foreach ($kvp in  $InputObject.GetEnumerator()) {
+                            # Write-Verbose "$($padding)Enumerate $($property.Name)"
+                            "$padding$accentColor$($kvp.Name):$resetColor " +
+                            (GetYamlRecursive -InputObject $kvp.Value @Recurse)
+                        }
+                        break
+                    }
+
+                    { $InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string] } {
+                        foreach ($item in $InputObject) {
+                            # Write-Verbose "$($padding)Enumerate $($property.Name)"
+                            $Value = GetYamlRecursive -InputObject $item @Recurse
+                            # if ($Value -ne 'null' -or $IncludeEmpty) {
+                            "$accentColor$padding$resetColor- $Value"
+                            # }
+                        }
+                        break
+                    }
+
+                    # Limit recursive enumeration to specific types:
+                    { $InputObject -is [Exception] -or $InputObject -is [System.Management.Automation.ErrorRecord] -or
+                        $InputObject.PSTypeNames[0] -in @(
+                            'System.Exception'
+                            'System.Management.Automation.ErrorRecord'
+                            'Microsoft.Rest.HttpRequestMessageWrapper'
+                            'Microsoft.Rest.HttpResponseMessageWrapper'
+                            'System.Management.Automation.InvocationInfo'
+                        ) } {
+                        # For exceptions, output a fake property for the exception type
+                        if ($InputObject -is [Exception]) {
+                            "$padding${accentColor}#Type:$resetColor ${errorAccentColor}" + $InputObject.GetType().FullName + $resetColor
+                        }
+                        foreach ($property in $InputObject.PSObject.Properties) {
+                            if ($property.Value) {
+                                $Value = GetYamlRecursive -InputObject $property.Value @Recurse
+                                # For special cases, add some color:
+                                if ($property.Name -eq "PositionMessage") {
+                                    $Value = $Value -replace "(\+\s+)(~+)", "`$1$errorColor`$2$resetColor"
+                                }
+                                if ($InputObject -is [Exception] -and $property.Name -eq "Message") {
+                                    $Value = "$errorColor$Value$resetColor"
+                                }
+                                if ((-not [string]::IsNullOrEmpty($Value) -and $Value -ne 'null' -and $Value.Count -gt 0) -or $IncludeEmpty) {
+                                    "$padding$accentColor$($property.Name):$resetColor " + $Value
+                                }
+                            }
+                        }
+                        break
+                    }
+                    # 'generic' {
+                    #     foreach($key in $InputObject.Keys) {
+                    #         # Write-Verbose "$($padding)Enumerate $($key)"
+                    #         $Value = GetYamlRecursive -InputObject $InputObject.$key @Recurse
+                    #         if ((-not [string]::IsNullOrEmpty($Value) -and $Value -ne 'null') -or $IncludeEmpty) {
+                    #             "$padding$accentColor$($key):$resetColor " + $Value
+                    #         }
+                    #     }
+                    # }
+                    default {
+                        # Treat anything else as a string
+                        $StringValue = $null
+                        if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($InputObject, [string], [ref]$StringValue) -and $null -ne $StringValue) {
+                            $StringValue = $StringValue.Trim()
+                            if ($StringValue -match '[\r\n]' -or $StringValue.Length -gt $wrap) {
+                                ">" # signal that we are going to use the readable 'newlines-folded' format
+                                $StringValue | WrapString $Wrap $padding -Colors:$LineColors
+                            } elseif ($StringValue.Contains(":")) {
+                                "'$($StringValue -replace '''', '''''')'" # single quote it
+                            } else {
+                                "$($StringValue -replace '''', '''''')"
+                            }
+                        } else {
+                            Write-Warning "Unable to convert $($InputObject.GetType().FullName) to string"
+                        }
+                    }
+                }
+            } catch {
+                Write-Error "Error'$($_)' in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($InputObject)' Class: $($InputObject.GetType().Name) BaseClass: $($InputObject.GetType().BaseType.Name) "
+            }
+        ) -join ""
+    }
+}
\ No newline at end of file
diff --git a/source/private/WrapString.ps1 b/source/private/WrapString.ps1
new file mode 100644
index 0000000..4d3fbfa
--- /dev/null
+++ b/source/private/WrapString.ps1
@@ -0,0 +1,119 @@
+$script:AnsiPattern = "[\u001b\u009b][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?(?:\u001b\u005c|\u0007))|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))"
+$script:AnsiRegex = [Regex]::new($AnsiPattern, "Compiled");
+function MeasureString {
+    [CmdletBinding()]
+    param(
+        [string]$InputObject
+    )
+    $AnsiRegex.Replace($InputObject, '').Length
+}
+
+
+filter WrapString {
+    [CmdletBinding()]
+    param(
+        # The input string will be wrapped to a certain length, with optional padding on the front
+        [Parameter(ValueFromPipeline)]
+        [string]$InputObject,
+
+        # The maximum length of a line. Defaults to [Console]::BufferWidth - 1
+        [Parameter(Position=0)]
+        [int]$Width = ($Host.UI.RawUI.BufferSize.Width),
+
+        # The padding to add to the front of each line to cause indenting. Defaults to empty string.
+        [Parameter(Position=1)]
+        [string]$IndentPadding = ([string]::Empty),
+
+        # If set, colors to use for alternating lines
+        [string[]]$Colors = @(''),
+
+        # If set, will output empty lines for each original new line
+        [switch]$EmphasizeOriginalNewlines
+    )
+    begin {
+        $color = 0;
+        Write-Debug "Colors: $($Colors -replace "`e(.+)", "`e`$1``e`$1")"
+        # $wrappableChars = [char[]]" ,.?!:;-`n`r`t"
+        # $maxLength = $width - $IndentPadding.Length -1
+        $wrapper = [Regex]::new("((?:$AnsiPattern)*[^-=,.?!:;\s\r\n\t\\\/\|]+(?:$AnsiPattern)*)", "Compiled")
+        $output = [System.Text.StringBuilder]::new($Colors[$color] + $IndentPadding)
+    }
+    process {
+        foreach($line in $InputObject -split "(\r?\n)") {
+            # Don't bother trying to split empty lines
+            if ([String]::IsNullOrWhiteSpace($AnsiRegex.Replace($line, ''))) {
+                Write-Debug "Empty String ($($line.Length))"
+                if ($EmphasizeOriginalNewlines) { [string]::Empty }
+                continue
+            }
+
+            $slices = $line -split $wrapper | ForEach-Object { @{ Text = $_; Length = MeasureString $_ } }
+            Write-Debug "$($line.Length) words in line. $($AnsiRegex.Replace($line, ''))"
+            $lineLength = $IndentPadding.Length
+            foreach($slice in $slices) {
+                $lineLength += $slice.Length
+                if ($lineLength -le $Width) {
+                    Write-Verbose "+ $($slice.Length) = $lineLength < $Width"
+                    $null = $output.Append($slice.Text)
+                } else {
+                    Write-Verbose "Output $($lineLength - $slice.Length)"
+                    Write-Verbose "+ $($slice.Length) = $($slice.Length)"
+                    $color = ($color + 1) % $Colors.Length
+                    $output.ToString().Trim()
+                    $null = $output.Clear().Append($Colors[$color]).Append($IndentPadding).Append($slice.Text)
+                    $lineLength = $IndentPadding.Length + $slice.Length
+                }
+            }
+            $output.ToString().Trim()
+            $null = $output.Clear().Append($Colors[$color]).Append($IndentPadding)
+        }
+
+            # $currentIndex = 0;
+            # $lastWrap = 0;
+            # do {
+            #     $lastWrap = $currentIndex;
+            #     #Write-Verbose "m: $Width, l: $($line.Length), c: $color $($Colors[$color] -replace "`e","``e")"
+
+
+
+            #     if ($AnsiEscapes.Replace($first, '').Length -gt $maxLength + 1) {
+            #         # If we couldn't find a good place to wrap, just wrap at the end of the line
+            #         $first = $line.Substring(0, $maxLength+1)
+            #         $line = $line.Substring($maxLength+1)
+            #     }
+            #     $Colors[$color] + $IndentPadding + $first.TrimEnd()
+
+<#                 $currentIndex = $(
+                    if ($lastWrap + $Width -gt $line.Length) {
+                        $line.Length
+                    } else {
+                        $line.LastIndexOfAny($wrappableChars, ([Math]::Min(($line.Length - 1), ($lastWrap + $Width)))) + 1
+                    }
+                )
+
+                $slice = $line.Substring($lastWrap, ($currentIndex - $lastWrap))
+                if (($slice.Length - $script:AnsiEscapes.Replace($slice,'').Length) -gt 0) {
+                    $currentIndex = $(
+                        if ($lastWrap + $Width -gt $line.Length) {
+                            $line.Length
+                        } else {
+                            $line.LastIndexOfAny($wrappableChars, ([Math]::Min(($line.Length - 1), ($lastWrap + $Width)))) + 1
+                        }
+                    )
+                }
+
+                # If we couldn't find a good place to wrap, just wrap at the end of the line
+                if ($currentIndex -le $lastWrap) {
+                    $currentIndex = [Math]::Min(($lastWrap + $Width), $line.Length )
+                }
+                # Output the line, with the appropriate color and padding
+                $Colors[$color] + $IndentPadding + $line.Substring($lastWrap, ($currentIndex - $lastWrap)).TrimEnd()
+#>
+<#             } while($line);
+
+            if ($line -ne "`n") {
+                $color = ($color + 1) % $Colors.Length
+            }
+        } #>
+    }
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-ConciseErrorView.ps1 b/source/public/ConvertTo-ConciseErrorView.ps1
new file mode 100644
index 0000000..391014f
--- /dev/null
+++ b/source/public/ConvertTo-ConciseErrorView.ps1
@@ -0,0 +1,62 @@
+function ConvertTo-ConciseErrorView {
+    [CmdletBinding()]
+    param(
+        [System.Management.Automation.ErrorRecord]
+        $InputObject
+    )
+    if ("$accentColor".Length) {
+        $local:accentColor = $script:errorAccentColor
+        $local:resetColor = $script:resetColor
+    } else {
+        $local:accentColor = ">>>"
+        $local:resetColor = "<<<"
+    }
+
+
+    if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
+        $errorColor + $InputObject.Exception.Message + $resetColor
+    } else {
+        $myinv = $InputObject.InvocationInfo
+        if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
+            # rip off lines that say "At line:1 char:1" (hopefully, in a language agnostic way)
+            $posmsg  = $myinv.PositionMessage -replace "^At line:1 char:1[\r\n]+"
+
+            # rip off the underline and use the accentcolor instead
+            $pattern = $posmsg -split "[\r\n]+" -match "\+( +~+)\s*" -replace '(~+)', '($1)' -replace '( +)','($1)' -replace '~| ','.'
+            $posmsg  = $posmsg -replace '[\r\n]+\+ +~+'
+            if ($pattern) {
+                $posmsg  = $posmsg -replace "\+$pattern", "+`$1$accentColor`$2$resetColor"
+            }
+        } else {
+            $posmsg = ""
+        }
+
+        if ($posmsg -ne "") {
+            $posmsg = "`n" + $posmsg
+        }
+
+        if ( & { Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
+            $posmsg = " : " + $InputObject.PSMessageDetails + $posmsg
+        }
+
+        $indent = 4
+        $width = $host.UI.RawUI.BufferSize.Width - $indent - 2
+
+        $originInfo = & { Set-StrictMode -Version 1; $InputObject.OriginInfo }
+        if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
+            $indentString = "+ PSComputerName        : " + $originInfo.PSComputerName
+            $posmsg += "`n"
+            foreach ($line in @($indentString -split "(.{$width})")) {
+                if ($line) {
+                    $posmsg += (" " * $indent + $line)
+                }
+            }
+        }
+
+        if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
+            $errorColor + $InputObject.Exception.Message + $resetColor + $posmsg + "`n "
+        } else {
+            $errorColor + $InputObject.ErrorDetails.Message + $resetColor + $posmsg
+        }
+    }
+}
\ No newline at end of file
diff --git a/source/public/ConvertTo-DetailedErrorView.ps1 b/source/public/ConvertTo-DetailedErrorView.ps1
index fbc1c58..210520c 100644
--- a/source/public/ConvertTo-DetailedErrorView.ps1
+++ b/source/public/ConvertTo-DetailedErrorView.ps1
@@ -21,186 +21,13 @@ function ConvertTo-DetailedErrorView {
     )
 
     begin {
+        Write-Information "ENTER ConvertTo-DetailedErrorView BEGIN " -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
 
-        Set-StrictMode -Off
-
-        $ellipsis = "`u{2026}"
-        $resetColor = ''
-        $errorColor = ''
-        $accentColor = ''
-        $newline = [Environment]::Newline
-        $OutputRoot = [System.Text.StringBuilder]::new()
-
-        if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-            $resetColor = "$([char]0x1b)[0m"
-            $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
-            $accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
-        }
-
-        function DetailedErrorView {
-            <#
-                .SYNOPSIS
-                    Internal implementation of the Detailed error view to support recursion and indentation
-            #>
-            [CmdletBinding()]
-            param(
-                $InputObject,
-                [int]$indent = 0,
-                [int]$depth = 1
-            )
-            $prefix = ' ' * $indent
-
-            $expandTypes = @(
-                'Microsoft.Rest.HttpRequestMessageWrapper'
-                'Microsoft.Rest.HttpResponseMessageWrapper'
-                'System.Management.Automation.InvocationInfo'
-            )
-
-            # if object is an Exception, add an ExceptionType property
-            if ($InputObject -is [Exception]) {
-                $InputObject | Add-Member -NotePropertyName Type -NotePropertyValue $InputObject.GetType().FullName -ErrorAction Ignore
-            }
-
-            # first find the longest property so we can indent properly
-            $propLength = 0
-            foreach ($prop in $InputObject.PSObject.Properties) {
-                if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
-                    $propLength = $prop.Name.Length
-                }
-            }
-
-            $addedProperty = $false
-            foreach ($prop in $InputObject.PSObject.Properties) {
-
-                # don't show empty properties or our added property for $error[index]
-                if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
-                    $addedProperty = $true
-                    $null = $OutputRoot.Append($prefix)
-                    $null = $OutputRoot.Append($accentColor)
-                    $null = $OutputRoot.Append($prop.Name)
-                    $null = $OutputRoot.Append(' ',($propLength - $prop.Name.Length))
-                    $null = $OutputRoot.Append(' : ')
-                    $null = $OutputRoot.Append($resetColor)
-
-                    $newIndent = $indent + 4
-
-                    # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
-                    if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
-                        $expandTypes -contains $prop.TypeNameOfValue -or ($null -ne $prop.TypeNames -and $expandTypes -contains $prop.TypeNames[0])) {
-
-                        if ($depth -ge $maxDepth) {
-                            $null = $OutputRoot.Append($ellipsis)
-                        }
-                        else {
-                            $null = $OutputRoot.Append($newline)
-                            $null = $OutputRoot.Append((DetailedErrorView $prop.Value $newIndent ($depth + 1)))
-                        }
-                    }
-                    # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
-                    elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
-                        if ($depth -ge $maxDepth) {
-                            $null = $OutputRoot.Append($ellipsis)
-                        }
-                        else {
-                            $targetSite = [PSCustomObject]@{
-                                Name = $prop.Value.Name
-                                DeclaringType = $prop.Value.DeclaringType
-                                MemberType = $prop.Value.MemberType
-                                Module = $prop.Value.Module
-                            }
-
-                            $null = $OutputRoot.Append($newline)
-                            $null = $OutputRoot.Append((DetailedErrorView $targetSite $newIndent ($depth + 1)))
-                        }
-                    }
-                    # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
-                    elseif ($prop.Name -eq 'StackTrace') {
-                        # for a stacktrace which is usually quite wide with info, we left justify it
-                        $null = $OutputRoot.Append($newline)
-                        $null = $OutputRoot.Append($prop.Value)
-                    }
-                    # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
-                    elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
-                        $isFirstElement = $true
-                        foreach ($key in $prop.Value.Keys) {
-                            if ($isFirstElement) {
-                                $null = $OutputRoot.Append($newline)
-                            }
-
-                            if ($key -eq 'Authorization') {
-                                $null = $OutputRoot.Append("${prefix}    ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
-                            }
-                            else {
-                                $null = $OutputRoot.Append("${prefix}    ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
-                            }
-
-                            $isFirstElement = $false
-                        }
-                    }
-                    # if the object implements IEnumerable and not a string, we try to show each object
-                    # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
-                    elseif (!($prop.Value -is [System.String]) -and $null -ne $prop.Value.GetType().GetInterface('IEnumerable') -and $prop.Name -ne 'Data') {
-
-                        if ($depth -ge $maxDepth) {
-                            $null = $OutputRoot.Append($ellipsis)
-                        }
-                        else {
-                            $isFirstElement = $true
-                            foreach ($value in $prop.Value) {
-                                $null = $OutputRoot.Append($newline)
-                                if (!$isFirstElement) {
-                                    $null = $OutputRoot.Append($newline)
-                                }
-                                $null = $OutputRoot.Append((DetailedErrorView $value $newIndent ($depth + 1)))
-                                $isFirstElement = $false
-                            }
-                        }
-                    }
-                    # Anything else, we convert to string.
-                    # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
-                    else {
-                        $value = $null
-                        if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $null -ne $value)
-                        {
-                            if ($prop.Name -eq 'PositionMessage') {
-                                $value = $value.Insert($value.IndexOf('~'), $errorColor)
-                            }
-                            elseif ($prop.Name -eq 'Message') {
-                                $value = $errorColor + $value
-                            }
-
-                            $isFirstLine = $true
-                            if ($value.Contains($newline)) {
-                                # the 3 is to account for ' : '
-                                $valueIndent = ' ' * ($propLength + 3)
-                                # need to trim any extra whitespace already in the text
-                                foreach ($line in $value.Split($newline)) {
-                                    if (!$isFirstLine) {
-                                        $null = $OutputRoot.Append("${newline}${prefix}${valueIndent}")
-                                    }
-                                    $null = $OutputRoot.Append($line.Trim())
-                                    $isFirstLine = $false
-                                }
-                            }
-                            else {
-                                $null = $OutputRoot.Append($value)
-                            }
-                        }
-                    }
-
-                    $null = $OutputRoot.Append($newline)
-                }
-            }
-
-            # if we had added nested properties, we need to remove the last newline
-            if ($addedProperty) {
-                $null = $OutputRoot.Remove($OutputRoot.Length - $newline.Length, $newline.Length)
-            }
-
-            $OutputRoot.ToString()
-        }
+        Write-Information "EXIT ConvertTo-DetailedErrorView BEGIN" -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
     }
     process {
-        DetailedErrorView $InputObject
+        Write-Information "ENTER ConvertTo-DetailedErrorView PROCESS $($InputObject.GetType().FullName)" -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
+        GetListRecursive $InputObject
+        Write-Information "EXIT ConvertTo-DetailedErrorView PROCESS $($InputObject.GetType().FullName)" -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
     }
 }
diff --git a/source/public/ConvertTo-FullErrorView.ps1 b/source/public/ConvertTo-FullErrorView.ps1
index 5377ef8..c13705e 100644
--- a/source/public/ConvertTo-FullErrorView.ps1
+++ b/source/public/ConvertTo-FullErrorView.ps1
@@ -22,13 +22,10 @@ filter ConvertTo-FullErrorView {
 
         $resetColor = "$([char]0x1b)[0m"
         $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
-        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
-        $Detail = $InputObject | Format-List * -Force | Out-String -Width 120
-        $Detail = $Detail -replace "((?:Exception|FullyQualifiedErrorId).*`e\[0m)(.*)", "$($PSStyle.Formatting.ErrorAccent)`$1$($PSStyle.Formatting.Error)`$2$($PSStyle.Reset)"
-    } else {
-        $Detail = $InputObject | Format-List * -Force | Out-String -Width 120
     }
 
+    $Detail = $InputObject | Format-List * -Force | Out-String -Width 120
+
     # NOTE: ErrorViewRecurse is normally false, and only set temporarily by Format-Error -Recurse
     if ($ErrorViewRecurse) {
         $Count = 1
diff --git a/source/public/ConvertTo-NormalErrorView.ps1 b/source/public/ConvertTo-NormalErrorView.ps1
index cdcd820..a33dc72 100644
--- a/source/public/ConvertTo-NormalErrorView.ps1
+++ b/source/public/ConvertTo-NormalErrorView.ps1
@@ -11,17 +11,8 @@ filter ConvertTo-NormalErrorView {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
-    $resetColor = ''
-    $errorColor = ''
-    #$accentColor = ''
 
-    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-        $resetColor = "$([char]0x1b)[0m"
-        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
-        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
-    }
-
-    if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
+    if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
         $errorColor + $InputObject.Exception.Message + $resetColor
     } else {
         $myinv = $InputObject.InvocationInfo
@@ -44,9 +35,9 @@ filter ConvertTo-NormalErrorView {
 
         $errorCategoryMsg = &{ Set-StrictMode -Version 1; $InputObject.ErrorCategory_Message }
         if ($null -ne $errorCategoryMsg) {
-            $indentString = "+ CategoryInfo            : " + $InputObject.ErrorCategory_Message
+            $indentString = $accentColor + "+ CategoryInfo         : " + $resetColor + $InputObject.ErrorCategory_Message
         } else {
-            $indentString = "+ CategoryInfo            : " + $InputObject.CategoryInfo
+            $indentString = $accentColor + "+ CategoryInfo         : " + $resetColor + $InputObject.CategoryInfo
         }
         $posmsg += "`n"
         foreach ($line in @($indentString -split "(.{$width})")) {
@@ -55,7 +46,7 @@ filter ConvertTo-NormalErrorView {
             }
         }
 
-        $indentString = "+ FullyQualifiedErrorId   : " + $InputObject.FullyQualifiedErrorId
+        $indentString = $accentColor + "+ FullyQualifiedErrorId: " + $resetColor + $InputObject.FullyQualifiedErrorId
         $posmsg += "`n"
         foreach ($line in @($indentString -split "(.{$width})")) {
             if ($line) {
@@ -65,7 +56,7 @@ filter ConvertTo-NormalErrorView {
 
         $originInfo = &{ Set-StrictMode -Version 1; $InputObject.OriginInfo }
         if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
-            $indentString = "+ PSComputerName          : " + $originInfo.PSComputerName
+            $indentString = "+ PSComputerName       : " + $originInfo.PSComputerName
             $posmsg += "`n"
             foreach ($line in @($indentString -split "(.{$width})")) {
                 if ($line) {
@@ -75,9 +66,9 @@ filter ConvertTo-NormalErrorView {
         }
 
         if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
-            $errorColor + $InputObject.Exception.Message + $posmsg + $resetColor + "`n "
+            $errorColor + $InputObject.Exception.Message + $resetColor + $posmsg + "`n "
         } else {
-            $errorColor + $InputObject.ErrorDetails.Message + $posmsg + $resetColor
+            $errorColor + $InputObject.ErrorDetails.Message + $resetColor + $posmsg
         }
     }
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalExceptionView.ps1 b/source/public/ConvertTo-NormalExceptionView.ps1
index fea1db0..929b040 100644
--- a/source/public/ConvertTo-NormalExceptionView.ps1
+++ b/source/public/ConvertTo-NormalExceptionView.ps1
@@ -21,6 +21,6 @@ filter ConvertTo-NormalExceptionView {
         #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
     }
 
-    $errorColor + $InputObject.Exception.Message + $resetColor
+    $errorColor + $InputObject.Message + $resetColor
 
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-SimpleErrorView.ps1 b/source/public/ConvertTo-SimpleErrorView.ps1
index 97197ff..c8b836b 100644
--- a/source/public/ConvertTo-SimpleErrorView.ps1
+++ b/source/public/ConvertTo-SimpleErrorView.ps1
@@ -14,28 +14,17 @@ function ConvertTo-SimpleErrorView {
         #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
     }
 
-    if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
+    if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
         $InputObject.Exception.Message
     } else {
         $myinv = $InputObject.InvocationInfo
         if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
             # rip off lines that say "At line:1 char:1" (hopefully, in a language agnostic way)
-            $posmsg  = $myinv.PositionMessage -replace "^At line:1 .*[\r\n]+"
-            # rip off the underline and instead, put >>>markers<<< around the important bit
-            # we could, instead, set the background to a highlight color?
-            $pattern = $posmsg -split "[\r\n]+" -match "\+( +~+)\s*" -replace '(~+)', '($1)' -replace '( +)','($1)' -replace '~| ','.'
-            $posmsg  = $posmsg -replace '[\r\n]+\+ +~+'
-            if ($pattern) {
-                $posmsg  = $posmsg -replace "\+$pattern", '+ $1>>>$2<<<'
-            }
+            $posmsg = "`n" + $myinv.PositionMessage -replace "^At line:1 .*[\r\n]+"
         } else {
             $posmsg = ""
         }
 
-        if ($posmsg -ne "") {
-            $posmsg = "`n" + $posmsg
-        }
-
         if ( & { Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
             $posmsg = " : " + $InputObject.PSMessageDetails + $posmsg
         }
diff --git a/source/public/ConvertTo-YamlErrorView.ps1 b/source/public/ConvertTo-YamlErrorView.ps1
new file mode 100644
index 0000000..704bc2d
--- /dev/null
+++ b/source/public/ConvertTo-YamlErrorView.ps1
@@ -0,0 +1,33 @@
+function ConvertTo-YamlErrorView {
+    <#
+        .SYNOPSIS
+            Creates a description of an ErrorRecord that looks like valid Yaml
+        .DESCRIPTION
+            This produces valid Yaml output from ErrorRecord you pass to it, recursively.
+    #>
+    [CmdletBinding()]
+    param(
+        # The object that you want to convert to YAML
+        [Parameter(Mandatory, ValueFromPipeline)]
+        [System.Management.Automation.ErrorRecord]
+        $InputObject,
+
+        # Optionally, a limit on the depth to recurse properties (defaults to 16)
+        [parameter()]
+        [int]$depth = 16,
+
+        # If set, include empty and null properties in the output
+        [switch]$IncludeEmpty,
+
+        # Recursive use only. Handles indentation for formatting
+        [parameter(DontShow)]
+        [int]$NestingLevel = 0,
+
+        # use OuterXml instead of treating XmlDocuments like objects
+        [parameter(DontShow)]
+        [switch]$XmlAsXml
+    )
+    process {
+        GetYamlRecursive $InputObject
+    }
+}
\ No newline at end of file
diff --git a/source/public/Format-Error.ps1 b/source/public/Format-Error.ps1
index 2e7edf2..6a28a37 100644
--- a/source/public/Format-Error.ps1
+++ b/source/public/Format-Error.ps1
@@ -1,13 +1,13 @@
 function Format-Error {
     <#
         .SYNOPSIS
-            Formats an error for the screen using a specified error view
+            Formats an error (or exception) for the screen using a specified error view
         .DESCRIPTION
             Temporarily switches the error view and outputs the errors
         .EXAMPLE
             Format-Error
 
-            Shows the Normal error view for the most recent error
+            Shows the Detailed error view for the most recent error (changed to be compatible with Get-Error)
         .EXAMPLE
             $error[0..4] | Format-Error Full
 
@@ -17,8 +17,8 @@ function Format-Error {
 
             Shows the full error view of the specific error, recursing into the inner exceptions (if that's supported by the view)
     #>
-    [CmdletBinding(DefaultParameterSetName="Count")]
-    [Alias("fe", "Get-Error")]
+    [CmdletBinding(DefaultParameterSetName = "InputObject")]
+    [Alias("fe"<#, "Get-Error"#>)]
     [OutputType([System.Management.Automation.ErrorRecord])]
     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'The ArgumentCompleter parameters are the required method signature')]
 
@@ -28,7 +28,7 @@ function Format-Error {
         [ArgumentCompleter({
             param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
             [System.Management.Automation.CompletionResult[]]((
-                Get-Command ConvertTo-*ErrorView -ListImported -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord]
+            Get-Command ConvertTo-*ErrorView -ListImported -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord], [System.Exception]
             ).Name -replace "ConvertTo-(.*)ErrorView",'$1' -like "*$($wordToComplete)*")
         })]
         $View = "Detailed",
@@ -47,19 +47,22 @@ function Format-Error {
             }
         ),
 
-        # Allows ErrorView functions to recurse to InnerException
+        # Encourages ErrorView functions to recurse InnerException properties
         [switch]$Recurse
     )
     begin {
         $ErrorActionPreference = "Continue"
-        $ErrorView, $View = $View, $ErrorView
-        [bool]$Recurse, [bool]$ErrorViewRecurse = [bool]$ErrorViewRecurse, $Recurse
+
+        $local:_ErrorView, $global:ErrorView = $global:ErrorView, $View
+        $local:_ErrorViewRecurse, [bool]$global:ErrorViewRecurse = [bool]$global:ErrorViewRecurse, $Recurse
     }
     process {
         $InputObject
     }
     end {
-        [bool]$ErrorViewRecurse = $Recurse
-        $ErrorView = $View
+        $global:ErrorView = $local:_ErrorView
+        if ($null -ne $local:_ErrorViewRecurse) {
+            [bool]$global:ErrorViewRecurse = $local:_ErrorViewRecurse
+        }
     }
 }
diff --git a/source/public/Set-ErrorView.ps1 b/source/public/Set-ErrorView.ps1
index 8041415..340394a 100644
--- a/source/public/Set-ErrorView.ps1
+++ b/source/public/Set-ErrorView.ps1
@@ -27,6 +27,7 @@ filter Set-ErrorView {
     ).Name -replace "ConvertTo-(\w+)ErrorView", '$1View' | Select-Object -Unique
 
     $ofs = ';'
-    .([ScriptBlock]::Create("enum ErrorView { $Names }"))
+    . ([ScriptBlock]::Create("enum ErrorView { $Names }"))
+
     [ErrorView]$global:ErrorView = $View
 }
diff --git a/source/public/Write-NativeCommandError.ps1 b/source/public/Write-NativeCommandError.ps1
index 85d98d1..5ab8889 100644
--- a/source/public/Write-NativeCommandError.ps1
+++ b/source/public/Write-NativeCommandError.ps1
@@ -12,36 +12,40 @@ function Write-NativeCommandError {
         $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
         $accentColor = $PSStyle.Formatting.ErrorAccent
     }
+    if ($InputObject -is [System.Exception]) {
+        $errorColor + $InputObject.GetType().FullName + " : " + $resetColor
+    }
 
-    if ($InputObject.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") { return }
-
-    $invoc = $InputObject.InvocationInfo
-    if ($invoc -and $invoc.MyCommand) {
-        switch -regex ( $invoc.MyCommand.CommandType ) {
-            ([System.Management.Automation.CommandTypes]::ExternalScript) {
-                if ($invoc.MyCommand.Path) {
-                    $accentColor + $invoc.MyCommand.Path + " : " + $resetColor
+    # @('NativeCommandErrorMessage', 'NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView', 'ConciseView', 'DetailedView') -notcontains $ErrorView
+    if (@('NativeCommandErrorMessage', 'NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView', 'ConciseView', 'DetailedView') -notcontains $ErrorView) {
+        $invoc = $InputObject.InvocationInfo
+        if ($invoc -and $invoc.MyCommand) {
+            switch -regex ( $invoc.MyCommand.CommandType ) {
+                ([System.Management.Automation.CommandTypes]::ExternalScript) {
+                    if ($invoc.MyCommand.Path) {
+                        $errorColor + $invoc.MyCommand.Path + " : " + $resetColor
+                    }
+                    break
                 }
-                break
-            }
-            ([System.Management.Automation.CommandTypes]::Script) {
-                if ($invoc.MyCommand.ScriptBlock) {
-                    $accentColor + $invoc.MyCommand.ScriptBlock.ToString() + " : " + $resetColor
+                ([System.Management.Automation.CommandTypes]::Script) {
+                    if ($invoc.MyCommand.ScriptBlock) {
+                        $errorColor + $invoc.MyCommand.ScriptBlock.ToString() + " : " + $resetColor
+                    }
+                    break
                 }
-                break
-            }
-            default {
-                if ($invoc.InvocationName -match '^[&amp;\.]?$') {
-                    if ($invoc.MyCommand.Name) {
-                        $accentColor + $invoc.MyCommand.Name + " : " + $resetColor
+                default {
+                    if ($invoc.InvocationName -match '^[&amp;\.]?$') {
+                        if ($invoc.MyCommand.Name) {
+                            $errorColor + $invoc.MyCommand.Name + " : " + $resetColor
+                        }
+                    } else {
+                        $errorColor + $invoc.InvocationName + " : " + $resetColor
                     }
-                } else {
-                    $accentColor + $invoc.InvocationName + " : " + $resetColor
+                    break
                 }
-                break
             }
+        } elseif ($invoc -and $invoc.InvocationName) {
+            $errorColor + $invoc.InvocationName + " : " + $resetColor
         }
-    } elseif ($invoc -and $invoc.InvocationName) {
-        $accentColor + $invoc.InvocationName + " : " + $resetColor
     }
 }
\ No newline at end of file

From 9ace773e700882ab384603a6a5176cc817bc4425 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Fri, 27 Sep 2024 01:56:10 -0400
Subject: [PATCH 03/15] Start by fixing the views. This is NormalView

---
 Reference/NativeCommandError.ps1              |  56 ++++++
 Reference/default.ps1                         | 113 ++++-------
 Reference/detail copy.ps1                     | 183 ------------------
 Reference/detail.ps1                          |  27 ++-
 Reference/legacy.ps1                          |  57 ++++++
 source/ErrorView.format.ps1xml                |  14 +-
 source/prefix.ps1                             |  26 +--
 source/private/Recolor.ps1                    |  11 ++
 source/private/WrapString.ps1                 |  72 ++-----
 source/public/ConvertTo-NormalErrorView.ps1   |  83 ++++----
 .../public/ConvertTo-NormalExceptionView.ps1  |  14 +-
 source/public/Get-ErrorPrefix.ps1             |  46 +++++
 source/public/Write-NativeCommandError.ps1    |  51 -----
 13 files changed, 294 insertions(+), 459 deletions(-)
 create mode 100644 Reference/NativeCommandError.ps1
 delete mode 100644 Reference/detail copy.ps1
 create mode 100644 Reference/legacy.ps1
 create mode 100644 source/private/Recolor.ps1
 create mode 100644 source/public/Get-ErrorPrefix.ps1
 delete mode 100644 source/public/Write-NativeCommandError.ps1

diff --git a/Reference/NativeCommandError.ps1 b/Reference/NativeCommandError.ps1
new file mode 100644
index 0000000..48f7007
--- /dev/null
+++ b/Reference/NativeCommandError.ps1
@@ -0,0 +1,56 @@
+$errorColor = ''
+$commandPrefix = ''
+if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
+{
+    $myinv = $_.InvocationInfo
+    if ($Host.UI.SupportsVirtualTerminal) {
+        $errorColor = $PSStyle.Formatting.Error
+    }
+
+    $commandPrefix = if ($myinv -and $myinv.MyCommand) {
+        switch -regex ( $myinv.MyCommand.CommandType )
+        {
+            ([System.Management.Automation.CommandTypes]::ExternalScript)
+            {
+                if ($myinv.MyCommand.Path)
+                {
+                    $myinv.MyCommand.Path + ' : '
+                }
+
+                break
+            }
+
+            ([System.Management.Automation.CommandTypes]::Script)
+            {
+                if ($myinv.MyCommand.ScriptBlock)
+                {
+                    $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+                }
+
+                break
+            }
+            default
+            {
+                if ($myinv.InvocationName -match '^[&amp;\.]?$')
+                {
+                    if ($myinv.MyCommand.Name)
+                    {
+                        $myinv.MyCommand.Name + ' : '
+                    }
+                }
+                else
+                {
+                    $myinv.InvocationName + ' : '
+                }
+
+                break
+            }
+        }
+    }
+    elseif ($myinv -and $myinv.InvocationName)
+    {
+        $myinv.InvocationName + ' : '
+    }
+}
+
+$errorColor + $commandPrefix
\ No newline at end of file
diff --git a/Reference/default.ps1 b/Reference/default.ps1
index d4f6ac8..6108b80 100644
--- a/Reference/default.ps1
+++ b/Reference/default.ps1
@@ -1,55 +1,3 @@
-<#
-if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
-{
-    $myinv = $_.InvocationInfo
-    if ($myinv -and $myinv.MyCommand)
-    {
-        switch -regex ( $myinv.MyCommand.CommandType )
-        {
-            ([System.Management.Automation.CommandTypes]::ExternalScript)
-            {
-                if ($myinv.MyCommand.Path)
-                {
-                    $myinv.MyCommand.Path + ' : '
-                }
-
-                break
-            }
-
-            ([System.Management.Automation.CommandTypes]::Script)
-            {
-                if ($myinv.MyCommand.ScriptBlock)
-                {
-                    $myinv.MyCommand.ScriptBlock.ToString() + ' : '
-                }
-
-                break
-            }
-            default
-            {
-                if ($myinv.InvocationName -match '^[&\.]?$')
-                {
-                    if ($myinv.MyCommand.Name)
-                    {
-                        $myinv.MyCommand.Name + ' : '
-                    }
-                }
-                else
-                {
-                    $myinv.InvocationName + ' : '
-                }
-
-                break
-            }
-        }
-    }
-    elseif ($myinv -and $myinv.InvocationName)
-    {
-        $myinv.InvocationName + ' : '
-    }
-}
-
-
 Set-StrictMode -Off
 $ErrorActionPreference = 'Stop'
 trap { 'Error found in error view definition: ' + $_.Exception.Message }
@@ -64,6 +12,7 @@ if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__Suppre
     $errorColor = $PSStyle.Formatting.Error
     $accentColor = $PSStyle.Formatting.ErrorAccent
 }
+
 function Get-ConciseViewPositionMessage {
 
     # returns a string cut to last whitespace
@@ -82,17 +31,30 @@ function Get-ConciseViewPositionMessage {
     $message = ''
     $prefix = ''
 
+    # Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
+    # Note that in some versions, this is a Dictionary&lt;,&gt; and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
+    # to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
+    $useTargetObject = $null -ne $err.TargetObject -and
+        $err.TargetObject -is [System.Collections.IDictionary] -and
+        ([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
+        ([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
+
     # The checks here determine if we show line detailed error information:
     # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+    $isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
+        $err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
+
     # - check if invocation is a script or multiple lines in the console
+    $isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
+
     # - check that it's not a script module as expectation is that users don't want to see the line of error within a module
-    if ((($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception -is 'System.Management.Automation.ParentContainsErrorRecordException') -or $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1) -and $myinv.ScriptName -notmatch '\.psm1$') {
-        $useTargetObject = $false
+    $shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
+        $myinv.ScriptName -notmatch '\.psm1$'
+
+    if ($useTargetObject -or $shouldShowLineDetail) {
 
-        # Handle case where there is a TargetObject and we can show the error at the target rather than the script source
-        if ($_.TargetObject.Line -and $_.TargetObject.LineText) {
-            $posmsg = "${resetcolor}$($_.TargetObject.File)${newline}"
-            $useTargetObject = $true
+        if ($useTargetObject) {
+            $posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
         }
         elseif ($myinv.ScriptName) {
             if ($env:TERM_PROGRAM -eq 'vscode') {
@@ -108,8 +70,8 @@ function Get-ConciseViewPositionMessage {
         }
 
         if ($useTargetObject) {
-            $scriptLineNumber = $_.TargetObject.Line
-            $scriptLineNumberLength = $_.TargetObject.Line.ToString().Length
+            $scriptLineNumber = $err.TargetObject.Line
+            $scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
         }
         else {
             $scriptLineNumber = $myinv.ScriptLineNumber
@@ -181,7 +143,7 @@ function Get-ConciseViewPositionMessage {
 
     # if rendering line information, break up the message if it's wider than the console
     if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
-        $prefixLength = "$([char]27)]8;;{0}`a{1}$([char]27)]8;;`a" -f $pwd, $pwd::new($prefix).ContentLength
+        $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
         $prefixVtLength = $prefix.Length - $prefixLength
 
         # replace newlines in message so it lines up correct
@@ -232,8 +194,8 @@ function Get-ConciseViewPositionMessage {
         $reason = $myinv.MyCommand
     }
     # If it's a scriptblock, better to show the command in the scriptblock that had the error
-    elseif ($_.CategoryInfo.Activity) {
-        $reason = $_.CategoryInfo.Activity
+    elseif ($err.CategoryInfo.Activity) {
+        $reason = $err.CategoryInfo.Activity
     }
     elseif ($myinv.MyCommand) {
         $reason = $myinv.MyCommand
@@ -264,9 +226,8 @@ if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQua
     return "${errorColor}$($err.Exception.Message)${resetcolor}"
 }
 
-$myinv = $err.InvocationInfo
 if ($ErrorView -eq 'DetailedView') {
-    $message = Get-Error
+    $message = Get-Error | Out-String
     return "${errorColor}${message}${resetcolor}"
 }
 
@@ -281,10 +242,9 @@ if ($ErrorView -eq 'ConciseView') {
 }
 elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) {
     $posmsg = $myinv.PositionMessage
-}
-
-if ($posmsg -ne '') {
-    $posmsg = $newline + $posmsg
+    if ($posmsg -ne '') {
+        $posmsg = $newline + $posmsg
+    }
 }
 
 if ($err.PSMessageDetails) {
@@ -292,10 +252,19 @@ if ($err.PSMessageDetails) {
 }
 
 if ($ErrorView -eq 'ConciseView') {
+    $recommendedAction = $_.ErrorDetails.RecommendedAction
+    if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
+        $recommendedAction = $newline +
+            ${errorColor} +
+            '  Recommendation: ' +
+            $recommendedAction +
+            ${resetcolor}
+    }
+
     if ($err.PSMessageDetails) {
         $posmsg = "${errorColor}${posmsg}"
     }
-    return $posmsg
+    return $posmsg + $recommendedAction
 }
 
 $indent = 4
@@ -330,6 +299,4 @@ $finalMsg = if ($err.ErrorDetails.Message) {
     $err.Exception.Message + $posmsg
 }
 
-"${errorColor}${finalMsg}${resetcolor}"
-
-#>
\ No newline at end of file
+"${errorColor}${finalMsg}${resetcolor}"
\ No newline at end of file
diff --git a/Reference/detail copy.ps1 b/Reference/detail copy.ps1
deleted file mode 100644
index 79e9c36..0000000
--- a/Reference/detail copy.ps1	
+++ /dev/null
@@ -1,183 +0,0 @@
-<#
-[int]$maxDepth = 10
-Set-StrictMode -Off
-
-$ellipsis = "`u{2026}"
-$resetColor = ''
-$errorColor = ''
-$accentColor = ''
-
-if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-    $resetColor = $PSStyle.Reset
-    $errorColor = $psstyle.Formatting.Error
-    $accentColor = $PSStyle.Formatting.FormatAccent
-}
-
-function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
-    $newline = [Environment]::Newline
-    $output = [System.Text.StringBuilder]::new()
-    $prefix = ' ' * $indent
-
-    $expandTypes = @(
-        'Microsoft.Rest.HttpRequestMessageWrapper'
-        'Microsoft.Rest.HttpResponseMessageWrapper'
-        'System.Management.Automation.InvocationInfo'
-    )
-
-    # if object is an Exception, add an ExceptionType property
-    if ($obj -is [Exception]) {
-        $obj | Add-Member -NotePropertyName Type -NotePropertyValue $obj.GetType().FullName -ErrorAction Ignore
-    }
-
-    # first find the longest property so we can indent properly
-    $propLength = 0
-    foreach ($prop in $obj.PSObject.Properties) {
-        if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
-            $propLength = $prop.Name.Length
-        }
-    }
-
-    $addedProperty = $false
-    foreach ($prop in $obj.PSObject.Properties) {
-
-        # don't show empty properties or our added property for $error[index]
-        if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
-            $addedProperty = $true
-            $null = $output.Append($prefix)
-            $null = $output.Append($accentColor)
-            $null = $output.Append($prop.Name)
-            $propNameIndent = ' ' * ($propLength - $prop.Name.Length)
-            $null = $output.Append($propNameIndent)
-            $null = $output.Append(' : ')
-            $null = $output.Append($resetColor)
-
-            $newIndent = $indent + 4
-
-            # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
-            if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
-                $expandTypes -contains $prop.TypeNameOfValue -or ($prop.TypeNames -ne $null -and $expandTypes -contains $prop.TypeNames[0])) {
-
-                if ($depth -ge $maxDepth) {
-                    $null = $output.Append($ellipsis)
-                }
-                else {
-                    $null = $output.Append($newline)
-                    $null = $output.Append((Show-ErrorRecord $prop.Value $newIndent ($depth + 1)))
-                }
-            }
-            # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
-            elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
-                if ($depth -ge $maxDepth) {
-                    $null = $output.Append($ellipsis)
-                }
-                else {
-                    $targetSite = [PSCustomObject]@{
-                        Name = $prop.Value.Name
-                        DeclaringType = $prop.Value.DeclaringType
-                        MemberType = $prop.Value.MemberType
-                        Module = $prop.Value.Module
-                    }
-
-                    $null = $output.Append($newline)
-                    $null = $output.Append((Show-ErrorRecord $targetSite $newIndent ($depth + 1)))
-                }
-            }
-            # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
-            elseif ($prop.Name -eq 'StackTrace') {
-                # for a stacktrace which is usually quite wide with info, we left justify it
-                $null = $output.Append($newline)
-                $null = $output.Append($prop.Value)
-            }
-            # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
-            elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
-                $isFirstElement = $true
-                foreach ($key in $prop.Value.Keys) {
-                    if ($isFirstElement) {
-                        $null = $output.Append($newline)
-                    }
-
-                    if ($key -eq 'Authorization') {
-                        $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
-                    }
-                    else {
-                        $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
-                    }
-
-                    $isFirstElement = $false
-                }
-            }
-            # if the object implements IEnumerable and not a string, we try to show each object
-            # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
-            elseif (!($prop.Value -is [System.String]) -and $prop.Value.GetType().GetInterface('IEnumerable') -ne $null -and $prop.Name -ne 'Data') {
-
-                if ($depth -ge $maxDepth) {
-                    $null = $output.Append($ellipsis)
-                }
-                else {
-                    $isFirstElement = $true
-                    foreach ($value in $prop.Value) {
-                        $null = $output.Append($newline)
-                        if (!$isFirstElement) {
-                            $null = $output.Append($newline)
-                        }
-                        $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
-                        $isFirstElement = $false
-                    }
-                }
-            }
-            # Anything else, we convert to string.
-            # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
-            else {
-                $value = $null
-                if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $value -ne $null)
-                {
-                    if ($prop.Name -eq 'PositionMessage') {
-                        $value = $value.Insert($value.IndexOf('~'), $errorColor)
-                    }
-                    elseif ($prop.Name -eq 'Message') {
-                        $value = $errorColor + $value
-                    }
-
-                    $isFirstLine = $true
-                    if ($value.Contains($newline)) {
-                        # the 3 is to account for ' : '
-                        $valueIndent = ' ' * ($propLength + 3)
-                        # need to trim any extra whitespace already in the text
-                        foreach ($line in $value.Split($newline)) {
-                            if (!$isFirstLine) {
-                                $null = $output.Append("${newline}${prefix}${valueIndent}")
-                            }
-                            $null = $output.Append($line.Trim())
-                            $isFirstLine = $false
-                        }
-                    }
-                    else {
-                        $null = $output.Append($value)
-                    }
-                }
-            }
-
-            $null = $output.Append($newline)
-        }
-    }
-
-    # if we had added nested properties, we need to remove the last newline
-    if ($addedProperty) {
-        $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
-    }
-
-    $output.ToString()
-}
-
-# Add back original typename and remove PSExtendedError
-if ($_.PSObject.TypeNames.Contains('System.Management.Automation.ErrorRecord#PSExtendedError')) {
-    $_.PSObject.TypeNames.Add('System.Management.Automation.ErrorRecord')
-    $null = $_.PSObject.TypeNames.Remove('System.Management.Automation.ErrorRecord#PSExtendedError')
-}
-elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
-    $_.PSObject.TypeNames.Add('System.Exception')
-    $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
-}
-
-Show-ErrorRecord $_
-#>
\ No newline at end of file
diff --git a/Reference/detail.ps1 b/Reference/detail.ps1
index 79e9c36..f4f1ac8 100644
--- a/Reference/detail.ps1
+++ b/Reference/detail.ps1
@@ -1,7 +1,6 @@
-<#
-[int]$maxDepth = 10
 Set-StrictMode -Off
 
+$maxDepth = 10
 $ellipsis = "`u{2026}"
 $resetColor = ''
 $errorColor = ''
@@ -117,14 +116,29 @@ function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
                     $isFirstElement = $true
                     foreach ($value in $prop.Value) {
                         $null = $output.Append($newline)
-                        if (!$isFirstElement) {
-                            $null = $output.Append($newline)
+                        $valueIndent = ' ' * ($newIndent + 2)
+
+                        if ($value -is [Type]) {
+                            # Just show the typename instead of it as an object
+                            $null = $output.Append("${prefix}${valueIndent}[$($value.ToString())]")
+                        }
+                        elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
+                            $null = $output.Append("${prefix}${valueIndent}${value}")
+                        }
+                        else {
+                            if (!$isFirstElement) {
+                                $null = $output.Append($newline)
+                            }
+                            $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
                         }
-                        $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
                         $isFirstElement = $false
                     }
                 }
             }
+            elseif ($prop.Value -is [Type]) {
+                # Just show the typename instead of it as an object
+                $null = $output.Append("[$($prop.Value.ToString())]")
+            }
             # Anything else, we convert to string.
             # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
             else {
@@ -179,5 +193,4 @@ elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
     $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
 }
 
-Show-ErrorRecord $_
-#>
\ No newline at end of file
+Show-ErrorRecord $_
\ No newline at end of file
diff --git a/Reference/legacy.ps1 b/Reference/legacy.ps1
new file mode 100644
index 0000000..8ab5ee2
--- /dev/null
+++ b/Reference/legacy.ps1
@@ -0,0 +1,57 @@
+if ($_.FullyQualifiedErrorId -eq "NativeCommandErrorMessage") {
+    $_.Exception.Message
+}
+else
+{
+    $myinv = $_.InvocationInfo
+    if ($myinv -and ($myinv.MyCommand -or ($_.CategoryInfo.Category -ne 'ParserError'))) {
+        $posmsg = $myinv.PositionMessage
+    } else {
+        $posmsg = ""
+    }
+
+    if ($posmsg -ne "")
+    {
+        $posmsg = "`n" + $posmsg
+    }
+
+    if ( & { Set-StrictMode -Version 1; $_.PSMessageDetails } ) {
+        $posmsg = " : " +  $_.PSMessageDetails + $posmsg
+    }
+
+    $indent = 4
+    $width = $host.UI.RawUI.BufferSize.Width - $indent - 2
+
+    $errorCategoryMsg = &amp; { Set-StrictMode -Version 1; $_.ErrorCategory_Message }
+    if ($errorCategoryMsg -ne $null)
+    {
+        $indentString = "+ CategoryInfo          : " + $_.ErrorCategory_Message
+    }
+    else
+    {
+        $indentString = "+ CategoryInfo          : " + $_.CategoryInfo
+    }
+    $posmsg += "`n"
+    foreach($line in @($indentString -split "(.{$width})")) { if($line) { $posmsg += (" " * $indent + $line) } }
+
+    $indentString = "+ FullyQualifiedErrorId : " + $_.FullyQualifiedErrorId
+    $posmsg += "`n"
+    foreach($line in @($indentString -split "(.{$width})")) { if($line) { $posmsg += (" " * $indent + $line) } }
+
+    $originInfo = &amp; { Set-StrictMode -Version 1; $_.OriginInfo }
+    if (($originInfo -ne $null) -and ($originInfo.PSComputerName -ne $null))
+    {
+        $indentString = "+ PSComputerName        : " + $originInfo.PSComputerName
+        $posmsg += "`n"
+        foreach($line in @($indentString -split "(.{$width})")) { if($line) { $posmsg += (" " * $indent + $line) } }
+    }
+
+    if ($ErrorView -eq "CategoryView") {
+        $_.CategoryInfo.GetMessage()
+    }
+    elseif (! $_.ErrorDetails -or ! $_.ErrorDetails.Message) {
+        $_.Exception.Message + $posmsg + "`n "
+    } else {
+        $_.ErrorDetails.Message + $posmsg
+    }
+}
\ No newline at end of file
diff --git a/source/ErrorView.format.ps1xml b/source/ErrorView.format.ps1xml
index 028b7b5..582f7f0 100644
--- a/source/ErrorView.format.ps1xml
+++ b/source/ErrorView.format.ps1xml
@@ -11,12 +11,13 @@
         <CustomEntries>
           <CustomEntry>
             <CustomItem>
-              <ExpressionBinding>
-                <ScriptBlock>Write-NativeCommandError $_</ScriptBlock>
-              </ExpressionBinding>
               <ExpressionBinding>
                 <ScriptBlock>
                   <![CDATA[
+                    Set-StrictMode -Off
+                    $ErrorActionPreference = 'Stop'
+                    trap { 'Error found in error view definition: ' + $_.Exception.Message }
+
                     if ($formatter = @(Get-Command "ConvertTo-$($ErrorView -replace "View$")ErrorView" -ListImported -ErrorAction Ignore -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord])) {
                       & ($formatter[0]) -InputObject $_
                     } else {
@@ -40,12 +41,13 @@
         <CustomEntries>
           <CustomEntry>
             <CustomItem>
-              <ExpressionBinding>
-                <ScriptBlock>Write-NativeCommandError $_</ScriptBlock>
-              </ExpressionBinding>
               <ExpressionBinding>
                 <ScriptBlock>
                   <![CDATA[
+                    Set-StrictMode -Off
+                    $ErrorActionPreference = 'Stop'
+                    trap { 'Error found in error view definition: ' + $_.Exception.Message }
+
                     if ($_.ErrorRecord) {
                       $Record = $_.ErrorRecord
                       if ($formatter = @(Get-Command "ConvertTo-$($ErrorView -replace "View$")ErrorView" -ListImported -ErrorAction Ignore -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord])) {
diff --git a/source/prefix.ps1 b/source/prefix.ps1
index aef18c4..5ab3ea2 100644
--- a/source/prefix.ps1
+++ b/source/prefix.ps1
@@ -5,10 +5,12 @@ param(
 )
 
 # We need to _overwrite_ the ErrorView
-# So -PrependPath, instead of FormatsToProcess
+# So -PrependPath, instead of using FormatsToProcess
 Update-FormatData -PrependPath $PSScriptRoot\ErrorView.format.ps1xml
 
 Set-StrictMode -Off
+$ErrorActionPreference = 'Stop'
+trap { 'Error found in error view definition: ' + $_.Exception.Message }
 
 # Borrowed this one from https://github.com/chalk/ansi-regex
 $script:AnsiEscapes = [Regex]::new("([\u001b\u009b][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?(?:\u001b\u005c|\u0007))|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~])))", "Compiled");
@@ -23,31 +25,9 @@ $script:AnsiEscapes = [Regex]::new("([\u001b\u009b][[\]()#;?]*(?:(?:(?:(?:;[-a-z
 # ESC O S
 # $script:AnsiEscapes = [Regex]::new("\x1b[\(\)%`"&\.\/*+.-][@-Z]|\x1b\].*?(?:\u001b\u005c|\u0007|^)|\x1b\[\P{L}*[@-_A-Za-z^`\{\|\}~]|\x1b#\d|\x1b[!-~]", [System.Text.RegularExpressions.RegexOptions]::Compiled);
 
-
-
-
-
 $script:ellipsis = [char]0x2026
 $script:newline = [Environment]::Newline
-$script:resetColor = ''
-$script:errorColor = ''
-$script:accentColor = ''
-$script:errorAccentColor = ''
 $script:LineColors = @(
     "`e[38;2;255;255;255m"
     "`e[38;2;179;179;179m"
 )
-
-if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-    if ($PSStyle) {
-        $script:resetColor = $PSStyle.Reset
-        $script:errorColor = $PSStyle.Formatting.Error
-        $script:accentColor = $PSStyle.Formatting.FormatAccent
-        $script:errorAccentColor = $PSStyle.Formatting.ErrorAccent
-    } else {
-        $script:resetColor = "$([char]27)[0m"
-        $script:errorColor = "$([char]27)[31m"
-        $script:accentColor = "$([char]27)[32;1m"
-        $script:errorAccentColor = "$([char]27)[31;1m"
-    }
-}
\ No newline at end of file
diff --git a/source/private/Recolor.ps1 b/source/private/Recolor.ps1
new file mode 100644
index 0000000..4a831a5
--- /dev/null
+++ b/source/private/Recolor.ps1
@@ -0,0 +1,11 @@
+function ResetColor {
+    $script:resetColor = ''
+    $script:errorColor = ''
+    $script:accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $script:resetColor = "$([char]27)[0m"
+        $script:errorColor = if ($null -ne $PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
+        $script:accentColor = if ($null -ne $PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+    }
+}
\ No newline at end of file
diff --git a/source/private/WrapString.ps1 b/source/private/WrapString.ps1
index 4d3fbfa..26c2f52 100644
--- a/source/private/WrapString.ps1
+++ b/source/private/WrapString.ps1
@@ -36,84 +36,44 @@ filter WrapString {
         # $wrappableChars = [char[]]" ,.?!:;-`n`r`t"
         # $maxLength = $width - $IndentPadding.Length -1
         $wrapper = [Regex]::new("((?:$AnsiPattern)*[^-=,.?!:;\s\r\n\t\\\/\|]+(?:$AnsiPattern)*)", "Compiled")
-        $output = [System.Text.StringBuilder]::new($Colors[$color] + $IndentPadding)
+        $output = [System.Text.StringBuilder]::new()
+        $buffer = [System.Text.StringBuilder]::new($Colors[$color])
+        $lineLength = 0
+        if ($Width -lt $IndentPadding.Length) {
+            Write-Warning "Width $Width is less than IndentPadding length $($IndentPadding.Length). Setting Width to BufferWidth ($($Host.UI.RawUI.BufferSize.Width))"
+        }
     }
     process {
         foreach($line in $InputObject -split "(\r?\n)") {
             # Don't bother trying to split empty lines
             if ([String]::IsNullOrWhiteSpace($AnsiRegex.Replace($line, ''))) {
                 Write-Debug "Empty String ($($line.Length))"
-                if ($EmphasizeOriginalNewlines) { [string]::Empty }
+                if ($EmphasizeOriginalNewlines) {
+                    $null = $output.Append($newline)
+                }
                 continue
             }
 
             $slices = $line -split $wrapper | ForEach-Object { @{ Text = $_; Length = MeasureString $_ } }
             Write-Debug "$($line.Length) words in line. $($AnsiRegex.Replace($line, ''))"
-            $lineLength = $IndentPadding.Length
             foreach($slice in $slices) {
                 $lineLength += $slice.Length
                 if ($lineLength -le $Width) {
                     Write-Verbose "+ $($slice.Length) = $lineLength < $Width"
-                    $null = $output.Append($slice.Text)
+                    $null = $buffer.Append($slice.Text)
                 } else {
                     Write-Verbose "Output $($lineLength - $slice.Length)"
                     Write-Verbose "+ $($slice.Length) = $($slice.Length)"
                     $color = ($color + 1) % $Colors.Length
-                    $output.ToString().Trim()
-                    $null = $output.Clear().Append($Colors[$color]).Append($IndentPadding).Append($slice.Text)
+                    #$null = $output.Append($buffer.ToString())
+                    $null = $buffer.Append($newline).Append($slice.Text)
                     $lineLength = $IndentPadding.Length + $slice.Length
                 }
             }
-            $output.ToString().Trim()
-            $null = $output.Clear().Append($Colors[$color]).Append($IndentPadding)
+            $null = $output.Append($buffer.ToString())
+            $null = $buffer.Clear().Append($newline).Append($Colors[$color]).Append($IndentPadding)
+            $lineLength = $IndentPadding.Length
         }
-
-            # $currentIndex = 0;
-            # $lastWrap = 0;
-            # do {
-            #     $lastWrap = $currentIndex;
-            #     #Write-Verbose "m: $Width, l: $($line.Length), c: $color $($Colors[$color] -replace "`e","``e")"
-
-
-
-            #     if ($AnsiEscapes.Replace($first, '').Length -gt $maxLength + 1) {
-            #         # If we couldn't find a good place to wrap, just wrap at the end of the line
-            #         $first = $line.Substring(0, $maxLength+1)
-            #         $line = $line.Substring($maxLength+1)
-            #     }
-            #     $Colors[$color] + $IndentPadding + $first.TrimEnd()
-
-<#                 $currentIndex = $(
-                    if ($lastWrap + $Width -gt $line.Length) {
-                        $line.Length
-                    } else {
-                        $line.LastIndexOfAny($wrappableChars, ([Math]::Min(($line.Length - 1), ($lastWrap + $Width)))) + 1
-                    }
-                )
-
-                $slice = $line.Substring($lastWrap, ($currentIndex - $lastWrap))
-                if (($slice.Length - $script:AnsiEscapes.Replace($slice,'').Length) -gt 0) {
-                    $currentIndex = $(
-                        if ($lastWrap + $Width -gt $line.Length) {
-                            $line.Length
-                        } else {
-                            $line.LastIndexOfAny($wrappableChars, ([Math]::Min(($line.Length - 1), ($lastWrap + $Width)))) + 1
-                        }
-                    )
-                }
-
-                # If we couldn't find a good place to wrap, just wrap at the end of the line
-                if ($currentIndex -le $lastWrap) {
-                    $currentIndex = [Math]::Min(($lastWrap + $Width), $line.Length )
-                }
-                # Output the line, with the appropriate color and padding
-                $Colors[$color] + $IndentPadding + $line.Substring($lastWrap, ($currentIndex - $lastWrap)).TrimEnd()
-#>
-<#             } while($line);
-
-            if ($line -ne "`n") {
-                $color = ($color + 1) % $Colors.Length
-            }
-        } #>
+        $output.ToString()
     }
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalErrorView.ps1 b/source/public/ConvertTo-NormalErrorView.ps1
index a33dc72..70a6da5 100644
--- a/source/public/ConvertTo-NormalErrorView.ps1
+++ b/source/public/ConvertTo-NormalErrorView.ps1
@@ -11,64 +11,49 @@ filter ConvertTo-NormalErrorView {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
-
-    if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
-        $errorColor + $InputObject.Exception.Message + $resetColor
-    } else {
-        $myinv = $InputObject.InvocationInfo
-        if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
-            $posmsg = $myinv.PositionMessage
+    begin { ResetColor }
+    process {
+        if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
+            "${errorColor}$($InputObject.Exception.Message)${resetColor}"
         } else {
-            $posmsg = ""
-        }
-
-        if ($posmsg -ne "") {
-            $posmsg = "`n" + $posmsg
-        }
-
-        if ( &{ Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
-            $posmsg = " : " +  $InputObject.PSMessageDetails + $posmsg
-        }
+            $myinv = $InputObject.InvocationInfo
+            $posmsg = ''
+            if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError')) -and $myinv.PositionMessage) {
+                $posmsg = $newline + $myinv.PositionMessage
+            }
 
-        $indent = 4
-        $width = $host.UI.RawUI.BufferSize.Width - $indent - 2
+            if ($err.PSMessageDetails) {
+                $posmsg = ' : ' + $err.PSMessageDetails + $posmsg
+            }
 
-        $errorCategoryMsg = &{ Set-StrictMode -Version 1; $InputObject.ErrorCategory_Message }
-        if ($null -ne $errorCategoryMsg) {
-            $indentString = $accentColor + "+ CategoryInfo         : " + $resetColor + $InputObject.ErrorCategory_Message
-        } else {
-            $indentString = $accentColor + "+ CategoryInfo         : " + $resetColor + $InputObject.CategoryInfo
-        }
-        $posmsg += "`n"
-        foreach ($line in @($indentString -split "(.{$width})")) {
-            if ($line) {
-                $posmsg += (" " * $indent + $line)
+            $Wrap = @{
+                Width = $width
+                IndentPadding = "                         "
             }
-        }
+            $width = $host.UI.RawUI.BufferSize.Width - 2
 
-        $indentString = $accentColor + "+ FullyQualifiedErrorId: " + $resetColor + $InputObject.FullyQualifiedErrorId
-        $posmsg += "`n"
-        foreach ($line in @($indentString -split "(.{$width})")) {
-            if ($line) {
-                $posmsg += (" " * $indent + $line)
+            $errorCategoryMsg = $InputObject.ErrorCategory_Message
+            [string]$line = if ($null -ne $errorCategoryMsg) {
+                $accentColor + "+ CategoryInfo         : " + $errorColor + $InputObject.ErrorCategory_Message | WrapString @Wrap
+            } else {
+                $accentColor + "+ CategoryInfo         : " + $errorColor + $InputObject.CategoryInfo | WrapString @Wrap
             }
-        }
+            $posmsg += $newline + $line
 
-        $originInfo = &{ Set-StrictMode -Version 1; $InputObject.OriginInfo }
-        if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
-            $indentString = "+ PSComputerName       : " + $originInfo.PSComputerName
-            $posmsg += "`n"
-            foreach ($line in @($indentString -split "(.{$width})")) {
-                if ($line) {
-                    $posmsg += (" " * $indent + $line)
-                }
+            $line = $accentColor + "+ FullyQualifiedErrorId: " + $errorColor + $InputObject.FullyQualifiedErrorId | WrapString @Wrap
+            $posmsg += $newline + $line
+
+            $originInfo = $InputObject.OriginInfo
+            if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
+                $line = $accentColor + "+ PSComputerName       : " + $errorColor + $originInfo.PSComputerName | WrapString @Wrap
+                $posmsg += $newline + $line
             }
-        }
 
-        if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
-            $errorColor + $InputObject.Exception.Message + $resetColor + $posmsg + "`n "
-        } else {
-            $errorColor + $InputObject.ErrorDetails.Message + $resetColor + $posmsg
+            if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
+                $errorColor + $InputObject.Exception.Message + $posmsg + $resetColor
+            } else {
+                $errorColor + $InputObject.ErrorDetails.Message + $posmsg + $resetColor
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-NormalExceptionView.ps1 b/source/public/ConvertTo-NormalExceptionView.ps1
index 929b040..31de80f 100644
--- a/source/public/ConvertTo-NormalExceptionView.ps1
+++ b/source/public/ConvertTo-NormalExceptionView.ps1
@@ -11,16 +11,8 @@ filter ConvertTo-NormalExceptionView {
         [System.Exception]
         $InputObject
     )
-    $resetColor = ''
-    $errorColor = ''
-    #$accentColor = ''
-
-    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-        $resetColor = "$([char]0x1b)[0m"
-        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
-        #$accentColor = if ($PSStyle.Formatting.ErrorAccent) { $PSStyle.Formatting.ErrorAccent } else { "`e[1;36m" }
+    begin { ResetColor }
+    process {
+        $errorColor + $InputObject.Message + $resetColor
     }
-
-    $errorColor + $InputObject.Message + $resetColor
-
 }
\ No newline at end of file
diff --git a/source/public/Get-ErrorPrefix.ps1 b/source/public/Get-ErrorPrefix.ps1
new file mode 100644
index 0000000..cce9461
--- /dev/null
+++ b/source/public/Get-ErrorPrefix.ps1
@@ -0,0 +1,46 @@
+filter Get-ErrorPrefix {
+    [CmdletBinding()]
+    param(
+        [Parameter(ValueFromPipeline)]
+        $InputObject
+    )
+    if (@('NativeCommandErrorMessage', 'NativeCommandError') -notcontains $_.FullyQualifiedErrorId) {
+        if ($InputObject -is [System.Exception]) {
+            $InputObject.GetType().FullName + " : "
+        } else {
+            $myinv = $InputObject.InvocationInfo
+            if ($myinv -and $myinv.MyCommand) {
+                switch -regex ( $myinv.MyCommand.CommandType ) {
+                    ([System.Management.Automation.CommandTypes]::ExternalScript) {
+                        if ($myinv.MyCommand.Path) {
+                            $myinv.MyCommand.Path + ' : '
+                        }
+
+                        break
+                    }
+
+                    ([System.Management.Automation.CommandTypes]::Script) {
+                        if ($myinv.MyCommand.ScriptBlock) {
+                            $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+                        }
+
+                        break
+                    }
+                    default {
+                        if ($myinv.InvocationName -match '^[&amp;\.]?$') {
+                            if ($myinv.MyCommand.Name) {
+                                $myinv.MyCommand.Name + ' : '
+                            }
+                        } else {
+                            $myinv.InvocationName + ' : '
+                        }
+
+                        break
+                    }
+                }
+            } elseif ($myinv -and $myinv.InvocationName) {
+                $myinv.InvocationName + ' : '
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/source/public/Write-NativeCommandError.ps1 b/source/public/Write-NativeCommandError.ps1
deleted file mode 100644
index 5ab8889..0000000
--- a/source/public/Write-NativeCommandError.ps1
+++ /dev/null
@@ -1,51 +0,0 @@
-function Write-NativeCommandError {
-    [CmdletBinding()]
-    param(
-        $InputObject
-    )
-    $resetColor = ''
-    $errorColor = ''
-    $accentColor = ''
-
-    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
-        $resetColor = "$([char]0x1b)[0m"
-        $errorColor = if ($PSStyle.Formatting.Error) { $PSStyle.Formatting.Error } else { "`e[1;31m" }
-        $accentColor = $PSStyle.Formatting.ErrorAccent
-    }
-    if ($InputObject -is [System.Exception]) {
-        $errorColor + $InputObject.GetType().FullName + " : " + $resetColor
-    }
-
-    # @('NativeCommandErrorMessage', 'NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView', 'ConciseView', 'DetailedView') -notcontains $ErrorView
-    if (@('NativeCommandErrorMessage', 'NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView', 'ConciseView', 'DetailedView') -notcontains $ErrorView) {
-        $invoc = $InputObject.InvocationInfo
-        if ($invoc -and $invoc.MyCommand) {
-            switch -regex ( $invoc.MyCommand.CommandType ) {
-                ([System.Management.Automation.CommandTypes]::ExternalScript) {
-                    if ($invoc.MyCommand.Path) {
-                        $errorColor + $invoc.MyCommand.Path + " : " + $resetColor
-                    }
-                    break
-                }
-                ([System.Management.Automation.CommandTypes]::Script) {
-                    if ($invoc.MyCommand.ScriptBlock) {
-                        $errorColor + $invoc.MyCommand.ScriptBlock.ToString() + " : " + $resetColor
-                    }
-                    break
-                }
-                default {
-                    if ($invoc.InvocationName -match '^[&amp;\.]?$') {
-                        if ($invoc.MyCommand.Name) {
-                            $errorColor + $invoc.MyCommand.Name + " : " + $resetColor
-                        }
-                    } else {
-                        $errorColor + $invoc.InvocationName + " : " + $resetColor
-                    }
-                    break
-                }
-            }
-        } elseif ($invoc -and $invoc.InvocationName) {
-            $errorColor + $invoc.InvocationName + " : " + $resetColor
-        }
-    }
-}
\ No newline at end of file

From f9abe85d43fc8525161ab81d006e0180ee42bbfd Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Fri, 27 Sep 2024 22:52:55 -0400
Subject: [PATCH 04/15] This is ConciseView

---
 .../private/GetConciseViewPositionMessage.ps1 | 176 ++++++++++++++++++
 source/public/ConvertTo-ConciseErrorView.ps1  |  64 ++-----
 2 files changed, 193 insertions(+), 47 deletions(-)
 create mode 100644 source/private/GetConciseViewPositionMessage.ps1

diff --git a/source/private/GetConciseViewPositionMessage.ps1 b/source/private/GetConciseViewPositionMessage.ps1
new file mode 100644
index 0000000..7acdaac
--- /dev/null
+++ b/source/private/GetConciseViewPositionMessage.ps1
@@ -0,0 +1,176 @@
+filter GetConciseViewPositionMessage {
+    [CmdletBinding()]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [System.Management.Automation.ErrorRecord]
+        $InputObject
+    )
+    $err = $InputObject
+    $posmsg = ''
+    $headerWhitespace = ''
+    $offsetWhitespace = ''
+    $message = ''
+    $prefix = ''
+
+    # Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
+    # Note that in some versions, this is a Dictionary&lt;,&gt; and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
+    # to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
+    $useTargetObject = $null -ne $err.TargetObject -and
+    $err.TargetObject -is [System.Collections.IDictionary] -and
+        ([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
+        ([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
+
+    # The checks here determine if we show line detailed error information:
+    # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+    $isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
+    $err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
+
+    # - check if invocation is a script or multiple lines in the console
+    $isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
+
+    # - check that it's not a script module as expectation is that users don't want to see the line of error within a module
+    $shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
+    $myinv.ScriptName -notmatch '\.psm1$'
+
+    if ($useTargetObject -or $shouldShowLineDetail) {
+
+        if ($useTargetObject) {
+            $posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
+        } elseif ($myinv.ScriptName) {
+            if ($env:TERM_PROGRAM -eq 'vscode') {
+                # If we are running in vscode, we know the file:line:col links are clickable so we use this format
+                $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
+            } else {
+                $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
+            }
+        } else {
+            $posmsg = "${newline}"
+        }
+
+        if ($useTargetObject) {
+            $scriptLineNumber = $err.TargetObject.Line
+            $scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
+        } else {
+            $scriptLineNumber = $myinv.ScriptLineNumber
+            $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
+        }
+
+        if ($scriptLineNumberLength -gt 4) {
+            $headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
+        }
+
+        $lineWhitespace = ''
+        if ($scriptLineNumberLength -lt 4) {
+            $lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
+        }
+
+        $verticalBar = '|'
+        $posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
+
+        $highlightLine = ''
+        if ($useTargetObject) {
+            $line = $_.TargetObject.LineText.Trim()
+            $offsetLength = 0
+            $offsetInLine = 0
+        } else {
+            $positionMessage = $myinv.PositionMessage.Split($newline)
+            $line = $positionMessage[1].Substring(1) # skip the '+' at the start
+            $highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
+            $offsetLength = $highlightLine.Trim().Length
+            $offsetInLine = $highlightLine.IndexOf('~')
+        }
+
+        if (-not $line.EndsWith($newline)) {
+            $line += $newline
+        }
+
+        # don't color the whole line
+        if ($offsetLength -lt $line.Length - 1) {
+            $line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
+        }
+
+        $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
+        $offsetWhitespace = ' ' * $offsetInLine
+        $prefix = "${accentColor}${headerWhitespace}     ${verticalBar} ${errorColor}"
+        if ($highlightLine -ne '') {
+            $posMsg += "${prefix}${highlightLine}${newline}"
+        }
+        $message = "${prefix}"
+    }
+
+    if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+        if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+            # need to parse out the relevant part of the pre-rendered positionmessage
+            $message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+        } elseif ($err.Exception) {
+            $message += $err.Exception.Message
+        } elseif ($err.Message) {
+            $message += $err.Message
+        } else {
+            $message += $err.ToString()
+        }
+    } else {
+        $message += $err.ErrorDetails.Message
+    }
+
+    # if rendering line information, break up the message if it's wider than the console
+    if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
+        $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
+        $prefixVtLength = $prefix.Length - $prefixLength
+
+        # replace newlines in message so it lines up correct
+        $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
+
+        $windowWidth = 120
+        if ($Host.UI.RawUI -ne $null) {
+            $windowWidth = $Host.UI.RawUI.WindowSize.Width
+        }
+
+        if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
+            $sb = [Text.StringBuilder]::new()
+            $substring = TruncateString -string $message -length ($windowWidth + $prefixVTLength)
+            $null = $sb.Append($substring)
+            $remainingMessage = $message.Substring($substring.Length).Trim()
+            $null = $sb.Append($newline)
+            while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
+                $subMessage = $prefix + $remainingMessage
+                $substring = TruncateString -string $subMessage -length ($windowWidth + $prefixVtLength)
+
+                if ($substring.Length - $prefix.Length -gt 0) {
+                    $null = $sb.Append($substring)
+                    $null = $sb.Append($newline)
+                    $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
+                } else {
+                    break
+                }
+            }
+            $null = $sb.Append($prefix + $remainingMessage.Trim())
+            $message = $sb.ToString()
+        }
+
+        $message += $newline
+    }
+
+    $posmsg += "${errorColor}" + $message
+
+    $reason = 'Error'
+    if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
+        $reason = 'Exception'
+        # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+    } elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore)) {
+        $reason = $myinv.MyCommand
+    } elseif ($err.CategoryInfo.Activity) {
+        # If it's a scriptblock, better to show the command in the scriptblock that had the error
+        $reason = $err.CategoryInfo.Activity
+    } elseif ($myinv.MyCommand) {
+        $reason = $myinv.MyCommand
+    } elseif ($myinv.InvocationName) {
+        $reason = $myinv.InvocationName
+    } elseif ($err.CategoryInfo.Category) {
+        $reason = $err.CategoryInfo.Category
+    } elseif ($err.CategoryInfo.Reason) {
+        $reason = $err.CategoryInfo.Reason
+    }
+
+    "${errorColor}${reason}: ${posmsg}${resetcolor}"
+}
diff --git a/source/public/ConvertTo-ConciseErrorView.ps1 b/source/public/ConvertTo-ConciseErrorView.ps1
index 391014f..f36b53b 100644
--- a/source/public/ConvertTo-ConciseErrorView.ps1
+++ b/source/public/ConvertTo-ConciseErrorView.ps1
@@ -1,62 +1,32 @@
 function ConvertTo-ConciseErrorView {
     [CmdletBinding()]
     param(
+        [Parameter(ValueFromPipeline)]
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
-    if ("$accentColor".Length) {
-        $local:accentColor = $script:errorAccentColor
-        $local:resetColor = $script:resetColor
-    } else {
-        $local:accentColor = ">>>"
-        $local:resetColor = "<<<"
-    }
-
-
-    if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
-        $errorColor + $InputObject.Exception.Message + $resetColor
-    } else {
-        $myinv = $InputObject.InvocationInfo
-        if ($myinv -and ($myinv.MyCommand -or ($InputObject.CategoryInfo.Category -ne 'ParserError'))) {
-            # rip off lines that say "At line:1 char:1" (hopefully, in a language agnostic way)
-            $posmsg  = $myinv.PositionMessage -replace "^At line:1 char:1[\r\n]+"
-
-            # rip off the underline and use the accentcolor instead
-            $pattern = $posmsg -split "[\r\n]+" -match "\+( +~+)\s*" -replace '(~+)', '($1)' -replace '( +)','($1)' -replace '~| ','.'
-            $posmsg  = $posmsg -replace '[\r\n]+\+ +~+'
-            if ($pattern) {
-                $posmsg  = $posmsg -replace "\+$pattern", "+`$1$accentColor`$2$resetColor"
-            }
+    begin { ResetColor }
+    process {
+        if ($InputObject.FullyQualifiedErrorId -in 'NativeCommandErrorMessage','NativeCommandError') {
+            "${errorColor}$($InputObject.Exception.Message)${resetColor}"
         } else {
-            $posmsg = ""
-        }
-
-        if ($posmsg -ne "") {
-            $posmsg = "`n" + $posmsg
-        }
+            if (!"$accentColor".Length) {
+                $local:accentColor = ">>>"
+                $local:resetColor = "<<<"
+            }
 
-        if ( & { Set-StrictMode -Version 1; $InputObject.PSMessageDetails } ) {
-            $posmsg = " : " + $InputObject.PSMessageDetails + $posmsg
-        }
+            $message = GetConciseViewPositionMessage -InputObject $InputObject
 
-        $indent = 4
-        $width = $host.UI.RawUI.BufferSize.Width - $indent - 2
+            if ($InputObject.PSMessageDetails) {
+                $message = $errorColor + ' : ' + $InputObject.PSMessageDetails + $message
+            }
 
-        $originInfo = & { Set-StrictMode -Version 1; $InputObject.OriginInfo }
-        if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) {
-            $indentString = "+ PSComputerName        : " + $originInfo.PSComputerName
-            $posmsg += "`n"
-            foreach ($line in @($indentString -split "(.{$width})")) {
-                if ($line) {
-                    $posmsg += (" " * $indent + $line)
-                }
+            $recommendedAction = $InputObject.ErrorDetails.RecommendedAction
+            if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
+                $message = $message + $newline + ${errorColor} + '  Recommendation: ' + $recommendedAction + ${resetcolor}
             }
-        }
 
-        if (!$InputObject.ErrorDetails -or !$InputObject.ErrorDetails.Message) {
-            $errorColor + $InputObject.Exception.Message + $resetColor + $posmsg + "`n "
-        } else {
-            $errorColor + $InputObject.ErrorDetails.Message + $resetColor + $posmsg
+            $message
         }
     }
 }
\ No newline at end of file

From f20497cdc4ca595bb3223cb35e3c698de8508cc2 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Fri, 27 Sep 2024 22:53:41 -0400
Subject: [PATCH 05/15] Start by fixing the views. This is NormalView

---
 source/public/ConvertTo-NormalErrorView.ps1 | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/source/public/ConvertTo-NormalErrorView.ps1 b/source/public/ConvertTo-NormalErrorView.ps1
index 70a6da5..9ee9113 100644
--- a/source/public/ConvertTo-NormalErrorView.ps1
+++ b/source/public/ConvertTo-NormalErrorView.ps1
@@ -27,10 +27,9 @@ filter ConvertTo-NormalErrorView {
             }
 
             $Wrap = @{
-                Width = $width
+                Width = $host.UI.RawUI.BufferSize.Width - 2
                 IndentPadding = "                         "
             }
-            $width = $host.UI.RawUI.BufferSize.Width - 2
 
             $errorCategoryMsg = $InputObject.ErrorCategory_Message
             [string]$line = if ($null -ne $errorCategoryMsg) {

From 771e1db9f7aeb9a597f0a914238904cf07f95cd2 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Sat, 28 Sep 2024 14:46:10 -0400
Subject: [PATCH 06/15] This is DetailedView

---
 source/private/GetListRecursive.ps1           | 116 +++++++++---------
 source/public/ConvertTo-DetailedErrorView.ps1 |  17 +--
 2 files changed, 64 insertions(+), 69 deletions(-)

diff --git a/source/private/GetListRecursive.ps1 b/source/private/GetListRecursive.ps1
index a6cdd05..0b3e4d2 100644
--- a/source/private/GetListRecursive.ps1
+++ b/source/private/GetListRecursive.ps1
@@ -9,10 +9,8 @@ function GetListRecursive {
         [int]$indent = 0,
         [int]$depth = 1
     )
-    Write-Information "ENTER GetListRecursive END $($InputObject.GetType().FullName) $indent $depth" -Tags 'Trace', 'Enter', 'GetListRecursive'
-    Write-Information (Get-PSCallStack) -Tags 'Trace', 'StackTrace', 'GetListRecursive'
     $output = [System.Text.StringBuilder]::new()
-    $prefix = ' ' * $indent
+    $padding = ' ' * $indent
 
     $expandTypes = @(
         'Microsoft.Rest.HttpRequestMessageWrapper'
@@ -20,29 +18,29 @@ function GetListRecursive {
         'System.Management.Automation.InvocationInfo'
     )
 
-    # The built-in DetailedView aligns all the ":" characters, so we need to find the longest property name
-    $propLength = 0
-    foreach ($prop in $InputObject.PSObject.Properties) {
-        if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
-            $propLength = $prop.Name.Length
-        }
-    }
+    # The built-in DetailedView aligns all the ":" characters, but it's awful
 
     $addedProperty = $false
     foreach ($prop in $InputObject.PSObject.Properties) {
+        # PowerShell creates an ErrorRecord property on Exceptions that points back to the parent ErrorRecord.
+        # This is basically a circular reference that causes repeated informtion, so we're going to skip them
+        if ($prop.Value -is [System.Management.Automation.ErrorRecord] -and $depth -ge 2) {
+            continue
+        }
         # don't show empty properties or our added property for $error[index]
         if ($null -ne $prop.Value -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
             $addedProperty = $true
-            $null = $output.Append($prefix)
+            $null = $output.Append($padding)
             $null = $output.Append($accentColor)
             $null = $output.Append($prop.Name)
-            $null = $output.Append(' ',($propLength - $prop.Name.Length))
-            $null = $output.Append(' : ')
+            $null = $output.Append(': ')
             $null = $output.Append($resetColor)
 
-            $newIndent = $indent + 2
+            [int]$nextIndent = $indent + 2
+            [int]$nextDepth = $depth + 1
+            $nextPadding = ' ' * $nextIndent
 
-            # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+            # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes
             if ($prop.Value -is [Exception] -or
                 $prop.Value -is [System.Management.Automation.ErrorRecord] -or
                 $expandTypes -contains $prop.TypeNameOfValue -or
@@ -51,16 +49,12 @@ function GetListRecursive {
                 if ($depth -ge $maxDepth) {
                     $null = $output.Append($ellipsis)
                 } else {
+                    # For Exceptions, add a fake "Type" property
                     if ($prop.Value -is [Exception]) {
-                        $null = $output.Append($newline)
-                        $null = $output.Append((
-                            GetListRecursive ([PSCustomObject]@{
-                                                "Type" = $errorAccentColor + $prop.Value.GetType().FullName + $resetColor
-                                            }) $newIndent ($depth + 1)
-                        ))
+                        $null = $output.Append(( $accentColor + "[" + $prop.Value.GetType().FullName + "]" + $resetColor))
                     }
                     $null = $output.Append($newline)
-                    $null = $output.Append((GetListRecursive $prop.Value $newIndent ($depth + 1)))
+                    $null = $output.Append((GetListRecursive $prop.Value $nextIndent $nextDepth))
                 }
             } elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
                 # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
@@ -75,28 +69,34 @@ function GetListRecursive {
                     }
 
                     $null = $output.Append($newline)
-                    $null = $output.Append((GetListRecursive $targetSite $newIndent ($depth + 1)))
+                    $null = $output.Append((GetListRecursive $targetSite $nextIndent $nextDepth))
                 }
             } elseif ($prop.Name -eq 'StackTrace') {
-                # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
-                # for a stacktrace which is usually quite wide with info, we left justify it
+                # StackTrace is handled specifically because the lines are typically long but we can't trucate them, so we don't indent it any more
                 $null = $output.Append($newline)
-                $null = $output.Append($prop.Value)
+                # $null = $output.Append($prop.Value)
+                $Wrap = @{
+                    Width = $Host.UI.RawUI.BufferSize.Width - 2
+                    IndentPadding = ""
+                    HangingIndent = "   "
+                }
+                $null = $output.Append(($prop.Value | WrapString @Wrap))
+            } elseif ($prop.Name -eq 'HResult') {
+                # `HResult` is handled specifically so we can format it in hex
+                # $null = $output.Append($newline)
+                $null = $output.Append("0x{0:x} ({0})" -f $prop.Value)
+            } elseif ($prop.Name -eq 'PipelineIterationInfo') {
+                # I literally have no idea what use this is
+                $null = $output.Append($prop.Value -join ', ')
             } elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
-                # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
-                $isFirstElement = $true
+                # Dictionary and Hashtable we want to show as Key/Value pairs
+                $null = $output.Append($newline)
                 foreach ($key in $prop.Value.Keys) {
-                    if ($isFirstElement) {
-                        $null = $output.Append($newline)
-                    }
-
                     if ($key -eq 'Authorization') {
-                        $null = $output.Append("${prefix}    ${accentColor}${key}: ${resetColor}${ellipsis}${newline}")
+                        $null = $output.Append("${nextPadding}${accentColor}${key}: ${resetColor}${ellipsis}${newline}")
                     } else {
-                        $null = $output.Append("${prefix}    ${accentColor}${key}: ${resetColor}$($prop.Value[$key])${newline}")
+                        $null = $output.Append("${nextPadding}${accentColor}${key}: ${resetColor}$($prop.Value[$key])${newline}")
                     }
-
-                    $isFirstElement = $false
                 }
             } elseif (!($prop.Value -is [System.String]) -and $null -ne $prop.Value.GetType().GetInterface('IEnumerable') -and $prop.Name -ne 'Data') {
                 # if the object implements IEnumerable and not a string, we try to show each object
@@ -108,41 +108,43 @@ function GetListRecursive {
                     $isFirstElement = $true
                     foreach ($value in $prop.Value) {
                         $null = $output.Append($newline)
-                        if (!$isFirstElement) {
-                            $null = $output.Append($newline)
+
+                        if ($value -is [Type]) {
+                            # Just show the typename instead of it as an object
+                            $null = $output.Append("${nextPadding}[$($value.ToString())]")
+                        } elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
+                            $null = $output.Append("${nextPadding}${value}")
+                        } else {
+                            if (!$isFirstElement) {
+                                $null = $output.Append($newline)
+                            }
+                            $null = $output.Append((GetListRecursive $value $nextIndent $nextDepth))
                         }
-                        $null = $output.Append((GetListRecursive $value $newIndent ($depth + 1)))
                         $isFirstElement = $false
                     }
                 }
+            }  elseif ($prop.Value -is [Type]) {
+                # Just show the typename instead of it as an object
+                $null = $output.Append("[$($prop.Value.ToString())]")
             } else {
                 # Anything else, we convert to string.
                 # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
                 $value = $null
                 if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $null -ne $value) {
                     $value = $value.Trim()
-                    if ($prop.Name -eq 'PositionMessage') {
+                    if ($InputObject -is [System.Management.Automation.InvocationInfo] -and $prop.Name -eq 'PositionMessage') {
+                        # Make the underline red
                         $value = $value.Insert($value.IndexOf('~'), $errorColor)
-                    } elseif ($prop.Name -eq 'Message') {
+                    } elseif ( ($InputObject -is [System.Management.Automation.ErrorRecord] -or
+                                $InputObject -is [System.Exception]) -and $prop.Name -in 'Message', 'FullyQualifiedErrorId', 'CategoryInfo') {
                         $value = $errorColor + $value
                     }
-
-                    $isFirstLine = $true
-                    if ($value.Contains($newline)) {
-                        # the 3 is to account for ' : '
-                        # $valueIndent = ' ' * ($prop.Name.Length + 2)
-                        $valueIndent = ' ' * ($propLength + 3)
-                        # need to trim any extra whitespace already in the text
-                        foreach ($line in $value.Split($newline)) {
-                            if (!$isFirstLine) {
-                                $null = $output.Append("${newline}${prefix}${valueIndent}")
-                            }
-                            $null = $output.Append($line.Trim())
-                            $isFirstLine = $false
-                        }
-                    } else {
-                        $null = $output.Append($value)
+                    $Wrap = @{
+                        Width = $Host.UI.RawUI.BufferSize.Width - 2
+                        IndentPadding = " " * ($nextIndent + $prop.Name.Length)
                     }
+
+                    $null = $output.Append(($value | WrapString @Wrap).TrimStart())
                 }
             }
 
diff --git a/source/public/ConvertTo-DetailedErrorView.ps1 b/source/public/ConvertTo-DetailedErrorView.ps1
index 210520c..555ba16 100644
--- a/source/public/ConvertTo-DetailedErrorView.ps1
+++ b/source/public/ConvertTo-DetailedErrorView.ps1
@@ -1,10 +1,10 @@
-function ConvertTo-DetailedErrorView {
+filter ConvertTo-DetailedErrorView {
     <#
         .SYNOPSIS
             Converts an ErrorRecord to a detailed error string
         .DESCRIPTION
-            The default PowerShell "DetailedView" ErrorView
-            Copied from the PowerShellCore.format.ps1xml
+            An "improved" version of the PowerShell "DetailedView" ErrorView
+            Originally copied from the PowerShellCore.format.ps1xml
         .LINK
             https://github.com/PowerShell/PowerShell/blob/c444645b0941d73dc769f0bba6ab70d317bd51a9/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs#L903
     #>
@@ -19,15 +19,8 @@ function ConvertTo-DetailedErrorView {
         # The maximum depth to recurse into the object
         [int]$maxDepth = 10
     )
-
-    begin {
-        Write-Information "ENTER ConvertTo-DetailedErrorView BEGIN " -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
-
-        Write-Information "EXIT ConvertTo-DetailedErrorView BEGIN" -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
-    }
+    begin { ResetColor }
     process {
-        Write-Information "ENTER ConvertTo-DetailedErrorView PROCESS $($InputObject.GetType().FullName)" -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
-        GetListRecursive $InputObject
-        Write-Information "EXIT ConvertTo-DetailedErrorView PROCESS $($InputObject.GetType().FullName)" -Tags 'Trace', 'Enter', 'ConvertTo-DetailedErrorView'
+        $newline + (GetListRecursive $InputObject) + $newline
     }
 }

From 7618f00532bb7a94fdf7d94ae05678910b34619b Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 30 Sep 2024 01:32:28 -0400
Subject: [PATCH 07/15] Fix WrapString so it works with LineColors

---
 source/private/WrapString.ps1 | 34 +++++++++++++++++++++++++++-------
 1 file changed, 27 insertions(+), 7 deletions(-)

diff --git a/source/private/WrapString.ps1 b/source/private/WrapString.ps1
index 26c2f52..ed52337 100644
--- a/source/private/WrapString.ps1
+++ b/source/private/WrapString.ps1
@@ -20,10 +20,17 @@ filter WrapString {
         [Parameter(Position=0)]
         [int]$Width = ($Host.UI.RawUI.BufferSize.Width),
 
-        # The padding to add to the front of each line to cause indenting. Defaults to empty string.
-        [Parameter(Position=1)]
+        # The padding for each line defaults to an empty string.
+        # If set, whitespace on the front of each line is replaced with this string.
         [string]$IndentPadding = ([string]::Empty),
 
+        # If set, this will be used only for the first line (defaults to IndentPadding)
+        [string]$FirstLineIndent = $IndentPadding,
+
+        # If set, wrapped lines use this instead of IndentPadding to create a hanging indent
+        [string]$WrappedIndent  = $IndentPadding,
+
+
         # If set, colors to use for alternating lines
         [string[]]$Colors = @(''),
 
@@ -31,13 +38,14 @@ filter WrapString {
         [switch]$EmphasizeOriginalNewlines
     )
     begin {
+        $FirstLine = $true
         $color = 0;
         Write-Debug "Colors: $($Colors -replace "`e(.+)", "`e`$1``e`$1")"
         # $wrappableChars = [char[]]" ,.?!:;-`n`r`t"
         # $maxLength = $width - $IndentPadding.Length -1
         $wrapper = [Regex]::new("((?:$AnsiPattern)*[^-=,.?!:;\s\r\n\t\\\/\|]+(?:$AnsiPattern)*)", "Compiled")
         $output = [System.Text.StringBuilder]::new()
-        $buffer = [System.Text.StringBuilder]::new($Colors[$color])
+        $buffer = [System.Text.StringBuilder]::new()
         $lineLength = 0
         if ($Width -lt $IndentPadding.Length) {
             Write-Warning "Width $Width is less than IndentPadding length $($IndentPadding.Length). Setting Width to BufferWidth ($($Host.UI.RawUI.BufferSize.Width))"
@@ -45,6 +53,9 @@ filter WrapString {
     }
     process {
         foreach($line in $InputObject -split "(\r?\n)") {
+            if ($FirstLine -and $PSBoundParameters.ContainsKey('FirstLineIndent')) {
+                $IndentPadding, $FirstLineIndent = $FirstLineIndent, $IndentPadding
+            }
             # Don't bother trying to split empty lines
             if ([String]::IsNullOrWhiteSpace($AnsiRegex.Replace($line, ''))) {
                 Write-Debug "Empty String ($($line.Length))"
@@ -64,15 +75,24 @@ filter WrapString {
                 } else {
                     Write-Verbose "Output $($lineLength - $slice.Length)"
                     Write-Verbose "+ $($slice.Length) = $($slice.Length)"
-                    $color = ($color + 1) % $Colors.Length
                     #$null = $output.Append($buffer.ToString())
-                    $null = $buffer.Append($newline).Append($slice.Text)
+                    $null = $buffer.Append($newline).Append($WrappedIndent).Append($slice.Text)
                     $lineLength = $IndentPadding.Length + $slice.Length
                 }
             }
-            $null = $output.Append($buffer.ToString())
-            $null = $buffer.Clear().Append($newline).Append($Colors[$color]).Append($IndentPadding)
+            if (!$FirstLine) {
+                $null = $output.Append($newline)
+            }
+            if ($PSBoundParameters.ContainsKey("IndentPadding")) {
+                $null = $output.Append($Colors[$color] + $IndentPadding + $buffer.ToString().TrimStart())
+            } else {
+                $null = $output.Append($Colors[$color] + $buffer.ToString())
+            }
+            $color = ($color + 1) % $Colors.Length
+            $null = $buffer.Clear() #.Append($Colors[$color]).Append($IndentPadding)
             $lineLength = $IndentPadding.Length
+            $FirstLine = $false
+            $IndentPadding = $FirstLineIndent
         }
         $output.ToString()
     }

From 85ab35e8c2a147ee569996f702f68631f666412e Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 30 Sep 2024 02:13:56 -0400
Subject: [PATCH 08/15] This is YamlView which is almost the same as Detailed

---
 source/private/GetYamlRecursive.ps1       | 54 +++++++++++++----------
 source/public/ConvertTo-YamlErrorView.ps1 | 18 +++-----
 2 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/source/private/GetYamlRecursive.ps1 b/source/private/GetYamlRecursive.ps1
index ac3880e..9862626 100644
--- a/source/private/GetYamlRecursive.ps1
+++ b/source/private/GetYamlRecursive.ps1
@@ -13,7 +13,7 @@
 
         # Optionally, a limit on the depth to recurse properties (defaults to 16)
         [parameter()]
-        [int]$depth = 16,
+        [int]$depth = 1,
 
         # If set, include empty and null properties in the output
         [switch]$IncludeEmpty,
@@ -27,18 +27,23 @@
         [switch]$XmlAsXml
     )
     process {
-        $wrap = [Console]::BufferWidth - 1 - ($NestingLevel * 2)
+        $Width = $Host.UI.RawUI.BufferSize.Width - 1 - ($NestingLevel * 2)
+        $__hasoutput = $true
+        $padding = '  ' * $NestingLevel # # lets just create our left-padding for the block
+        $Recurse = @{
+            'Depth'        = $depth + 1
+            'NestingLevel' = $NestingLevel + 1
+            'XmlAsXml'     = $XmlAsXml
+        }
+        $Wrap = @{
+            Width         = $Host.UI.RawUI.BufferSize.Width - 2
+            IndentPadding = $padding
+            Colors        = $LineColors
+        }
+
         @(
             if ($Null -eq $InputObject) { return 'null' } # if it is null return null
             if ($NestingLevel -eq 0 -and $local:__hasoutput) { '---' } # if we have output before, add a yaml separator
-            $__hasoutput = $true
-            $padding = "`n$('  ' * $NestingLevel)" # lets just create our left-padding for the block
-            $Recurse = @{
-                'Depth'        = $depth - 1
-                'NestingLevel' = $NestingLevel + 1
-                'XmlAsXml'     = $XmlAsXml
-            }
-            $Wrap =
 
             try {
                 switch ($InputObject) {
@@ -57,7 +62,7 @@
                     }
                     { $InputObject -is [System.Xml.XmlDocument] -or $InputObject -is [System.Xml.XmlElement] } {
                         "|"
-                        $InputObject.OuterXml | WrapString $Wrap $padding -Colors:$LineColors
+                        $InputObject.OuterXml | WrapString @Wrap
                         break
                     }
                     { $InputObject -is [datetime] -or $InputObject -is [datetimeoffset] } {
@@ -77,7 +82,7 @@
                     }
                     # If we're going to go over our depth, just output like it's a value type
                     # ValueTypes are just output with no possibility of wrapping or recursion
-                    { $InputObject -is [Enum] -or $InputObject.GetType().BaseType -eq [ValueType] -or $depth -eq 1 } {
+                    { $InputObject -is [Enum] -or $InputObject.GetType().BaseType -eq [ValueType] -or $depth -gt $maxDepth } {
                         "$InputObject"
                         break
                     }
@@ -87,7 +92,7 @@
                     { $InputObject -is [System.Collections.IDictionary] } {
                         foreach ($kvp in  $InputObject.GetEnumerator()) {
                             # Write-Verbose "$($padding)Enumerate $($property.Name)"
-                            "$padding$accentColor$($kvp.Name):$resetColor " +
+                            "$newline$padding$accentColor$($kvp.Name):$resetColor " +
                             (GetYamlRecursive -InputObject $kvp.Value @Recurse)
                         }
                         break
@@ -98,24 +103,27 @@
                             # Write-Verbose "$($padding)Enumerate $($property.Name)"
                             $Value = GetYamlRecursive -InputObject $item @Recurse
                             # if ($Value -ne 'null' -or $IncludeEmpty) {
-                            "$accentColor$padding$resetColor- $Value"
+                            "$newline$accentColor$padding$resetColor- $Value"
                             # }
                         }
                         break
                     }
 
                     # Limit recursive enumeration to specific types:
-                    { $InputObject -is [Exception] -or $InputObject -is [System.Management.Automation.ErrorRecord] -or
+                    { $InputObject -is [Exception] -or ($InputObject -is [System.Management.Automation.ErrorRecord] -and $depth -lt 2) -or
                         $InputObject.PSTypeNames[0] -in @(
-                            'System.Exception'
-                            'System.Management.Automation.ErrorRecord'
+                            # 'System.Exception'
+                            # 'System.Management.Automation.ErrorRecord'
                             'Microsoft.Rest.HttpRequestMessageWrapper'
                             'Microsoft.Rest.HttpResponseMessageWrapper'
                             'System.Management.Automation.InvocationInfo'
                         ) } {
+                        if ($depth -ge $maxDepth) {
+                            $null = $output.Append($ellipsis)
+                        }
                         # For exceptions, output a fake property for the exception type
                         if ($InputObject -is [Exception]) {
-                            "$padding${accentColor}#Type:$resetColor ${errorAccentColor}" + $InputObject.GetType().FullName + $resetColor
+                            "$newline$padding${accentColor}#Type:$resetColor ${accentColor}" + $InputObject.GetType().FullName + $resetColor
                         }
                         foreach ($property in $InputObject.PSObject.Properties) {
                             if ($property.Value) {
@@ -128,7 +136,7 @@
                                     $Value = "$errorColor$Value$resetColor"
                                 }
                                 if ((-not [string]::IsNullOrEmpty($Value) -and $Value -ne 'null' -and $Value.Count -gt 0) -or $IncludeEmpty) {
-                                    "$padding$accentColor$($property.Name):$resetColor " + $Value
+                                    "$newline$padding$accentColor$($property.Name):$resetColor " + $Value
                                 }
                             }
                         }
@@ -148,9 +156,9 @@
                         $StringValue = $null
                         if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($InputObject, [string], [ref]$StringValue) -and $null -ne $StringValue) {
                             $StringValue = $StringValue.Trim()
-                            if ($StringValue -match '[\r\n]' -or $StringValue.Length -gt $wrap) {
-                                ">" # signal that we are going to use the readable 'newlines-folded' format
-                                $StringValue | WrapString $Wrap $padding -Colors:$LineColors
+                            if ($StringValue -match '[\r\n]' -or $StringValue.Length -gt $Width) {
+                                ">$newline" # signal that we are going to use the readable 'newlines-folded' format
+                                $StringValue | WrapString @Wrap -EmphasizeOriginalNewlines
                             } elseif ($StringValue.Contains(":")) {
                                 "'$($StringValue -replace '''', '''''')'" # single quote it
                             } else {
@@ -162,7 +170,7 @@
                     }
                 }
             } catch {
-                Write-Error "Error'$($_)' in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($InputObject)' Class: $($InputObject.GetType().Name) BaseClass: $($InputObject.GetType().BaseType.Name) "
+                "Error formatting error ($($_)) in script $($_.InvocationInfo.ScriptName) $($_.InvocationInfo.Line.Trim()) (line $($_.InvocationInfo.ScriptLineNumber)) char $($_.InvocationInfo.OffsetInLine) executing $($_.InvocationInfo.MyCommand) on $type object '$($InputObject)' Class: $($InputObject.GetType().Name) BaseClass: $($InputObject.GetType().BaseType.Name) "
             }
         ) -join ""
     }
diff --git a/source/public/ConvertTo-YamlErrorView.ps1 b/source/public/ConvertTo-YamlErrorView.ps1
index 704bc2d..0e8ce63 100644
--- a/source/public/ConvertTo-YamlErrorView.ps1
+++ b/source/public/ConvertTo-YamlErrorView.ps1
@@ -12,22 +12,14 @@
         [System.Management.Automation.ErrorRecord]
         $InputObject,
 
-        # Optionally, a limit on the depth to recurse properties (defaults to 16)
-        [parameter()]
-        [int]$depth = 16,
+        # The maximum depth to recurse into the object
+        [int]$maxDepth = 10,
 
         # If set, include empty and null properties in the output
-        [switch]$IncludeEmpty,
-
-        # Recursive use only. Handles indentation for formatting
-        [parameter(DontShow)]
-        [int]$NestingLevel = 0,
-
-        # use OuterXml instead of treating XmlDocuments like objects
-        [parameter(DontShow)]
-        [switch]$XmlAsXml
+        [switch]$IncludeEmpty
     )
+    begin { ResetColor }
     process {
-        GetYamlRecursive $InputObject
+        GetYamlRecursive -InputObject $InputObject -IncludeEmpty:$IncludeEmpty
     }
 }
\ No newline at end of file

From cfc60de442bafe7239a5078e8d29accbd3a5a422 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 30 Sep 2024 02:14:52 -0400
Subject: [PATCH 09/15] This is ConciseView

---
 source/private/TruncateString.ps1 | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 source/private/TruncateString.ps1

diff --git a/source/private/TruncateString.ps1 b/source/private/TruncateString.ps1
new file mode 100644
index 0000000..86ffab6
--- /dev/null
+++ b/source/private/TruncateString.ps1
@@ -0,0 +1,21 @@
+filter TruncateString {
+    [CmdletBinding()]
+    param(
+        # The input string will be wrapped to a certain length, with optional padding on the front
+        [Parameter(ValueFromPipeline)]
+        [string]$InputObject,
+
+        [Parameter(Position = 0)]
+        [Alias('Length')]
+        [int]$Width = ($Host.UI.RawUI.BufferSize.Width)
+    )
+    # $wrappableChars = [char[]]" ,.?!:;-`n`r`t"
+    # $maxLength = $width - $IndentPadding.Length -1
+    $wrapper = [Regex]::new("((?:$AnsiPattern)*[^-=,.?!:;\s\r\n\t\\\/\|]+(?:$AnsiPattern)*)", "Compiled")
+
+    if ($InputObject.Length -le $Width) {
+        return $InputObject
+    }
+
+    ($InputObject.Substring(0,$length) -split $wrapper,-2)[0]
+}
\ No newline at end of file

From 47c2a4809e90cd13209df173325a18cd479da908 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 30 Sep 2024 02:15:11 -0400
Subject: [PATCH 10/15] This is DetailedView

---
 source/private/GetListRecursive.ps1 | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/source/private/GetListRecursive.ps1 b/source/private/GetListRecursive.ps1
index 0b3e4d2..ef943ea 100644
--- a/source/private/GetListRecursive.ps1
+++ b/source/private/GetListRecursive.ps1
@@ -78,7 +78,8 @@ function GetListRecursive {
                 $Wrap = @{
                     Width = $Host.UI.RawUI.BufferSize.Width - 2
                     IndentPadding = ""
-                    HangingIndent = "   "
+                    WrappedIndent = "   "
+                    Colors        = $LineColors
                 }
                 $null = $output.Append(($prop.Value | WrapString @Wrap))
             } elseif ($prop.Name -eq 'HResult') {
@@ -111,9 +112,9 @@ function GetListRecursive {
 
                         if ($value -is [Type]) {
                             # Just show the typename instead of it as an object
-                            $null = $output.Append("${nextPadding}[$($value.ToString())]")
+                            $null = $output.Append("[$($value.ToString())]")
                         } elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
-                            $null = $output.Append("${nextPadding}${value}")
+                            $null = $output.Append("${value}")
                         } else {
                             if (!$isFirstElement) {
                                 $null = $output.Append($newline)
@@ -141,10 +142,14 @@ function GetListRecursive {
                     }
                     $Wrap = @{
                         Width = $Host.UI.RawUI.BufferSize.Width - 2
+                        # Because the first line contains the property name, we don't want to indent it
+                        FirstLineIndent = ''
+                        # But all other lines (including wrapped lines) should be indented to align
                         IndentPadding = " " * ($nextIndent + $prop.Name.Length)
+                        Colors = $LineColors
                     }
 
-                    $null = $output.Append(($value | WrapString @Wrap).TrimStart())
+                    $null = $output.Append(($value | WrapString @Wrap))
                 }
             }
 

From e103abf69c2ed355853ec239896320bd86eab41f Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 7 Oct 2024 01:36:17 -0400
Subject: [PATCH 11/15] Detect Github Workflow and Azure Pipelines and use
 DetailedView

---
 build.psd1                      |  1 +
 source/postfix.ps1              |  7 +++++++
 source/prefix.ps1               | 22 +++-------------------
 source/public/Set-ErrorView.ps1 |  3 ++-
 4 files changed, 13 insertions(+), 20 deletions(-)
 create mode 100644 source/postfix.ps1

diff --git a/build.psd1 b/build.psd1
index a3d17d5..bc39a65 100644
--- a/build.psd1
+++ b/build.psd1
@@ -2,6 +2,7 @@
     ModuleManifest           = "./source/ErrorView.psd1"
     CopyPaths                = 'ErrorView.format.ps1xml'
     Prefix                   = 'prefix.ps1'
+    Postfix                  = 'postfix.ps1'
     # The rest of the paths are relative to the manifest
     OutputDirectory          = ".."
     VersionedOutputDirectory = $true
diff --git a/source/postfix.ps1 b/source/postfix.ps1
new file mode 100644
index 0000000..bac8866
--- /dev/null
+++ b/source/postfix.ps1
@@ -0,0 +1,7 @@
+if ($script:ErrorView) {
+    Set-ErrorView $ErrorView
+} elseif ($Env:GITHUB_ACTIONS -or $Env:TF_BUILD) {
+    Set-ErrorView "DetailedErrorView"
+} else {
+    Set-ErrorView "ConciseView"
+}
\ No newline at end of file
diff --git a/source/prefix.ps1 b/source/prefix.ps1
index 5ab3ea2..a116ae8 100644
--- a/source/prefix.ps1
+++ b/source/prefix.ps1
@@ -1,33 +1,17 @@
-[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Justification = 'ErrorView is all about the ErrorView global variable')]
-[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Seriously. Stop complaining about ErrorView')]
 param(
-    $global:ErrorView = "Simple"
+    $ErrorView
 )
 
-# We need to _overwrite_ the ErrorView
-# So -PrependPath, instead of using FormatsToProcess
+# We need to _overwrite_ the ErrorView, so we must use -PrependPath
 Update-FormatData -PrependPath $PSScriptRoot\ErrorView.format.ps1xml
 
 Set-StrictMode -Off
 $ErrorActionPreference = 'Stop'
 trap { 'Error found in error view definition: ' + $_.Exception.Message }
 
-# Borrowed this one from https://github.com/chalk/ansi-regex
-$script:AnsiEscapes = [Regex]::new("([\u001b\u009b][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?(?:\u001b\u005c|\u0007))|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~])))", "Compiled");
-
-# starting with an escape character and then...
-# ESC ] <anything> <ST> - where ST is either 1B 5C or 7 (BEL, aka `a)
-# ESC [ non-letters letter (or ~, =, @, >)
-# ESC ( <any character>
-# ESC O P
-# ESC O Q
-# ESC O R
-# ESC O S
-# $script:AnsiEscapes = [Regex]::new("\x1b[\(\)%`"&\.\/*+.-][@-Z]|\x1b\].*?(?:\u001b\u005c|\u0007|^)|\x1b\[\P{L}*[@-_A-Za-z^`\{\|\}~]|\x1b#\d|\x1b[!-~]", [System.Text.RegularExpressions.RegexOptions]::Compiled);
-
 $script:ellipsis = [char]0x2026
 $script:newline = [Environment]::Newline
 $script:LineColors = @(
     "`e[38;2;255;255;255m"
     "`e[38;2;179;179;179m"
-)
+)
\ No newline at end of file
diff --git a/source/public/Set-ErrorView.ps1 b/source/public/Set-ErrorView.ps1
index 340394a..7a0d5e6 100644
--- a/source/public/Set-ErrorView.ps1
+++ b/source/public/Set-ErrorView.ps1
@@ -21,7 +21,8 @@ filter Set-ErrorView {
             })]
         $View = "Normal"
     )
-    # Update the enum every time, because how often do you change the error view?
+
+    # Re-create an update the enum every time, because how often do you change the error view?
     $Names = [System.Management.Automation.ErrorView].GetEnumNames() + @(
         Get-Command ConvertTo-*ErrorView -ListImported -ParameterName InputObject -ParameterType [System.Management.Automation.ErrorRecord]
     ).Name -replace "ConvertTo-(\w+)ErrorView", '$1View' | Select-Object -Unique

From 76e474ad222b65a8763d75f1b2e55decd68d4b67 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 7 Oct 2024 01:38:43 -0400
Subject: [PATCH 12/15] Add explicit formatting for Github Actions and Azure
 Pipelines

Closes #2 and #3
---
 .../GetAzurePipelinePositionMessage.ps1       | 36 +++++++++++++++++
 ...itionMessage.ps1 => GetConciseMessage.ps1} |  2 +-
 source/private/GetErrorMessage.ps1            | 23 +++++++++++
 source/private/GetErrorTitle.ps1              | 29 ++++++++++++++
 .../GetGoogleWorkflowPositionMessage.ps1      | 39 +++++++++++++++++++
 source/private/WrapString.ps1                 | 10 -----
 source/public/ConvertTo-ConciseErrorView.ps1  |  2 +-
 source/public/ConvertTo-DetailedErrorView.ps1 |  7 +++-
 8 files changed, 135 insertions(+), 13 deletions(-)
 create mode 100644 source/private/GetAzurePipelinePositionMessage.ps1
 rename source/private/{GetConciseViewPositionMessage.ps1 => GetConciseMessage.ps1} (99%)
 create mode 100644 source/private/GetErrorMessage.ps1
 create mode 100644 source/private/GetErrorTitle.ps1
 create mode 100644 source/private/GetGoogleWorkflowPositionMessage.ps1

diff --git a/source/private/GetAzurePipelinePositionMessage.ps1 b/source/private/GetAzurePipelinePositionMessage.ps1
new file mode 100644
index 0000000..a32031e
--- /dev/null
+++ b/source/private/GetAzurePipelinePositionMessage.ps1
@@ -0,0 +1,36 @@
+filter GetGooglePositionMessage {
+    [CmdletBinding()]
+    [OUtputType([string])]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [System.Management.Automation.ErrorRecord]
+        $InputObject
+    )
+    $InvocationInfo = $InputObject.InvocationInfo
+    # Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
+    # Note that in some versions, this is a Dictionary<,> and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
+    # to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
+    $useTargetObject = $null -ne $InputObject.TargetObject -and
+                    $InputObject.TargetObject -is [System.Collections.IDictionary] -and
+                    ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('Line') -and
+                    ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('LineText')
+
+    $file = if ($useTargetObject) {
+        "$($InputObject.TargetObject.File)"
+    } elseif (.ScriptName) {
+        "$($InvocationInfo.ScriptName)"
+    }
+
+    $line = if ($useTargetObject) {
+        $InputObject.TargetObject.Line
+    } else {
+        $InvocationInfo.ScriptLineNumber
+    }
+
+    if ($useTargetObject) {
+        "sourcepath=$file;linenumber=$line"
+    } else {
+        $column = $InvocationInfo.OffsetInLine
+        "sourcepath=$file;linenumber=$line,columnnumber=$column"
+    }
+}
diff --git a/source/private/GetConciseViewPositionMessage.ps1 b/source/private/GetConciseMessage.ps1
similarity index 99%
rename from source/private/GetConciseViewPositionMessage.ps1
rename to source/private/GetConciseMessage.ps1
index 7acdaac..a9ac955 100644
--- a/source/private/GetConciseViewPositionMessage.ps1
+++ b/source/private/GetConciseMessage.ps1
@@ -1,4 +1,4 @@
-filter GetConciseViewPositionMessage {
+filter GetConciseMessage {
     [CmdletBinding()]
     param(
         [Parameter(ValueFromPipeline)]
diff --git a/source/private/GetErrorMessage.ps1 b/source/private/GetErrorMessage.ps1
new file mode 100644
index 0000000..e7d77cc
--- /dev/null
+++ b/source/private/GetErrorMessage.ps1
@@ -0,0 +1,23 @@
+filter GetErrorTitle {
+    [CmdletBinding()]
+    [OUtputType([string])]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [System.Management.Automation.ErrorRecord]
+        $InputObject
+    )
+    if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+        if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+            # need to parse out the relevant part of the pre-rendered positionmessage
+            $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+        } elseif ($err.Exception) {
+            $err.Exception.Message
+        } elseif ($err.Message) {
+            $err.Message
+        } else {
+            $err.ToString()
+        }
+    } else {
+        $err.ErrorDetails.Message
+    }
+}
\ No newline at end of file
diff --git a/source/private/GetErrorTitle.ps1 b/source/private/GetErrorTitle.ps1
new file mode 100644
index 0000000..f1f779f
--- /dev/null
+++ b/source/private/GetErrorTitle.ps1
@@ -0,0 +1,29 @@
+filter GetErrorTitle {
+    [CmdletBinding()]
+    [OUtputType([string])]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [System.Management.Automation.ErrorRecord]
+        $InputObject
+    )
+
+    if ($InputObject.Exception -and $InputObject.Exception.WasThrownFromThrowStatement) {
+        'Exception'
+        # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+    } elseif ($InputObject.InvocationInfo.MyCommand -and $InputObject.InvocationInfo.MyCommand.Name -and (Get-Command -Name $InputObject.InvocationInfo.MyCommand -ErrorAction Ignore)) {
+        $InputObject.InvocationInfo.MyCommand
+    } elseif ($InputObject.CategoryInfo.Activity) {
+        # If it's a scriptblock, better to show the command in the scriptblock that had the error
+        $InputObject.CategoryInfo.Activity
+    } elseif ($InputObject.InvocationInfo.MyCommand) {
+        $InputObject.InvocationInfo.MyCommand
+    } elseif ($InputObject.InvocationInfo.InvocationName) {
+        $InputObject.InvocationInfo.InvocationName
+    } elseif ($InputObject.CategoryInfo.Category) {
+        $InputObject.CategoryInfo.Category
+    } elseif ($InputObject.CategoryInfo.Reason) {
+        $InputObject.CategoryInfo.Reason
+    } else {
+        'Error'
+    }
+}
\ No newline at end of file
diff --git a/source/private/GetGoogleWorkflowPositionMessage.ps1 b/source/private/GetGoogleWorkflowPositionMessage.ps1
new file mode 100644
index 0000000..6faac67
--- /dev/null
+++ b/source/private/GetGoogleWorkflowPositionMessage.ps1
@@ -0,0 +1,39 @@
+filter GetGoogleWorkflowPositionMessage {
+    [CmdletBinding()]
+    [OUtputType([string])]
+    param(
+        [Parameter(ValueFromPipeline)]
+        [System.Management.Automation.ErrorRecord]
+        $InputObject
+    )
+    $InvocationInfo = $InputObject.InvocationInfo
+    # Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
+    # Note that in some versions, this is a Dictionary<,> and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
+    # to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
+    $useTargetObject = $null -ne $InputObject.TargetObject -and
+                    $InputObject.TargetObject -is [System.Collections.IDictionary] -and
+                    ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('Line') -and
+                    ([System.Collections.IDictionary]$InputObject.TargetObject).Contains('LineText')
+
+    $file = if ($useTargetObject) {
+        "$($InputObject.TargetObject.File)"
+    } elseif (.ScriptName) {
+        "$($InvocationInfo.ScriptName)"
+    }
+
+    $line = if ($useTargetObject) {
+        $InputObject.TargetObject.Line
+    } else {
+        $InvocationInfo.ScriptLineNumber
+    }
+
+    if ($useTargetObject) {
+        "file=$file,line=$line"
+    } else {
+        $column = $InvocationInfo.OffsetInLine
+
+        $Length = $InvocationInfo.PositionMessage.Split($newline)[-1].Substring(1).Trim().Length
+        $endColumn = $column + $Length
+        "file=$file,line=$line,col=$column,endColumn=$endColumn"
+    }
+}
diff --git a/source/private/WrapString.ps1 b/source/private/WrapString.ps1
index ed52337..e825dd1 100644
--- a/source/private/WrapString.ps1
+++ b/source/private/WrapString.ps1
@@ -1,13 +1,3 @@
-$script:AnsiPattern = "[\u001b\u009b][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d\/#&.:=?%@~_]*)*)?(?:\u001b\u005c|\u0007))|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))"
-$script:AnsiRegex = [Regex]::new($AnsiPattern, "Compiled");
-function MeasureString {
-    [CmdletBinding()]
-    param(
-        [string]$InputObject
-    )
-    $AnsiRegex.Replace($InputObject, '').Length
-}
-
 
 filter WrapString {
     [CmdletBinding()]
diff --git a/source/public/ConvertTo-ConciseErrorView.ps1 b/source/public/ConvertTo-ConciseErrorView.ps1
index f36b53b..390e6b4 100644
--- a/source/public/ConvertTo-ConciseErrorView.ps1
+++ b/source/public/ConvertTo-ConciseErrorView.ps1
@@ -15,7 +15,7 @@ function ConvertTo-ConciseErrorView {
                 $local:resetColor = "<<<"
             }
 
-            $message = GetConciseViewPositionMessage -InputObject $InputObject
+            $message = GetConciseMessage -InputObject $InputObject
 
             if ($InputObject.PSMessageDetails) {
                 $message = $errorColor + ' : ' + $InputObject.PSMessageDetails + $message
diff --git a/source/public/ConvertTo-DetailedErrorView.ps1 b/source/public/ConvertTo-DetailedErrorView.ps1
index 555ba16..437a1ba 100644
--- a/source/public/ConvertTo-DetailedErrorView.ps1
+++ b/source/public/ConvertTo-DetailedErrorView.ps1
@@ -22,5 +22,10 @@ filter ConvertTo-DetailedErrorView {
     begin { ResetColor }
     process {
         $newline + (GetListRecursive $InputObject) + $newline
+        if ($Env:GITHUB_ACTIONS) {
+            Write-Host "::error $(GetGoogleWorkflowPositionMesage),title=$(GetErrorTitle $InputObject)::$(GetErrorMessage $InputObject)"
+        } elseif ($Env:TF_BUILD) {
+            Write-Host "##vso[task.logissue type=error;$(GetAzurePipelinesPositionMesage)]$(GetErrorTitle $InputObject): $(GetErrorMessage $InputObject)"
+        }
     }
-}
+}
\ No newline at end of file

From c2e893719c9b3fce20ca4c87ab65be3d0eb13a0e Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 7 Oct 2024 01:53:14 -0400
Subject: [PATCH 13/15] Clean up PSScriptAnalyzer

---
 Reference/default.ps1                         |  2 +-
 ScriptAnalyzerSettings.psd1                   |  4 ++++
 source/private/GetConciseMessage.ps1          |  4 +---
 source/private/GetErrorMessage.ps1            | 20 +++++++++----------
 source/public/ConvertTo-DetailedErrorView.ps1 |  4 ++--
 source/public/ConvertTo-YamlErrorView.ps1     |  1 +
 6 files changed, 19 insertions(+), 16 deletions(-)
 create mode 100644 ScriptAnalyzerSettings.psd1

diff --git a/Reference/default.ps1 b/Reference/default.ps1
index 6108b80..cae60b6 100644
--- a/Reference/default.ps1
+++ b/Reference/default.ps1
@@ -150,7 +150,7 @@ function Get-ConciseViewPositionMessage {
         $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
 
         $windowWidth = 120
-        if ($Host.UI.RawUI -ne $null) {
+        if ($null -ne $Host.UI.RawUI) {
             $windowWidth = $Host.UI.RawUI.WindowSize.Width
         }
 
diff --git a/ScriptAnalyzerSettings.psd1 b/ScriptAnalyzerSettings.psd1
new file mode 100644
index 0000000..caca8da
--- /dev/null
+++ b/ScriptAnalyzerSettings.psd1
@@ -0,0 +1,4 @@
+@{
+    Severity     = @('Error', 'Warning')
+    ExcludeRules = @('PSAvoidGlobalVars')
+}
diff --git a/source/private/GetConciseMessage.ps1 b/source/private/GetConciseMessage.ps1
index a9ac955..707194f 100644
--- a/source/private/GetConciseMessage.ps1
+++ b/source/private/GetConciseMessage.ps1
@@ -8,7 +8,6 @@ filter GetConciseMessage {
     $err = $InputObject
     $posmsg = ''
     $headerWhitespace = ''
-    $offsetWhitespace = ''
     $message = ''
     $prefix = ''
 
@@ -90,7 +89,6 @@ filter GetConciseMessage {
         }
 
         $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
-        $offsetWhitespace = ' ' * $offsetInLine
         $prefix = "${accentColor}${headerWhitespace}     ${verticalBar} ${errorColor}"
         if ($highlightLine -ne '') {
             $posMsg += "${prefix}${highlightLine}${newline}"
@@ -122,7 +120,7 @@ filter GetConciseMessage {
         $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
 
         $windowWidth = 120
-        if ($Host.UI.RawUI -ne $null) {
+        if ($null -ne $Host.UI.RawUI) {
             $windowWidth = $Host.UI.RawUI.WindowSize.Width
         }
 
diff --git a/source/private/GetErrorMessage.ps1 b/source/private/GetErrorMessage.ps1
index e7d77cc..515abfe 100644
--- a/source/private/GetErrorMessage.ps1
+++ b/source/private/GetErrorMessage.ps1
@@ -6,18 +6,18 @@ filter GetErrorTitle {
         [System.Management.Automation.ErrorRecord]
         $InputObject
     )
-    if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
-        if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+    if ($InputObject.ErrorDetails -and $InputObject.ErrorDetails.Message) {
+        $InputObject.ErrorDetails.Message
+    } else {
+        if ($InputObject.CategoryInfo.Category -eq 'ParserError' -and $InputObject.Exception.Message.Contains("~$newline")) {
             # need to parse out the relevant part of the pre-rendered positionmessage
-            $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
-        } elseif ($err.Exception) {
-            $err.Exception.Message
-        } elseif ($err.Message) {
-            $err.Message
+            $InputObject.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+        } elseif ($InputObject.Exception) {
+            $InputObject.Exception.Message
+        } elseif ($InputObject.Message) {
+            $InputObject.Message
         } else {
-            $err.ToString()
+            $InputObject.ToString()
         }
-    } else {
-        $err.ErrorDetails.Message
     }
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-DetailedErrorView.ps1 b/source/public/ConvertTo-DetailedErrorView.ps1
index 437a1ba..539b43f 100644
--- a/source/public/ConvertTo-DetailedErrorView.ps1
+++ b/source/public/ConvertTo-DetailedErrorView.ps1
@@ -23,9 +23,9 @@ filter ConvertTo-DetailedErrorView {
     process {
         $newline + (GetListRecursive $InputObject) + $newline
         if ($Env:GITHUB_ACTIONS) {
-            Write-Host "::error $(GetGoogleWorkflowPositionMesage),title=$(GetErrorTitle $InputObject)::$(GetErrorMessage $InputObject)"
+            "::error $(GetGoogleWorkflowPositionMesage),title=$(GetErrorTitle $InputObject)::$(GetErrorMessage $InputObject)"
         } elseif ($Env:TF_BUILD) {
-            Write-Host "##vso[task.logissue type=error;$(GetAzurePipelinesPositionMesage)]$(GetErrorTitle $InputObject): $(GetErrorMessage $InputObject)"
+            "##vso[task.logissue type=error;$(GetAzurePipelinesPositionMesage)]$(GetErrorTitle $InputObject): $(GetErrorMessage $InputObject)"
         }
     }
 }
\ No newline at end of file
diff --git a/source/public/ConvertTo-YamlErrorView.ps1 b/source/public/ConvertTo-YamlErrorView.ps1
index 0e8ce63..089e314 100644
--- a/source/public/ConvertTo-YamlErrorView.ps1
+++ b/source/public/ConvertTo-YamlErrorView.ps1
@@ -5,6 +5,7 @@
         .DESCRIPTION
             This produces valid Yaml output from ErrorRecord you pass to it, recursively.
     #>
+    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'maxDepth')]
     [CmdletBinding()]
     param(
         # The object that you want to convert to YAML

From 1a56d18984b86ee3a76d8f014b40bf013e27085f Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 7 Oct 2024 01:53:59 -0400
Subject: [PATCH 14/15] Additional reference files, just for the record.

---
 Reference/FileInfo.format.ps1xml              | 206 ++++++
 Reference/LegacyErrorView.format.ps1xml       |   1 +
 Reference/OriginalErrorView.format.ps1xml     | 600 ++++++++++++++++++
 Reference/OriginalExceptionView.format.ps1xml |  45 ++
 4 files changed, 852 insertions(+)
 create mode 100644 Reference/FileInfo.format.ps1xml
 create mode 100644 Reference/LegacyErrorView.format.ps1xml
 create mode 100644 Reference/OriginalErrorView.format.ps1xml
 create mode 100644 Reference/OriginalExceptionView.format.ps1xml

diff --git a/Reference/FileInfo.format.ps1xml b/Reference/FileInfo.format.ps1xml
new file mode 100644
index 0000000..745a045
--- /dev/null
+++ b/Reference/FileInfo.format.ps1xml
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Configuration>
+  <ViewDefinitions>
+    <View>
+      <Name>children</Name>
+      <ViewSelectedBy>
+        <TypeName>System.IO.DirectoryInfo</TypeName>
+      </ViewSelectedBy>
+      <GroupBy>
+        <PropertyName>PSParentPath</PropertyName>
+      </GroupBy>
+      <TableControl>
+        <TableHeaders>
+          <TableColumnHeader>
+            <Label>Mode</Label>
+            <Width>7</Width>
+            <Alignment>Left</Alignment>
+          </TableColumnHeader>
+          <TableColumnHeader>
+            <Label>LastWriteTime</Label>
+            <Width>26</Width>
+            <Alignment>Right</Alignment>
+          </TableColumnHeader>
+          <TableColumnHeader>
+            <Label>Length</Label>
+            <Width>14</Width>
+            <Alignment>Right</Alignment>
+          </TableColumnHeader>
+          <TableColumnHeader>
+            <Label>Name</Label>
+            <Alignment>Left</Alignment>
+          </TableColumnHeader>
+        </TableHeaders>
+        <TableRowEntries>
+          <TableRowEntry>
+            <Wrap />
+            <TableColumnItems>
+              <TableColumnItem>
+                <PropertyName>ModeWithoutHardLink</PropertyName>
+              </TableColumnItem>
+              <TableColumnItem>
+                <PropertyName>LastWriteTimeString</PropertyName>
+              </TableColumnItem>
+              <TableColumnItem>
+                <PropertyName>LengthString</PropertyName>
+              </TableColumnItem>
+              <TableColumnItem>
+                <PropertyName>NameString</PropertyName>
+              </TableColumnItem>
+            </TableColumnItems>
+          </TableRowEntry>
+        </TableRowEntries>
+      </TableControl>
+    </View>
+    <View>
+      <Name>childrenWithHardlink</Name>
+      <ViewSelectedBy>
+        <TypeName>System.IO.DirectoryInfo</TypeName>
+      </ViewSelectedBy>
+      <GroupBy>
+        <PropertyName>PSParentPath</PropertyName>
+      </GroupBy>
+      <TableControl>
+        <TableHeaders>
+          <TableColumnHeader>
+            <Label>Mode</Label>
+            <Width>7</Width>
+            <Alignment>Left</Alignment>
+          </TableColumnHeader>
+          <TableColumnHeader>
+            <Label>LastWriteTime</Label>
+            <Width>26</Width>
+            <Alignment>Right</Alignment>
+          </TableColumnHeader>
+          <TableColumnHeader>
+            <Label>Length</Label>
+            <Width>14</Width>
+            <Alignment>Right</Alignment>
+          </TableColumnHeader>
+          <TableColumnHeader>
+            <Label>Name</Label>
+            <Alignment>Left</Alignment>
+          </TableColumnHeader>
+        </TableHeaders>
+        <TableRowEntries>
+          <TableRowEntry>
+            <Wrap />
+            <TableColumnItems>
+              <TableColumnItem>
+                <PropertyName>Mode</PropertyName>
+              </TableColumnItem>
+              <TableColumnItem>
+                <PropertyName>LastWriteTimeString</PropertyName>
+              </TableColumnItem>
+              <TableColumnItem>
+                <PropertyName>LengthString</PropertyName>
+              </TableColumnItem>
+              <TableColumnItem>
+                <PropertyName>NameString</PropertyName>
+              </TableColumnItem>
+            </TableColumnItems>
+          </TableRowEntry>
+        </TableRowEntries>
+      </TableControl>
+    </View>
+    <View>
+      <Name>children</Name>
+      <ViewSelectedBy>
+        <TypeName>System.IO.DirectoryInfo</TypeName>
+      </ViewSelectedBy>
+      <GroupBy>
+        <PropertyName>PSParentPath</PropertyName>
+      </GroupBy>
+      <ListControl>
+        <ListEntries>
+          <ListEntry>
+            <ListItems>
+              <ListItem>
+                <PropertyName>Name</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>CreationTime</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>LastWriteTime</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>LastAccessTime</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>Mode</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>LinkType</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>Target</PropertyName>
+              </ListItem>
+            </ListItems>
+          </ListEntry>
+          <ListEntry>
+            <EntrySelectedBy>
+              <TypeName>System.IO.FileInfo</TypeName>
+            </EntrySelectedBy>
+            <ListItems>
+              <ListItem>
+                <PropertyName>Name</PropertyName>
+              </ListItem>
+              <ListItem>
+                <Label>Length</Label>
+                <PropertyName>LengthString</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>CreationTime</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>LastWriteTime</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>LastAccessTime</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>Mode</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>LinkType</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>Target</PropertyName>
+              </ListItem>
+              <ListItem>
+                <PropertyName>VersionInfo</PropertyName>
+              </ListItem>
+            </ListItems>
+          </ListEntry>
+        </ListEntries>
+      </ListControl>
+    </View>
+    <View>
+      <Name>children</Name>
+      <ViewSelectedBy>
+        <TypeName>System.IO.DirectoryInfo</TypeName>
+      </ViewSelectedBy>
+      <GroupBy>
+        <PropertyName>PSParentPath</PropertyName>
+      </GroupBy>
+      <WideControl>
+        <WideEntries>
+          <WideEntry>
+            <WideItem>
+              <PropertyName>Name</PropertyName>
+            </WideItem>
+          </WideEntry>
+          <WideEntry>
+            <EntrySelectedBy>
+              <TypeName>System.IO.DirectoryInfo</TypeName>
+            </EntrySelectedBy>
+            <WideItem>
+              <PropertyName>Name</PropertyName>
+            </WideItem>
+          </WideEntry>
+        </WideEntries>
+      </WideControl>
+    </View>
+  </ViewDefinitions>
+</Configuration>
\ No newline at end of file
diff --git a/Reference/LegacyErrorView.format.ps1xml b/Reference/LegacyErrorView.format.ps1xml
new file mode 100644
index 0000000..fde1bd3
--- /dev/null
+++ b/Reference/LegacyErrorView.format.ps1xml
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="utf-8"?><Configuration><ViewDefinitions /></Configuration>
\ No newline at end of file
diff --git a/Reference/OriginalErrorView.format.ps1xml b/Reference/OriginalErrorView.format.ps1xml
new file mode 100644
index 0000000..835a083
--- /dev/null
+++ b/Reference/OriginalErrorView.format.ps1xml
@@ -0,0 +1,600 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Configuration>
+  <ViewDefinitions>
+    <View>
+      <Name>GetErrorInstance</Name>
+      <ViewSelectedBy>
+        <TypeName>System.Management.Automation.ErrorRecord#PSExtendedError</TypeName>
+      </ViewSelectedBy>
+      <GroupBy>
+        <PropertyName>PSErrorIndex</PropertyName>
+        <Label>ErrorIndex</Label>
+      </GroupBy>
+      <CustomControl>
+        <CustomEntries>
+          <CustomEntry>
+            <CustomItem>
+              <ExpressionBinding>
+                <ScriptBlock>
+                            Set-StrictMode -Off
+
+                            $maxDepth = 10
+                            $ellipsis = "`u{2026}"
+                            $resetColor = ''
+                            $errorColor = ''
+                            $accentColor = ''
+
+                            if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+                                $resetColor = $PSStyle.Reset
+                                $errorColor = $psstyle.Formatting.Error
+                                $accentColor = $PSStyle.Formatting.FormatAccent
+                            }
+
+                            function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+                                $newline = [Environment]::Newline
+                                $output = [System.Text.StringBuilder]::new()
+                                $prefix = ' ' * $indent
+
+                                $expandTypes = @(
+                                    'Microsoft.Rest.HttpRequestMessageWrapper'
+                                    'Microsoft.Rest.HttpResponseMessageWrapper'
+                                    'System.Management.Automation.InvocationInfo'
+                                )
+
+                                # if object is an Exception, add an ExceptionType property
+                                if ($obj -is [Exception]) {
+                                    $obj | Add-Member -NotePropertyName Type -NotePropertyValue $obj.GetType().FullName -ErrorAction Ignore
+                                }
+
+                                # first find the longest property so we can indent properly
+                                $propLength = 0
+                                foreach ($prop in $obj.PSObject.Properties) {
+                                    if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Name.Length -gt $propLength) {
+                                        $propLength = $prop.Name.Length
+                                    }
+                                }
+
+                                $addedProperty = $false
+                                foreach ($prop in $obj.PSObject.Properties) {
+
+                                    # don't show empty properties or our added property for $error[index]
+                                    if ($prop.Value -ne $null -and $prop.Value -ne [string]::Empty -and $prop.Value.count -gt 0 -and $prop.Name -ne 'PSErrorIndex') {
+                                        $addedProperty = $true
+                                        $null = $output.Append($prefix)
+                                        $null = $output.Append($accentColor)
+                                        $null = $output.Append($prop.Name)
+                                        $propNameIndent = ' ' * ($propLength - $prop.Name.Length)
+                                        $null = $output.Append($propNameIndent)
+                                        $null = $output.Append(' : ')
+                                        $null = $output.Append($resetColor)
+
+                                        $newIndent = $indent + 4
+
+                                        # only show nested objects that are Exceptions, ErrorRecords, or types defined in $expandTypes and types not in $ignoreTypes
+                                        if ($prop.Value -is [Exception] -or $prop.Value -is [System.Management.Automation.ErrorRecord] -or
+                                            $expandTypes -contains $prop.TypeNameOfValue -or ($prop.TypeNames -ne $null -and $expandTypes -contains $prop.TypeNames[0])) {
+
+                                            if ($depth -ge $maxDepth) {
+                                                $null = $output.Append($ellipsis)
+                                            }
+                                            else {
+                                                $null = $output.Append($newline)
+                                                $null = $output.Append((Show-ErrorRecord $prop.Value $newIndent ($depth + 1)))
+                                            }
+                                        }
+                                        # `TargetSite` has many members that are not useful visually, so we have a reduced view of the relevant members
+                                        elseif ($prop.Name -eq 'TargetSite' -and $prop.Value.GetType().Name -eq 'RuntimeMethodInfo') {
+                                            if ($depth -ge $maxDepth) {
+                                                $null = $output.Append($ellipsis)
+                                            }
+                                            else {
+                                                $targetSite = [PSCustomObject]@{
+                                                    Name = $prop.Value.Name
+                                                    DeclaringType = $prop.Value.DeclaringType
+                                                    MemberType = $prop.Value.MemberType
+                                                    Module = $prop.Value.Module
+                                                }
+
+                                                $null = $output.Append($newline)
+                                                $null = $output.Append((Show-ErrorRecord $targetSite $newIndent ($depth + 1)))
+                                            }
+                                        }
+                                        # `StackTrace` is handled specifically because the lines are typically long but necessary so they are left justified without additional indentation
+                                        elseif ($prop.Name -eq 'StackTrace') {
+                                            # for a stacktrace which is usually quite wide with info, we left justify it
+                                            $null = $output.Append($newline)
+                                            $null = $output.Append($prop.Value)
+                                        }
+                                        # Dictionary and Hashtable we want to show as Key/Value pairs, we don't do the extra whitespace alignment here
+                                        elseif ($prop.Value.GetType().Name.StartsWith('Dictionary') -or $prop.Value.GetType().Name -eq 'Hashtable') {
+                                            $isFirstElement = $true
+                                            foreach ($key in $prop.Value.Keys) {
+                                                if ($isFirstElement) {
+                                                    $null = $output.Append($newline)
+                                                }
+
+                                                if ($key -eq 'Authorization') {
+                                                    $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}${ellipsis}${newline}")
+                                                }
+                                                else {
+                                                    $null = $output.Append("${prefix}    ${accentColor}${key} : ${resetColor}$($prop.Value[$key])${newline}")
+                                                }
+
+                                                $isFirstElement = $false
+                                            }
+                                        }
+                                        # if the object implements IEnumerable and not a string, we try to show each object
+                                        # We ignore the `Data` property as it can contain lots of type information by the interpreter that isn't useful here
+                                        elseif (!($prop.Value -is [System.String]) -and $prop.Value.GetType().GetInterface('IEnumerable') -ne $null -and $prop.Name -ne 'Data') {
+
+                                            if ($depth -ge $maxDepth) {
+                                                $null = $output.Append($ellipsis)
+                                            }
+                                            else {
+                                                $isFirstElement = $true
+                                                foreach ($value in $prop.Value) {
+                                                    $null = $output.Append($newline)
+                                                    $valueIndent = ' ' * ($newIndent + 2)
+
+                                                    if ($value -is [Type]) {
+                                                        # Just show the typename instead of it as an object
+                                                        $null = $output.Append("${prefix}${valueIndent}[$($value.ToString())]")
+                                                    }
+                                                    elseif ($value -is [string] -or $value.GetType().IsPrimitive) {
+                                                        $null = $output.Append("${prefix}${valueIndent}${value}")
+                                                    }
+                                                    else {
+                                                        if (!$isFirstElement) {
+                                                            $null = $output.Append($newline)
+                                                        }
+                                                        $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1)))
+                                                    }
+                                                    $isFirstElement = $false
+                                                }
+                                            }
+                                        }
+                                        elseif ($prop.Value -is [Type]) {
+                                            # Just show the typename instead of it as an object
+                                            $null = $output.Append("[$($prop.Value.ToString())]")
+                                        }
+                                        # Anything else, we convert to string.
+                                        # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error
+                                        else {
+                                            $value = $null
+                                            if ([System.Management.Automation.LanguagePrimitives]::TryConvertTo($prop.Value, [string], [ref]$value) -and $value -ne $null)
+                                            {
+                                                if ($prop.Name -eq 'PositionMessage') {
+                                                    $value = $value.Insert($value.IndexOf('~'), $errorColor)
+                                                }
+                                                elseif ($prop.Name -eq 'Message') {
+                                                    $value = $errorColor + $value
+                                                }
+
+                                                $isFirstLine = $true
+                                                if ($value.Contains($newline)) {
+                                                    # the 3 is to account for ' : '
+                                                    $valueIndent = ' ' * ($propLength + 3)
+                                                    # need to trim any extra whitespace already in the text
+                                                    foreach ($line in $value.Split($newline)) {
+                                                        if (!$isFirstLine) {
+                                                            $null = $output.Append("${newline}${prefix}${valueIndent}")
+                                                        }
+                                                        $null = $output.Append($line.Trim())
+                                                        $isFirstLine = $false
+                                                    }
+                                                }
+                                                else {
+                                                    $null = $output.Append($value)
+                                                }
+                                            }
+                                        }
+
+                                        $null = $output.Append($newline)
+                                    }
+                                }
+
+                                # if we had added nested properties, we need to remove the last newline
+                                if ($addedProperty) {
+                                    $null = $output.Remove($output.Length - $newline.Length, $newline.Length)
+                                }
+
+                                $output.ToString()
+                            }
+
+                            # Add back original typename and remove PSExtendedError
+                            if ($_.PSObject.TypeNames.Contains('System.Management.Automation.ErrorRecord#PSExtendedError')) {
+                                $_.PSObject.TypeNames.Add('System.Management.Automation.ErrorRecord')
+                                $null = $_.PSObject.TypeNames.Remove('System.Management.Automation.ErrorRecord#PSExtendedError')
+                            }
+                            elseif ($_.PSObject.TypeNames.Contains('System.Exception#PSExtendedError')) {
+                                $_.PSObject.TypeNames.Add('System.Exception')
+                                $null = $_.PSObject.TypeNames.Remove('System.Exception#PSExtendedError')
+                            }
+
+                            Show-ErrorRecord $_
+                        </ScriptBlock>
+              </ExpressionBinding>
+            </CustomItem>
+          </CustomEntry>
+        </CustomEntries>
+      </CustomControl>
+    </View>
+    <View>
+      <Name>ErrorInstance</Name>
+      <ViewSelectedBy>
+        <TypeName>System.Management.Automation.ErrorRecord</TypeName>
+      </ViewSelectedBy>
+      <OutOfBand />
+      <CustomControl>
+        <CustomEntries>
+          <CustomEntry>
+            <CustomItem>
+              <ExpressionBinding>
+                <ScriptBlock>    $errorColor = ''
+    $commandPrefix = ''
+    if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
+    {
+        $myinv = $_.InvocationInfo
+        if ($Host.UI.SupportsVirtualTerminal) {
+            $errorColor = $PSStyle.Formatting.Error
+        }
+
+        $commandPrefix = if ($myinv -and $myinv.MyCommand) {
+            switch -regex ( $myinv.MyCommand.CommandType )
+            {
+                ([System.Management.Automation.CommandTypes]::ExternalScript)
+                {
+                    if ($myinv.MyCommand.Path)
+                    {
+                        $myinv.MyCommand.Path + ' : '
+                    }
+
+                    break
+                }
+
+                ([System.Management.Automation.CommandTypes]::Script)
+                {
+                    if ($myinv.MyCommand.ScriptBlock)
+                    {
+                        $myinv.MyCommand.ScriptBlock.ToString() + ' : '
+                    }
+
+                    break
+                }
+                default
+                {
+                    if ($myinv.InvocationName -match '^[&amp;\.]?$')
+                    {
+                        if ($myinv.MyCommand.Name)
+                        {
+                            $myinv.MyCommand.Name + ' : '
+                        }
+                    }
+                    else
+                    {
+                        $myinv.InvocationName + ' : '
+                    }
+
+                    break
+                }
+            }
+        }
+        elseif ($myinv -and $myinv.InvocationName)
+        {
+            $myinv.InvocationName + ' : '
+        }
+    }
+
+    $errorColor + $commandPrefix</ScriptBlock>
+              </ExpressionBinding>
+              <ExpressionBinding>
+                <ScriptBlock>    Set-StrictMode -Off
+    $ErrorActionPreference = 'Stop'
+    trap { 'Error found in error view definition: ' + $_.Exception.Message }
+    $newline = [Environment]::Newline
+
+    $resetColor = ''
+    $errorColor = ''
+    $accentColor = ''
+
+    if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+        $resetColor = $PSStyle.Reset
+        $errorColor = $PSStyle.Formatting.Error
+        $accentColor = $PSStyle.Formatting.ErrorAccent
+    }
+
+    function Get-ConciseViewPositionMessage {
+
+        # returns a string cut to last whitespace
+        function Get-TruncatedString($string, [int]$length) {
+
+            if ($string.Length -le $length) {
+                return $string
+            }
+
+            return ($string.Substring(0,$length) -split '\s',-2)[0]
+        }
+
+        $posmsg = ''
+        $headerWhitespace = ''
+        $offsetWhitespace = ''
+        $message = ''
+        $prefix = ''
+
+        # Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
+        # Note that in some versions, this is a Dictionary&lt;,&gt; and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
+        # to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
+        $useTargetObject = $null -ne $err.TargetObject -and
+            $err.TargetObject -is [System.Collections.IDictionary] -and
+            ([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
+            ([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
+
+        # The checks here determine if we show line detailed error information:
+        # - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
+        $isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
+            $err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
+
+        # - check if invocation is a script or multiple lines in the console
+        $isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
+
+        # - check that it's not a script module as expectation is that users don't want to see the line of error within a module
+        $shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
+            $myinv.ScriptName -notmatch '\.psm1$'
+
+        if ($useTargetObject -or $shouldShowLineDetail) {
+
+            if ($useTargetObject) {
+                $posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
+            }
+            elseif ($myinv.ScriptName) {
+                if ($env:TERM_PROGRAM -eq 'vscode') {
+                    # If we are running in vscode, we know the file:line:col links are clickable so we use this format
+                    $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
+                }
+                else {
+                    $posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
+                }
+            }
+            else {
+                $posmsg = "${newline}"
+            }
+
+            if ($useTargetObject) {
+                $scriptLineNumber = $err.TargetObject.Line
+                $scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
+            }
+            else {
+                $scriptLineNumber = $myinv.ScriptLineNumber
+                $scriptLineNumberLength = $myinv.ScriptLineNumber.ToString().Length
+            }
+
+            if ($scriptLineNumberLength -gt 4) {
+                $headerWhitespace = ' ' * ($scriptLineNumberLength - 4)
+            }
+
+            $lineWhitespace = ''
+            if ($scriptLineNumberLength -lt 4) {
+                $lineWhitespace = ' ' * (4 - $scriptLineNumberLength)
+            }
+
+            $verticalBar = '|'
+            $posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
+
+            $highlightLine = ''
+            if ($useTargetObject) {
+                $line = $_.TargetObject.LineText.Trim()
+                $offsetLength = 0
+                $offsetInLine = 0
+            }
+            else {
+                $positionMessage = $myinv.PositionMessage.Split($newline)
+                $line = $positionMessage[1].Substring(1) # skip the '+' at the start
+                $highlightLine = $positionMessage[$positionMessage.Count - 1].Substring(1)
+                $offsetLength = $highlightLine.Trim().Length
+                $offsetInLine = $highlightLine.IndexOf('~')
+            }
+
+            if (-not $line.EndsWith($newline)) {
+                $line += $newline
+            }
+
+            # don't color the whole line
+            if ($offsetLength -lt $line.Length - 1) {
+                $line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
+            }
+
+            $posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
+            $offsetWhitespace = ' ' * $offsetInLine
+            $prefix = "${accentColor}${headerWhitespace}     ${verticalBar} ${errorColor}"
+            if ($highlightLine -ne '') {
+                $posMsg += "${prefix}${highlightLine}${newline}"
+            }
+            $message = "${prefix}"
+        }
+
+        if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
+            if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
+                # need to parse out the relevant part of the pre-rendered positionmessage
+                $message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
+            }
+            elseif ($err.Exception) {
+                $message += $err.Exception.Message
+            }
+            elseif ($err.Message) {
+                $message += $err.Message
+            }
+            else {
+                $message += $err.ToString()
+            }
+        }
+        else {
+            $message += $err.ErrorDetails.Message
+        }
+
+        # if rendering line information, break up the message if it's wider than the console
+        if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') {
+            $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength
+            $prefixVtLength = $prefix.Length - $prefixLength
+
+            # replace newlines in message so it lines up correct
+            $message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
+
+            $windowWidth = 120
+            if ($Host.UI.RawUI -ne $null) {
+                $windowWidth = $Host.UI.RawUI.WindowSize.Width
+            }
+
+            if ($windowWidth -gt 0 -and ($message.Length - $prefixVTLength) -gt $windowWidth) {
+                $sb = [Text.StringBuilder]::new()
+                $substring = Get-TruncatedString -string $message -length ($windowWidth + $prefixVTLength)
+                $null = $sb.Append($substring)
+                $remainingMessage = $message.Substring($substring.Length).Trim()
+                $null = $sb.Append($newline)
+                while (($remainingMessage.Length + $prefixLength) -gt $windowWidth) {
+                    $subMessage = $prefix + $remainingMessage
+                    $substring = Get-TruncatedString -string $subMessage -length ($windowWidth + $prefixVtLength)
+
+                    if ($substring.Length - $prefix.Length -gt 0)
+                    {
+                        $null = $sb.Append($substring)
+                        $null = $sb.Append($newline)
+                        $remainingMessage = $remainingMessage.Substring($substring.Length - $prefix.Length).Trim()
+                    }
+                    else
+                    {
+                        break
+                    }
+                }
+                $null = $sb.Append($prefix + $remainingMessage.Trim())
+                $message = $sb.ToString()
+            }
+
+            $message += $newline
+        }
+
+        $posmsg += "${errorColor}" + $message
+
+        $reason = 'Error'
+        if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
+            $reason = 'Exception'
+        }
+        # MyCommand can be the script block, so we don't want to show that so check if it's an actual command
+        elseif ($myinv.MyCommand -and $myinv.MyCommand.Name -and (Get-Command -Name $myinv.MyCommand -ErrorAction Ignore))
+        {
+            $reason = $myinv.MyCommand
+        }
+        # If it's a scriptblock, better to show the command in the scriptblock that had the error
+        elseif ($err.CategoryInfo.Activity) {
+            $reason = $err.CategoryInfo.Activity
+        }
+        elseif ($myinv.MyCommand) {
+            $reason = $myinv.MyCommand
+        }
+        elseif ($myinv.InvocationName) {
+            $reason = $myinv.InvocationName
+        }
+        elseif ($err.CategoryInfo.Category) {
+            $reason = $err.CategoryInfo.Category
+        }
+        elseif ($err.CategoryInfo.Reason) {
+            $reason = $err.CategoryInfo.Reason
+        }
+
+        $errorMsg = 'Error'
+
+        "${errorColor}${reason}: ${posmsg}${resetcolor}"
+    }
+
+    $myinv = $_.InvocationInfo
+    $err = $_
+    if (!$myinv -and $_.ErrorRecord -and $_.ErrorRecord.InvocationInfo) {
+        $err = $_.ErrorRecord
+        $myinv = $err.InvocationInfo
+    }
+
+    if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQualifiedErrorId -eq 'NativeCommandError') {
+        return "${errorColor}$($err.Exception.Message)${resetcolor}"
+    }
+
+    if ($ErrorView -eq 'DetailedView') {
+        $message = Get-Error | Out-String
+        return "${errorColor}${message}${resetcolor}"
+    }
+
+    if ($ErrorView -eq 'CategoryView') {
+        $message = $err.CategoryInfo.GetMessage()
+        return "${errorColor}${message}${resetcolor}"
+    }
+
+    $posmsg = ''
+    if ($ErrorView -eq 'ConciseView') {
+        $posmsg = Get-ConciseViewPositionMessage
+    }
+    elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) {
+        $posmsg = $myinv.PositionMessage
+        if ($posmsg -ne '') {
+            $posmsg = $newline + $posmsg
+        }
+    }
+
+    if ($err.PSMessageDetails) {
+        $posmsg = ' : ' +  $err.PSMessageDetails + $posmsg
+    }
+
+    if ($ErrorView -eq 'ConciseView') {
+        $recommendedAction = $_.ErrorDetails.RecommendedAction
+        if (-not [String]::IsNullOrWhiteSpace($recommendedAction)) {
+            $recommendedAction = $newline +
+                ${errorColor} +
+                '  Recommendation: ' +
+                $recommendedAction +
+                ${resetcolor}
+        }
+
+        if ($err.PSMessageDetails) {
+            $posmsg = "${errorColor}${posmsg}"
+        }
+        return $posmsg + $recommendedAction
+    }
+
+    $indent = 4
+
+    $errorCategoryMsg = $err.ErrorCategory_Message
+
+    if ($null -ne $errorCategoryMsg)
+    {
+        $indentString = '+ CategoryInfo          : ' + $err.ErrorCategory_Message
+    }
+    else
+    {
+        $indentString = '+ CategoryInfo          : ' + $err.CategoryInfo
+    }
+
+    $posmsg += $newline + $indentString
+
+    $indentString = "+ FullyQualifiedErrorId : " + $err.FullyQualifiedErrorId
+    $posmsg += $newline + $indentString
+
+    $originInfo = $err.OriginInfo
+
+    if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName))
+    {
+        $indentString = "+ PSComputerName        : " + $originInfo.PSComputerName
+        $posmsg += $newline + $indentString
+    }
+
+    $finalMsg = if ($err.ErrorDetails.Message) {
+        $err.ErrorDetails.Message + $posmsg
+    } else {
+        $err.Exception.Message + $posmsg
+    }
+
+    "${errorColor}${finalMsg}${resetcolor}"</ScriptBlock>
+              </ExpressionBinding>
+            </CustomItem>
+          </CustomEntry>
+        </CustomEntries>
+      </CustomControl>
+    </View>
+  </ViewDefinitions>
+</Configuration>
\ No newline at end of file
diff --git a/Reference/OriginalExceptionView.format.ps1xml b/Reference/OriginalExceptionView.format.ps1xml
new file mode 100644
index 0000000..d6e176d
--- /dev/null
+++ b/Reference/OriginalExceptionView.format.ps1xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Configuration>
+  <ViewDefinitions>
+    <View>
+      <Name>GetErrorInstance</Name>
+      <ViewSelectedBy>
+        <TypeName>System.Management.Automation.ErrorRecord#PSExtendedError</TypeName>
+      </ViewSelectedBy>
+      <GroupBy>
+        <PropertyName>PSErrorIndex</PropertyName>
+        <Label>ErrorIndex</Label>
+      </GroupBy>
+      <CustomControl>
+        <CustomEntries>
+          <CustomEntry>
+            <CustomItem>
+              <ExpressionBinding>
+                <ScriptBlock>
+                            Set-StrictMode -Off
+
+                            $maxDepth = 10
+                            $ellipsis = "`u{2026}"
+                            $resetColor = ''
+                            $errorColor = ''
+                            $accentColor = ''
+
+                            if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) {
+                                $resetColor = $PSStyle.Reset
+                                $errorColor = $psstyle.Formatting.Error
+                                $accentColor = $PSStyle.Formatting.FormatAccent
+                            }
+
+                            function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) {
+                                $newline = [Environment]::Newline
+                                $output = [System.Text.StringBuilder]::new()
+                                $prefix = ' ' * $indent
+
+                                $expandTypes = @(
+                                    'Microsoft.Rest.HttpRequestMessageWrapper'
+                                    'Microsoft.Rest.HttpResponseMessageWrapper'
+                                    'System.Management.Automation.InvocationInfo'
+                                )
+
+                                # if object is an Exception, add an ExceptionType property
+$
\ No newline at end of file

From 82f2d6f898dcfb73aeb76d42480883f8a1340e80 Mon Sep 17 00:00:00 2001
From: Joel Bennett <Jaykul@HuddledMasses.org>
Date: Mon, 7 Oct 2024 01:58:25 -0400
Subject: [PATCH 15/15] Fix workflow

---
 .github/workflows/build.yml | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 94cb4da..f7b7d82 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -20,6 +20,10 @@ jobs:
         submodules: true
         fetch-depth: 0
 
+    - name: earthly +build
+      if: github.ref != 'refs/heads/main'
+      run: earthly --strict +build
+
     - name: earthly +test
       if: github.ref != 'refs/heads/main'
       run: earthly --strict +test
@@ -34,18 +38,18 @@ jobs:
 
     - uses: actions/upload-artifact@v4
       with:
-        name: ModuleBuilder
-        path: Modules/ModuleBuilder
+        name: ErrorView
+        path: Modules/ErrorView
 
     - uses: actions/upload-artifact@v4
       with:
         name: TestResults
-        path: Modules/ModuleBuilder-TestResults
+        path: Modules/ErrorView-TestResults
 
     - uses: actions/upload-artifact@v4
       with:
         name: Packages
-        path: Modules/ModuleBuilder-Packages
+        path: Modules/ErrorView-Packages
 
     - name: Upload Tests
       uses: actions/upload-artifact@v4
@@ -68,8 +72,8 @@ jobs:
     - name: Download Build Output
       uses: actions/download-artifact@v4
       with:
-        name: ModuleBuilder
-        path: Modules/ModuleBuilder
+        name: ErrorView
+        path: Modules/ErrorView
     - name: Download Pester Tests
       uses: actions/download-artifact@v4
       with:
@@ -83,15 +87,15 @@ jobs:
     - uses: PoshCode/Actions/install-requiredmodules@v1
     - uses: PoshCode/Actions/pester@v1
       with:
-        codeCoveragePath: Modules/ModuleBuilder
-        moduleUnderTest: ModuleBuilder
+        codeCoveragePath: Modules/ErrorView
+        moduleUnderTest: ErrorView
         additionalModulePaths: ${{github.workspace}}/Modules
     - name: Publish Test Results
       uses: zyborg/dotnet-tests-report@v1
       with:
         test_results_path: results.xml
     - name: Upload Results
-      uses: actions/upload-artifact@v2
+      uses: actions/upload-artifact@v4
       with:
         name: Pester Results
         path: ${{github.workspace}}/*.xml