Private/Show-rwCLIMenu.ps1

function Show-rwCLIMenu {
    param(
        [string]$Title,
        [string[]]$Options,
        [ValidateSet('Single','Multiple','YesNo')]
        [string]$OutputMode,
        [int]$DefaultSelection = -1,
        [string]$CancelLabel = 'Cancel'
    )

    switch ($OutputMode) {
        'Single'   { return Show-rwCLISingle   -Title $Title -Options $Options -DefaultSelection $DefaultSelection -CancelLabel $CancelLabel }
        'Multiple' { return Show-rwCLIMultiple -Title $Title -Options $Options -CancelLabel $CancelLabel }
        'YesNo'    { return Show-rwPromptYesNo -Title $Title }
        default    { throw "Unexpected OutputMode: $OutputMode" }
    }
}

function Show-rwCLISingle {
    param(
        [string]$Title,
        [string[]]$Options,
        [int]$DefaultSelection = -1,
        [string]$CancelLabel = 'Cancel'
    )

    $separator = '=' * 51
    while ($true) {
        if ($Host.UI.RawUI -and -not $env:CI) {
            try { Clear-Host } catch { Write-Verbose "Clear-Host failed" }
        }

        Write-Host $separator
        Write-Host " $Title"
        Write-Host $separator

        for ($i = 0; $i -lt $Options.Count; $i++) {
            $marker = if ($i -eq $DefaultSelection) { '*' }
            $display = $Options[$i] -replace '[\[\]]', '[$&]'
            Write-Host "[$($i+1)]$marker $display"
        }

        Write-Host "[Q] $CancelLabel"
        Write-Host $separator
        $input = Read-Host 'Choose an option'

        if ($input -match '^[Qq]$') {
            Write-Verbose "User cancelled single menu"
            return $null
        }

        if ($input -match '^\d+$') {
            $index = [int]$input - 1
            if ($index -ge 0 -and $index -lt $Options.Count) {
                Write-Verbose "User selected: $($Options[$index])"
                return $Options[$index]
            }
        }

        Write-Host "`nInvalid selection. Valid range: 1-$($Options.Count)" -ForegroundColor Yellow
        Write-Host 'Press Enter to try again.'
        Read-Host | Out-Null
    }
}

function Show-rwCLIMultiple {
    param(
        [string]$Title,
        [string[]]$Options,
        [string]$CancelLabel = 'Cancel'
    )

    $separator = '=' * 51
    while ($true) {
        if ($Host.UI.RawUI -and -not $env:CI) {
            try { Clear-Host } catch { Write-Verbose "Clear-Host failed" }
        }

        Write-Host $separator
        Write-Host " $Title"
        Write-Host $separator

        for ($i = 0; $i -lt $Options.Count; $i++) {
            $display = $Options[$i] -replace '[\[\]]', '[$&]'
            Write-Host "[$($i+1)] $display"
        }

        Write-Host "`nEnter numbers separated by commas (e.g., 1,3,5) or ranges (e.g., 1-3,5)"
        Write-Host "[Q] $CancelLabel"
        Write-Host $separator
        $input = Read-Host 'Selection'

        if ($input -match '^[Qq]$') {
            Write-Verbose "User cancelled multiple menu"
            return $null
        }

        $indexes = @()
        foreach ($part in ($input -split ',')) {
            $trim = $part.Trim()
            if ($trim -match '^(\d+)-(\d+)$') {
                $start = [int]$Matches[1] - 1
                $end = [int]$Matches[2] - 1
                if ($start -gt $end) { $temp = $start; $start = $end; $end = $temp }
                $indexes += $start..$end
            } elseif ($trim -match '^\d+$') {
                $indexes += [int]$trim - 1
            }
        }

        $validIndexes = @($indexes | Select-Object -Unique | Where-Object { $_ -ge 0 -and $_ -lt $Options.Count })
        $invalidIndexes = @($indexes | Where-Object { $_ -lt 0 -or $_ -ge $Options.Count })

        if ($validIndexes.Count -gt 0 -and $invalidIndexes.Count -eq 0) {
            $selected = @($validIndexes | ForEach-Object { $Options[$_] })
            Write-Verbose "User selected: @($($selected -join ','))"
            return $selected
        }

        if ($invalidIndexes.Count -gt 0) {
            Write-Host "`nInvalid selection(s). Valid range: 1-$($Options.Count)" -ForegroundColor Yellow
        } else {
            Write-Host "`nNo valid selections entered." -ForegroundColor Yellow
        }
        Write-Host 'Press Enter to try again, or Q to cancel.'
        Read-Host | Out-Null
    }
}