private/uitool/Show-UiToolHelp.ps1

<#
.SYNOPSIS
    Displays help for a command in New-UiTool output panel.
#>

function Show-UiToolHelp {
    [CmdletBinding()]
    param(
        [string]$CommandName,

        [string]$CommandDisplayName,

        [string]$CommandDefinition,

        [string]$FunctionFile
    )

    $session = Get-UiSession
    $def = $session.PSBase.CurrentDefinition

    if (!$CommandName -and $def) {
        $CommandName = $def.CommandName
        $CommandDisplayName = $def.DisplayName
        $CommandDefinition = $def.CommandDefinition
        $FunctionFile = $def.FunctionFile
    }

    if (!$CommandName) {
        Write-Host "No command specified." -ForegroundColor Red
        return
    }

    $displayName = if ($CommandDisplayName) { $CommandDisplayName } else { $CommandName }

    # Help written in markdown keeps its links and [!NOTE] tags in the Text, and the output window would print them as is
    $writeHelpText = {
        param([string[]]$Text, [string]$Indent)

        $plain = ConvertFrom-UiHelpMarkdown -Text $Text -Plain
        if (!$plain) { return }

        # Installed cmdlet help separates some paragraphs with a lone U+0080, and Trim doesn't count that as space
        $plain = [regex]::Replace($plain.Replace("`r", ''), '[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]', '')

        # Collapse blank runs to one
        $lastBlank = $true
        foreach ($line in $plain.Split("`n")) {
            $blank = !$line.Trim()
            if ($blank -and $lastBlank) { continue }
            Write-Host $(if ($blank) { '' } else { "$Indent$line" })
            $lastBlank = $blank
        }
    }

    # Installed help leaves Type empty and puts the name in parameterValue
    $typeLabel = {
        param($Parameter)
        if ($Parameter.Type.Name) { return $Parameter.Type.Name }
        if ($Parameter.parameterValue) { return ([string]$Parameter.parameterValue).Split('.')[-1] }
        'Object'
    }

    # Installed help leaves Code empty and puts it all in introduction
    $writeExample = {
        param($Example)

        $title = ([string]$Example.Title).Trim().Trim('-').Trim()
        Write-Host " $(ConvertFrom-UiHelpMarkdown -Text $title -Plain)" -ForegroundColor Cyan
        if ($Example.Code) { Write-Host " $($Example.Code)" -ForegroundColor Green }

        # Every empty entry in introduction is a paragraph break of its own
        $intro  = ((@($Example.introduction) | ForEach-Object { $_.Text }) -join "`n").Replace("`r", '')
        $intro  = [regex]::Replace($intro, '[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]', '')
        $intro  = [regex]::Replace($intro, '\n[ \t]*(\n[ \t]*)+', "`n`n")
        $pieces = @([regex]::Split($intro, '(?s)(```[^\n]*\n.*?```)') | Where-Object { $_.Trim() })

        for ($i = 0; $i -lt $pieces.Count; $i++) {
            if ($i -gt 0) { Write-Host '' }
            if ($pieces[$i] -match '(?s)^```[^\n]*\n(.*?)```$') {
                foreach ($codeLine in $Matches[1].TrimEnd().Split("`n")) {
                    Write-Host " $($codeLine.TrimEnd())" -ForegroundColor Green
                }
            }
            else { & $writeHelpText $pieces[$i].Trim() ' ' }
        }
        if ($Example.Remarks) { & $writeHelpText @($Example.Remarks | ForEach-Object { $_.Text }) ' ' }
        Write-Host ""
    }

    Write-Host "=== $displayName ===" -ForegroundColor Cyan
    Write-Host ""

    # Get-Help can't see a local function or a file's function from this runspace until it's loaded here
    if ($FunctionFile -or $CommandDefinition) {
        $help = $null
        try {
            # The whole file keeps help written above the function keyword
            if ($FunctionFile) { . $FunctionFile }
            else {
                $funcBlock = [scriptblock]::Create("function $CommandName {`n$CommandDefinition`n}")
                . $funcBlock
            }
            $help = Get-Help $CommandName -Full -ErrorAction SilentlyContinue
        }
        catch { Write-Debug "Help retrieval failed: $_" }

        if ($help -and $help.Description) {
            if ($help.Synopsis) {
                Write-Host "SYNOPSIS:" -ForegroundColor Yellow
                & $writeHelpText $help.Synopsis ' '
                Write-Host ""
            }
            if ($help.Description) {
                Write-Host "DESCRIPTION:" -ForegroundColor Yellow
                & $writeHelpText @($help.Description | ForEach-Object { $_.Text }) ' '
                Write-Host ""
            }
            if ($help.parameters.parameter) {
                Write-Host "PARAMETERS:" -ForegroundColor Yellow
                $help.parameters.parameter | ForEach-Object {
                    Write-Host " -$($_.Name) <$(& $typeLabel $_)>" -ForegroundColor Green
                    if ($_.Description) {
                        & $writeHelpText @($_.Description | ForEach-Object { $_.Text }) ' '
                    }
                    Write-Host ""
                }
            }
        }
        else {
            Write-Host "This is a locally-defined function." -ForegroundColor Gray
            Write-Host ""
            Write-Host "DEFINITION:" -ForegroundColor Yellow
            Write-Host ""
            $CommandDefinition.Split([char[]]@("`r","`n"), [StringSplitOptions]::RemoveEmptyEntries) |
                ForEach-Object { Write-Host " $_" }
        }
    }
    else {
        # Global command - use standard Get-Help
        $help = Get-Help $CommandName -Full -ErrorAction SilentlyContinue

        # Detect stub help (PS 7+ doesn't ship help files - Description will be empty)
        $hasRealHelp = $help -and $help.Description

        # Try to get online help URI from the definition or the command itself
        $onlineUri = $null
        if ($def -and $def.HelpUri) { $onlineUri = $def.HelpUri }
        else {
            try {
                $cmdObj = Get-Command $CommandName -ErrorAction SilentlyContinue
                if ($cmdObj.HelpUri) { $onlineUri = $cmdObj.HelpUri }
            } catch { Write-Debug "Could not resolve HelpUri for ${CommandName}: $_" }
        }

        if (!$hasRealHelp) {
            Write-Host "Help files not installed for this command." -ForegroundColor Yellow
            Write-Host "Run " -NoNewline -ForegroundColor Gray
            Write-Host "Update-Help" -NoNewline -ForegroundColor Cyan
            Write-Host " to enable full offline help." -ForegroundColor Gray
            Write-Host ""
            
            # Show parameter names/types as a quick reference
            if ($help.parameters.parameter) {
                Write-Host "PARAMETERS:" -ForegroundColor Yellow
                $help.parameters.parameter | ForEach-Object {
                    Write-Host " -$($_.Name) <$(& $typeLabel $_)>" -ForegroundColor Green
                    if ($_.Required -eq 'true') {
                        Write-Host " Required: Yes" -ForegroundColor Gray
                    }
                }
                Write-Host ""
            }

            # Offer to open online help if a URI is available
            if ($onlineUri) {
                $choices = @(
                    [System.Management.Automation.Host.ChoiceDescription]::new('&Open Online Help', 'Opens the documentation in your default browser')
                    [System.Management.Automation.Host.ChoiceDescription]::new('&Close', 'Dismiss')
                )
                $result = $host.UI.PromptForChoice('Online Help Available', "Open documentation for $displayName in your browser?", $choices, 0)
                if ($result -eq 0) {
                    try { Start-Process $onlineUri }
                    catch { Write-Host "Could not open browser: $_" -ForegroundColor Red }
                }
            }
        }
        else {
            if ($help.Synopsis) {
                Write-Host "SYNOPSIS:" -ForegroundColor Yellow
                & $writeHelpText $help.Synopsis ' '
                Write-Host ""
            }
            if ($help.Description) {
                Write-Host "DESCRIPTION:" -ForegroundColor Yellow
                & $writeHelpText @($help.Description | ForEach-Object { $_.Text }) ' '
                Write-Host ""
            }
            if ($help.parameters.parameter) {
                Write-Host "PARAMETERS:" -ForegroundColor Yellow
                $help.parameters.parameter | ForEach-Object {
                    Write-Host " -$($_.Name) <$(& $typeLabel $_)>" -ForegroundColor Green
                    if ($_.Description) {
                        & $writeHelpText @($_.Description | ForEach-Object { $_.Text }) ' '
                    }
                    if ($_.Required -eq 'true') {
                        Write-Host " Required: Yes" -ForegroundColor Gray
                    }
                    Write-Host ""
                }
            }
            if ($help.Examples.Example) {
                Write-Host "EXAMPLES:" -ForegroundColor Yellow
                foreach ($example in $help.Examples.Example) { & $writeExample $example }
            }
        }
    }
}