Private/Tui/Show-HydrationTuiSelect.ps1

function Show-HydrationTuiSelect {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory)]
        [string]$Title,

        [Parameter(Mandatory)]
        [string[]]$Choices,

        [Parameter()]
        [switch]$Multiple,

        [Parameter()]
        [switch]$ExclusiveFirst
    )

    if ($Choices.Count -eq 0) {
        return [pscustomobject]@{
            Cancelled = $false
            Indices   = @()
        }
    }

    $palette = Get-HydrationTuiPalette
    $selectedIndex = 0
    $checked = [bool[]]::new($Choices.Count)
    $instructions = if ($Multiple) {
        if ($ExclusiveFirst) {
            'Space toggles, A selects All, Enter confirms, q cancels.'
        } else {
            'Space toggles, A selects all, Enter confirms, q cancels.'
        }
    } else {
        'Use arrow keys to navigate, Enter to select, q to cancel.'
    }

    try {
        try { [Console]::CursorVisible = $false } catch { $null = $_ }

        while ($true) {
            Clear-HydrationTuiScreen
            Show-HydrationTuiHeader
            Show-HydrationTuiPanel -Title $Title -Lines @($instructions)

            for ($index = 0; $index -lt $Choices.Count; $index++) {
                $prefix = if ($index -eq $selectedIndex) { ' > ' } else { ' ' }
                $style = if ($index -eq $selectedIndex) { $palette.Highlight + $palette.Bold } elseif ($Multiple -and $checked[$index]) { $palette.Success } else { $palette.Text }
                $choiceText = if ($Multiple) {
                    $box = if ($checked[$index]) { '[x]' } else { '[ ]' }
                    "$prefix$box $($Choices[$index])"
                } else {
                    "$prefix$($Choices[$index])"
                }
                [Console]::WriteLine(('{0}{1}{2}' -f $style, $choiceText, $palette.Reset))
            }

            $action = Get-HydrationTuiKeyAction -KeyInfo ([Console]::ReadKey($true))
            if ($Multiple) {
                $state = Update-HydrationTuiMultiSelectState `
                    -SelectedIndex $selectedIndex `
                    -Checked $checked `
                    -Action $action `
                    -ExclusiveFirst:$ExclusiveFirst

                if ($state.Cancelled) {
                    return [pscustomobject]@{
                        Cancelled = $true
                        Indices   = @()
                    }
                }

                if ($state.Confirmed) {
                    $selected = @()
                    for ($index = 0; $index -lt $state.Checked.Count; $index++) {
                        if ($state.Checked[$index]) { $selected += $index }
                    }

                    return [pscustomobject]@{
                        Cancelled = $false
                        Indices   = $selected
                    }
                }

                $selectedIndex = $state.SelectedIndex
                $checked = [bool[]]$state.Checked
                continue
            }

            switch ($action) {
                'MoveUp' {
                    if ($selectedIndex -gt 0) { $selectedIndex-- } else { $selectedIndex = $Choices.Count - 1 }
                }
                'MoveDown' {
                    if ($selectedIndex -lt ($Choices.Count - 1)) { $selectedIndex++ } else { $selectedIndex = 0 }
                }
                'Confirm' {
                    return [pscustomobject]@{
                        Cancelled = $false
                        Indices   = @($selectedIndex)
                    }
                }
                'Cancel' {
                    return [pscustomobject]@{
                        Cancelled = $true
                        Indices   = @()
                    }
                }
            }
        }
    }
    finally {
        try { [Console]::CursorVisible = $true } catch { $null = $_ }
    }
}