private/Menu.ps1

# FR-018: interactive checklist menu. Supports two equivalent ways to select an item -
# a direct shortcut keypress (case-insensitive) or Up/Down arrow navigation + Enter. Renders
# the banner (FR-022) above the list on every screen, not only once.

function Show-OctaMenu {
    <#
        $Items: array of @{ Key = '1'; Label = 'Telemetry'; Description = '...' }
        Returns the Key of the chosen item.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][object[]]$Items,
        [Parameter(Mandatory)][string]$Header,
        [string]$NavHint = 'Up/Down to navigate - Enter to select - or press the number/letter directly'
    )

    $selectedIndex = 0

    while ($true) {
        Clear-Host
        Show-OctaBanner
        Write-Host $Header
        Write-Host ''
        for ($i = 0; $i -lt $Items.Count; $i++) {
            $item = $Items[$i]
            $pointer = if ($i -eq $selectedIndex) { '>' } else { ' ' }
            Write-Host ("{0} {1} {2,-22}{3}" -f $pointer, $item.Key.ToString().ToUpperInvariant(), $item.Label, $item.Description)
        }
        Write-Host ''
        Write-Host $NavHint

        $key = [Console]::ReadKey($true)
        switch ($key.Key) {
            'UpArrow' { $selectedIndex = ($selectedIndex - 1 + $Items.Count) % $Items.Count }
            'DownArrow' { $selectedIndex = ($selectedIndex + 1) % $Items.Count }
            'Enter' { return $Items[$selectedIndex].Key }
            default {
                $typed = $key.KeyChar.ToString().ToLowerInvariant()
                $match = $Items | Where-Object { $_.Key.ToString().ToLowerInvariant() -eq $typed }
                if ($match) { return $match[0].Key }
            }
        }
    }
}