feat: Add help popup with keyboard shortcuts triggered by "?"

This commit is contained in:
n loewen (aider) 2025-06-08 02:35:03 +01:00
parent 8eaad2ae49
commit 78eaaf35e2
1 changed files with 106 additions and 1 deletions

107
gtm
View File

@ -145,6 +145,9 @@ class AppState:
search_query: str = ""
search_matches: List[int] = field(default_factory=list) # Line numbers of matches
current_match_idx: int = -1 # Index in search_matches
# Help popup
show_help: bool = False
# --- Actions (Controller) ---
@ -646,6 +649,99 @@ def draw_selection(stdscr, state):
except curses.error:
pass
def draw_help_popup(stdscr, state):
"""Draw a popup with keyboard shortcut help."""
if not state.show_help:
return
# Calculate popup dimensions and position
popup_width = 60
popup_height = 20
popup_x = max(0, (state.width - popup_width) // 2)
popup_y = max(0, (state.height - popup_height) // 2)
# Draw popup border
for y in range(popup_y, popup_y + popup_height):
for x in range(popup_x, popup_x + popup_width):
if y == popup_y or y == popup_y + popup_height - 1:
# Top and bottom borders
try:
stdscr.addch(y, x, curses.ACS_HLINE)
except curses.error:
pass
elif x == popup_x or x == popup_x + popup_width - 1:
# Left and right borders
try:
stdscr.addch(y, x, curses.ACS_VLINE)
except curses.error:
pass
# Draw corners
try:
stdscr.addch(popup_y, popup_x, curses.ACS_ULCORNER)
stdscr.addch(popup_y, popup_x + popup_width - 1, curses.ACS_URCORNER)
stdscr.addch(popup_y + popup_height - 1, popup_x, curses.ACS_LLCORNER)
stdscr.addch(popup_y + popup_height - 1, popup_x + popup_width - 1, curses.ACS_LRCORNER)
except curses.error:
pass
# Draw title
title = " Keyboard Shortcuts "
title_x = popup_x + (popup_width - len(title)) // 2
try:
stdscr.addstr(popup_y, title_x, title, curses.A_BOLD)
except curses.error:
pass
# Define help content
help_items = [
("Navigation", ""),
("j / Down", "Scroll down"),
("k / Up", "Scroll up"),
("Space / Page Down", "Page down"),
("b / Page Up", "Page up"),
("h / Left", "Focus left pane"),
("l / Right", "Focus right pane"),
("", ""),
("Features", ""),
("/ (slash)", "Search in file"),
("n", "Next search match"),
("N", "Previous search match"),
("c", "Next change"),
("C", "Previous change"),
("s", "Toggle sidebar"),
("w", "Toggle line wrapping"),
("L", "Toggle line numbers"),
("q / Esc", "Quit"),
("? (question mark)", "Show/hide this help")
]
# Draw help content
for i, (key, desc) in enumerate(help_items):
y = popup_y + 2 + i
if y < popup_y + popup_height - 1:
if key == "Navigation" or key == "Features":
# Section headers
try:
stdscr.addstr(y, popup_x + 2, key, curses.A_BOLD | curses.A_UNDERLINE)
except curses.error:
pass
elif key:
# Key and description
try:
stdscr.addstr(y, popup_x + 2, key, curses.A_BOLD)
stdscr.addstr(y, popup_x + 16, desc)
except curses.error:
pass
# Draw footer
footer = " Press any key to close "
footer_x = popup_x + (popup_width - len(footer)) // 2
try:
stdscr.addstr(popup_y + popup_height - 1, footer_x, footer, curses.A_BOLD)
except curses.error:
pass
def draw_status_bars(stdscr, state):
# We'll use height-1 for the single status bar
visible_height = state.height - 1
@ -780,6 +876,7 @@ def draw_ui(stdscr, state):
draw_divider(stdscr, state)
draw_status_bars(stdscr, state)
draw_selection(stdscr, state)
draw_help_popup(stdscr, state)
stdscr.refresh()
# --- Input Handling Functions (Controller) ---
@ -972,6 +1069,10 @@ def handle_mouse_input(stdscr, state: AppState) -> AppState:
return state
def handle_keyboard_input(key, state: AppState) -> AppState:
# If help popup is open, any key closes it
if state.show_help:
return replace(state, show_help=False)
# If in search mode, handle search input
if state.search_mode:
if key == 27: # Escape key - exit search mode
@ -993,10 +1094,14 @@ def handle_keyboard_input(key, state: AppState) -> AppState:
if key in [ord('q')]:
return replace(state, should_exit=True)
elif key == 27: # Escape key
if state.is_selecting:
if state.show_help:
return replace(state, show_help=False)
elif state.is_selecting:
return replace(state, is_selecting=False, selection_start_coord=None, selection_end_coord=None)
else:
return replace(state, should_exit=True)
elif key == ord('?'): # Toggle help popup
return replace(state, show_help=not state.show_help)
elif key == ord('/'): # Start search
return replace(state, search_mode=True, search_query="")
elif key == ord('s'):