diff --git a/install.sh b/install.sh index dd2d83a..40509ff 100755 --- a/install.sh +++ b/install.sh @@ -81,6 +81,101 @@ prompt() { printf "%s" "$answer" } +tty_prompt_available() { + [ -t 2 ] && [ -r /dev/tty ] && [ -w /dev/tty ] +} + +menu_select() { + local title="$1" + shift + local options=("$@") + local count="${#options[@]}" + local index=0 + local key="" + local i=0 + local rows=$((count + 3)) + local rendered=0 + + if [ "$count" -eq 0 ]; then + return 1 + fi + + if ! tty_prompt_available; then + ui_title "$title" + i=0 + while [ "$i" -lt "$count" ]; do + printf " %d) %s\n" "$((i + 1))" "${options[$i]}" >&2 + i=$((i + 1)) + done + + while true; do + local raw_choice + raw_choice="$(prompt "Choix" "1")" + case "$raw_choice" in + ''|*[!0-9]*) + ui_warn "Choix invalide: $raw_choice" + ;; + *) + if [ "$raw_choice" -ge 1 ] && [ "$raw_choice" -le "$count" ]; then + printf "%s" "${options[$((raw_choice - 1))]}" + return 0 + fi + ui_warn "Choix invalide: $raw_choice" + ;; + esac + done + fi + + while true; do + if [ "$rendered" -eq 1 ]; then + printf "\033[%dA\033[J" "$rows" >&2 2>/dev/null || true + fi + ui_title "$title" + i=0 + while [ "$i" -lt "$count" ]; do + if [ "$i" -eq "$index" ]; then + printf " %b› %s%b\n" "$C_BOLD$C_CYAN" "${options[$i]}" "$C_RESET" >&2 + else + printf " %s\n" "${options[$i]}" >&2 + fi + i=$((i + 1)) + done + printf "%bUtilise ↑/↓ puis Entrée.%b\n" "$C_DIM" "$C_RESET" >&2 + rendered=1 + + if ! IFS= read -rsn1 key < /dev/tty; then + continue + fi + + case "$key" in + "") + printf "%s" "${options[$index]}" + return 0 + ;; + $'\x1b') + if IFS= read -rsn2 key < /dev/tty; then + case "$key" in + "[A") + if [ "$index" -eq 0 ]; then + index=$((count - 1)) + else + index=$((index - 1)) + fi + ;; + "[B") + if [ "$index" -eq $((count - 1)) ]; then + index=0 + else + index=$((index + 1)) + fi + ;; + esac + fi + ;; + esac + done +} + sanitize_server_name() { local raw="$1" local sanitized @@ -314,25 +409,15 @@ collect_server_inputs() { choose_scope() { local selected - while true; do - ui_title "Scope de configuration" - printf " 1) global (user)\n" >&2 - printf " 2) project (projet courant)\n" >&2 - selected="$(prompt "Choix" "1")" - case "$selected" in - 1) - printf "global" - return - ;; - 2) - printf "project" - return - ;; - *) - ui_warn "Choix invalide: $selected" - ;; - esac - done + selected="$(menu_select "Scope de configuration" "global (user)" "project (projet courant)")" + case "$selected" in + "global (user)") + printf "global" + ;; + *) + printf "project" + ;; + esac } apply_claude_mcp() { @@ -453,48 +538,40 @@ print_header() { printf "%bMCP Install Wizard%b for %b%s%b\n" "$C_BOLD$C_MAGENTA" "$C_RESET" "$C_BOLD" "$BINARY_NAME" "$C_RESET" >&2 printf "%bRelease repo:%b %s\n" "$C_DIM" "$C_RESET" "$RELEASE_REPOSITORY" >&2 ui_line - printf "Choisis une action:\n" >&2 - printf " 1) Installer/mettre a jour le binaire + setup\n" >&2 - printf " 2) Configurer Claude Code (apply direct)\n" >&2 - printf " 3) Configurer Codex (apply direct)\n" >&2 - printf " 4) Generer JSON MCP manuel\n" >&2 - printf " 5) Quitter\n" >&2 + printf "%bSelectionne une action dans le menu interactif.%b\n" "$C_DIM" "$C_RESET" >&2 } main() { - while true; do - print_header - local choice - choice="$(prompt "Choix" "1")" - printf "\n" >&2 + print_header - case "$choice" in - 1) - run_setup_wizard - return - ;; - 2) - apply_claude_mcp - return - ;; - 3) - apply_codex_mcp - return - ;; - 4) - ui_info "JSON MCP genere sur stdout." - print_mcp_json - return - ;; - 5) - ui_warn "Annule." - return - ;; - *) - ui_warn "Choix invalide: $choice" - ;; - esac - done + local action + action="$(menu_select \ + "Choisis une action" \ + "Installer/mettre a jour le binaire + setup" \ + "Configurer Claude Code (apply direct)" \ + "Configurer Codex (apply direct)" \ + "Generer JSON MCP manuel" \ + "Quitter")" + printf "\n" >&2 + + case "$action" in + "Installer/mettre a jour le binaire + setup") + run_setup_wizard + ;; + "Configurer Claude Code (apply direct)") + apply_claude_mcp + ;; + "Configurer Codex (apply direct)") + apply_codex_mcp + ;; + "Generer JSON MCP manuel") + ui_info "JSON MCP genere sur stdout." + print_mcp_json + ;; + *) + ui_warn "Annule." + ;; + esac } main "$@"