You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A Navigable menu of options provided by the user, to select arguments from a list, leveraging click.readchar() function. This feature is inspired by tools like PyInquirer/Questionary and TUIs like lazygit UX to allow users an entry point to cli app rather than typing arguments.
The feature use case is similar prompt but instead of typing, navigating a list. This could reduce typos errors.
My personal use case is to request all valid files (file with a certain extension) from a path, and make the user choose the file e.g.
click.prompter("Choose a file you want to convert: ", [fileforfileinpath.iterdir() iffile.suffix==".pdf"])
This is a small demo of the selector:
20241031180853.mp4
My current implementation, where prompter is the entrypoint:
fromtypingimportList, Optionalfromclickimportecho, getchar, secho, styleclassColors:
RED="red"YELLOW="yellow"GREEN="green"WHITE="white"classCursors:
SELECTED="→"NON_SELECTED=" "EXIT_CURSOR="✕"CHECKMARK="✓"# Terminal control sequencesCURSOR_UP="\033[1A"# Move cursor up and to start of the lineCLEAR_LINE="\033[K"# Clears the line# Helper TextEXIT_TEXT="Quit (q)"HELP_TEXT="[↑/k] up | [↓/j] down | [enter] select | [q] quit"defprompter(prompt: str, choices: list[str]) ->Optional[str]:
""" Display an interactive prompt with selectable options. Args: prompt: Text to display above the options choices: List of options to choose from """ifnotchoices:
raiseValueError("Options list must contain at least one item.")
ifnotall(choice!=""forchoiceinchoices):
raiseValueError("Options list cannot be empty strings")
secho(prompt, fg=Colors.YELLOW)
selection=_select_option(choices)
# Reprints the selected option next to the prompt_clear_lines()
secho(prompt, fg=Colors.YELLOW, nl=False)
secho(selection, fg=Colors.GREEN, bold=True)
returnselectiondef_clear_lines(n_lines: int=0) ->None:
"""Clears the terminals n lines up"""clear_text=f"{CURSOR_UP}{CLEAR_LINE}"* (n_lines+1) # +1 for help textecho(clear_text, nl=False)
def_update_menu(options: List[str], current_row: int) ->str:
""" Updates the current menu to be displayed according to the cursor position. """display_text=""fori, optioninenumerate(options):
# Highlight selected row with appropriate colorsifi==current_row:
prefix=Cursors.EXIT_CURSORifoption==EXIT_TEXTelseCursors.SELECTEDsel_color=Colors.REDifoption==EXIT_TEXTelseColors.GREENdisplay_text+=style(f"{prefix}{option}\n", fg=sel_color, bold=True)
else:
display_text+=style(f"{Cursors.NON_SELECTED}{option}\n", fg=Colors.WHITE)
# Display help textdisplay_text+=style(HELP_TEXT, fg=Colors.YELLOW, dim=True)
returndisplay_text# Highlight selected row with green [selected], red [exit] or white [non-selected]def_select_option(options: list[str]) ->Optional[str]:
""" Handle user keydown presses for navigating options. """# Create a copy of the list with the quit optionmenu_options=options+ [EXIT_TEXT]
current_row=0# Initial displayecho(_update_menu(menu_options, current_row))
try:
whileTrue:
ch=getchar()
matchch:
# WIN ARROW UP KEY | UNIX ARROW UP KEY | VIM UP KEYcase"\xe0H"|"\x1b[A"|"k":
current_row=max(current_row-1, 0)
current_menu=_update_menu(menu_options, current_row)
_clear_lines(len(menu_options))
echo(current_menu)
# WIN ARROW DOWN KEY | UNIX ARROW DOWN KEY | VIM DOWN KEYcase"\xe0P"|"\x1b[B"|"j":
current_row=min(current_row+1, len(menu_options) -1)
current_menu=_update_menu(menu_options, current_row)
_clear_lines(len(menu_options))
echo(current_menu)
# ENTER KEYcase"\r"|"\n":
selected=menu_options[current_row]
ifselected==EXIT_TEXT:
selected=Nonebreak# EXITcase"q":
selected=Nonebreak# Handle Ctrl+C exit gracefullyexceptKeyboardInterrupt:
selected=None_clear_lines(len(menu_options))
returnselected
A Navigable menu of options provided by the user, to select arguments from a list, leveraging click.readchar() function. This feature is inspired by tools like PyInquirer/Questionary and TUIs like lazygit UX to allow users an entry point to cli app rather than typing arguments.
The feature use case is similar prompt but instead of typing, navigating a list. This could reduce typos errors.
My personal use case is to request all valid files (file with a certain extension) from a path, and make the user choose the file e.g.
This is a small demo of the selector:
20241031180853.mp4
My current implementation, where prompter is the entrypoint: