feat: Add sidebar toggle with "s" key press

This commit is contained in:
n loewen (aider) 2025-06-07 23:48:25 +01:00
parent a73213a88b
commit 352edde492
1 changed files with 70 additions and 35 deletions

87
gtm2.py
View File

@ -102,6 +102,7 @@ class AppState:
self.show_additions = show_add self.show_additions = show_add
self.show_deletions = show_del self.show_deletions = show_del
self.enable_mouse = mouse self.enable_mouse = mouse
self.show_sidebar = True
self.commits = get_commits(filename) self.commits = get_commits(filename)
self.file_lines = [] self.file_lines = []
@ -137,6 +138,12 @@ class AppState:
else: else:
curses.mousemask(0) curses.mousemask(0)
def toggle_sidebar(self):
self.show_sidebar = not self.show_sidebar
# When hiding sidebar, focus should be on right pane
if not self.show_sidebar:
self.focus = "right"
def move_commit_selection(self, delta): def move_commit_selection(self, delta):
new_idx = self.selected_commit_idx + delta new_idx = self.selected_commit_idx + delta
if 0 <= new_idx < len(self.commits): if 0 <= new_idx < len(self.commits):
@ -203,6 +210,9 @@ class AppState:
# --- Rendering Functions (View) --- # --- Rendering Functions (View) ---
def draw_left_pane(stdscr, state): def draw_left_pane(stdscr, state):
if not state.show_sidebar:
return
if state.selected_commit_idx < state.left_scroll_offset: if state.selected_commit_idx < state.left_scroll_offset:
state.left_scroll_offset = state.selected_commit_idx state.left_scroll_offset = state.selected_commit_idx
elif state.selected_commit_idx >= state.left_scroll_offset + state.height - 2: elif state.selected_commit_idx >= state.left_scroll_offset + state.height - 2:
@ -218,7 +228,9 @@ def draw_left_pane(stdscr, state):
stdscr.attroff(curses.A_REVERSE) stdscr.attroff(curses.A_REVERSE)
def draw_right_pane(stdscr, state): def draw_right_pane(stdscr, state):
right_width = state.width - state.divider_col - 3 # If sidebar is hidden, right pane starts at column 0
right_start = 0 if not state.show_sidebar else state.divider_col + 2
right_width = state.width - right_start - 1
max_scroll = max(0, len(state.file_lines) - (state.height - 2)) max_scroll = max(0, len(state.file_lines) - (state.height - 2))
state.right_scroll_offset = min(state.right_scroll_offset, max_scroll) state.right_scroll_offset = min(state.right_scroll_offset, max_scroll)
@ -257,22 +269,25 @@ def draw_right_pane(stdscr, state):
content = line_info['content'] content = line_info['content']
if line_type == 'added': if line_type == 'added':
stdscr.addstr(display_row, state.divider_col + 2, "+ ", curses.color_pair(3)) stdscr.addstr(display_row, right_start, "+ ", curses.color_pair(3))
stdscr.addnstr(display_row, state.divider_col + 4, content, right_width - 2, curses.color_pair(3)) stdscr.addnstr(display_row, right_start + 2, content, right_width - 2, curses.color_pair(3))
elif line_type == 'deleted': elif line_type == 'deleted':
stdscr.addstr(display_row, state.divider_col + 2, "- ", curses.color_pair(4)) stdscr.addstr(display_row, right_start, "- ", curses.color_pair(4))
stdscr.addnstr(display_row, state.divider_col + 4, content, right_width - 2, curses.color_pair(4)) stdscr.addnstr(display_row, right_start + 2, content, right_width - 2, curses.color_pair(4))
else: else:
if state.show_whole_diff or state.show_additions or state.show_deletions: if state.show_whole_diff or state.show_additions or state.show_deletions:
stdscr.addstr(display_row, state.divider_col + 2, " ") stdscr.addstr(display_row, right_start, " ")
stdscr.addnstr(display_row, state.divider_col + 4, content, right_width - 2) stdscr.addnstr(display_row, right_start + 2, content, right_width - 2)
else: else:
# No diff mode, so don't add the margin padding # No diff mode, so don't add the margin padding
stdscr.addnstr(display_row, state.divider_col + 2, content, right_width) stdscr.addnstr(display_row, right_start, content, right_width)
display_row += 1 display_row += 1
def draw_divider(stdscr, state): def draw_divider(stdscr, state):
if not state.show_sidebar:
return
divider_char = "" if state.dragging_divider else "" divider_char = "" if state.dragging_divider else ""
for y in range(state.height - 1): # Don't draw through the commit message bar for y in range(state.height - 1): # Don't draw through the commit message bar
try: try:
@ -368,31 +383,20 @@ def draw_status_bars(stdscr, state):
left_attr = curses.color_pair(1) if state.focus == "left" else curses.color_pair(2) left_attr = curses.color_pair(1) if state.focus == "left" else curses.color_pair(2)
right_attr = curses.color_pair(1) if state.focus == "right" else curses.color_pair(2) right_attr = curses.color_pair(1) if state.focus == "right" else curses.color_pair(2)
if state.show_sidebar:
# Fill the original status bar with spaces # Fill the original status bar with spaces
for x in range(state.divider_col): for x in range(state.divider_col):
try: try:
stdscr.addch(state.height - 2, x, ' ', left_attr) stdscr.addch(state.height - 2, x, ' ', left_attr)
except curses.error: except curses.error:
pass pass
for x in range(state.divider_col + 1, state.width - 1):
try:
stdscr.addch(state.height - 2, x, ' ', right_attr)
except curses.error:
pass
# Add percentage indicators on left and right sides of original status bar # Add percentage indicators on left side of original status bar
try: try:
stdscr.addstr(state.height - 2, 1, left_status, left_attr) stdscr.addstr(state.height - 2, 1, left_status, left_attr)
except curses.error: except curses.error:
pass pass
try:
right_x = state.width - len(right_status) - 1
if right_x >= 0:
stdscr.addstr(state.height - 2, right_x, right_status, right_attr)
except curses.error:
pass
# Draw divider in status bar with left and right colors # Draw divider in status bar with left and right colors
try: try:
# Use the half block character "▐" which can show both colors # Use the half block character "▐" which can show both colors
@ -401,6 +405,28 @@ def draw_status_bars(stdscr, state):
except curses.error: except curses.error:
pass pass
# Fill right side of status bar
for x in range(state.divider_col + 1, state.width - 1):
try:
stdscr.addch(state.height - 2, x, ' ', right_attr)
except curses.error:
pass
else:
# When sidebar is hidden, fill the entire status bar with right pane color
for x in range(state.width - 1):
try:
stdscr.addch(state.height - 2, x, ' ', right_attr)
except curses.error:
pass
# Add right percentage indicator
try:
right_x = state.width - len(right_status) - 1
if right_x >= 0:
stdscr.addstr(state.height - 2, right_x, right_status, right_attr)
except curses.error:
pass
# Draw new full-width status bar with commit message below the original status bars # Draw new full-width status bar with commit message below the original status bars
status_attr = curses.color_pair(5) # Color pair for commit message bar status_attr = curses.color_pair(5) # Color pair for commit message bar
@ -503,12 +529,15 @@ def handle_mouse_input(stdscr, state):
# Handle mouse button press # Handle mouse button press
if bstate & curses.BUTTON1_PRESSED: if bstate & curses.BUTTON1_PRESSED:
# Check if clicking near divider # Check if clicking near divider (only if sidebar is visible)
if abs(mx - state.divider_col) <= 1: if state.show_sidebar and abs(mx - state.divider_col) <= 1:
state.dragging_divider = True state.dragging_divider = True
else: else:
# Switch panes immediately on click # Switch panes immediately on click (only if sidebar is visible)
if state.show_sidebar:
state.focus = "left" if mx < state.divider_col else "right" state.focus = "left" if mx < state.divider_col else "right"
else:
state.focus = "right" # When sidebar is hidden, focus is always right
# Also start a potential selection (in case this becomes a drag) # Also start a potential selection (in case this becomes a drag)
state.is_selecting = True state.is_selecting = True
@ -534,11 +563,14 @@ def handle_mouse_input(stdscr, state):
if (state.click_position and if (state.click_position and
abs(state.click_position[0] - mx) <= 2 and abs(state.click_position[0] - mx) <= 2 and
abs(state.click_position[1] - my) <= 2): abs(state.click_position[1] - my) <= 2):
# This was a click, so switch panes # This was a click, so switch panes (only if sidebar is visible)
if state.show_sidebar:
state.focus = "left" if mx < state.divider_col else "right" state.focus = "left" if mx < state.divider_col else "right"
else:
state.focus = "right" # When sidebar is hidden, focus is always right
# If clicking in the left pane on a commit entry, select that commit # If clicking in the left pane on a commit entry, select that commit
if mx < state.divider_col and my < len(state.commits) - state.left_scroll_offset: if state.show_sidebar and mx < state.divider_col and my < len(state.commits) - state.left_scroll_offset:
new_commit_idx = my + state.left_scroll_offset new_commit_idx = my + state.left_scroll_offset
if 0 <= new_commit_idx < len(state.commits): if 0 <= new_commit_idx < len(state.commits):
state.selected_commit_idx = new_commit_idx state.selected_commit_idx = new_commit_idx
@ -579,7 +611,10 @@ def handle_keyboard_input(key, state):
state.should_exit = True state.should_exit = True
elif key == ord('m'): elif key == ord('m'):
state.toggle_mouse() state.toggle_mouse()
elif key == ord('s'):
state.toggle_sidebar()
elif key in [curses.KEY_LEFT, ord('h')]: elif key in [curses.KEY_LEFT, ord('h')]:
if state.show_sidebar:
state.focus = "left" state.focus = "left"
elif key in [curses.KEY_RIGHT, ord('l')]: elif key in [curses.KEY_RIGHT, ord('l')]:
state.focus = "right" state.focus = "right"