diff --git a/cli/azd/pkg/ux/internal/console_other.go b/cli/azd/pkg/ux/internal/console_other.go new file mode 100644 index 00000000000..6d5d2ee82f2 --- /dev/null +++ b/cli/azd/pkg/ux/internal/console_other.go @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +//go:build !windows + +package internal + +import "os" + +// disableVirtualTerminalInput is a no-op on non-Windows platforms. +func disableVirtualTerminalInput(_ *os.File) error { + return nil +} diff --git a/cli/azd/pkg/ux/internal/console_windows.go b/cli/azd/pkg/ux/internal/console_windows.go new file mode 100644 index 00000000000..8e2f9011e86 --- /dev/null +++ b/cli/azd/pkg/ux/internal/console_windows.go @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +//go:build windows + +package internal + +import ( + "os" + + "golang.org/x/sys/windows" +) + +const enableVirtualTerminalInput uint32 = 0x0200 + +// disableVirtualTerminalInput clears the ENABLE_VIRTUAL_TERMINAL_INPUT +// console mode flag so that Windows delivers native virtual-key codes +// (KEY_EVENT_RECORD with wVirtualKeyCode) instead of ANSI escape +// sequences for arrow keys and other special keys. +// +// The survey library's Windows RuneReader expects native VK codes +// (it checks unicodeChar == 0, then switches on wVirtualKeyCode). +// When ENABLE_VIRTUAL_TERMINAL_INPUT is set — as it is by default in +// Windows Terminal, PowerShell 7, VS Code, and Ghostty — the console +// emits ESC [ A / ESC [ B / etc. instead, which leak through as +// literal characters. +// +// This function is called after SetTermMode() and its effect is +// reversed when RestoreTermMode() restores the original console mode. +func disableVirtualTerminalInput(f *os.File) error { + h := windows.Handle(f.Fd()) + + var mode uint32 + if err := windows.GetConsoleMode(h, &mode); err != nil { + return err + } + + if mode&enableVirtualTerminalInput == 0 { + return nil // VTI not set, nothing to do + } + + return windows.SetConsoleMode(h, mode&^enableVirtualTerminalInput) +} diff --git a/cli/azd/pkg/ux/internal/input.go b/cli/azd/pkg/ux/internal/input.go index d41279bd163..0104700f9c9 100644 --- a/cli/azd/pkg/ux/internal/input.go +++ b/cli/azd/pkg/ux/internal/input.go @@ -97,6 +97,17 @@ func (i *Input) ReadInput(ctx context.Context, config *InputConfig, handler KeyP errChan <- err return } + + // On Windows, clear ENABLE_VIRTUAL_TERMINAL_INPUT so the + // console delivers native virtual-key codes instead of ANSI + // escape sequences. The survey library's RuneReader expects + // VK codes for arrow key dispatch. RestoreTermMode() will + // restore the original console mode when input completes. + if err := disableVirtualTerminalInput(os.Stdin); err != nil { + // Non-fatal: worst case is the pre-existing ANSI leak. + _ = err + } + defer func() { if err := rr.RestoreTermMode(); err != nil { log.Printf("Error restoring terminal mode: %v\n", err)