Merge remote-tracking branch 'refs/remotes/origin/main'
This commit is contained in:
commit
331574053b
|
|
@ -0,0 +1 @@
|
||||||
|
.aider*
|
||||||
|
|
@ -14,36 +14,66 @@ def get_file_at_commit(commit_hash, filename):
|
||||||
|
|
||||||
def main(stdscr, filename):
|
def main(stdscr, filename):
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
|
curses.mousemask(curses.ALL_MOUSE_EVENTS)
|
||||||
curses.mouseinterval(0)
|
curses.mouseinterval(0)
|
||||||
stdscr.keypad(True)
|
stdscr.keypad(True)
|
||||||
|
|
||||||
|
# Initialize colors if terminal supports them
|
||||||
|
if curses.has_colors():
|
||||||
|
curses.use_default_colors() # Use terminal's default colors
|
||||||
|
curses.init_pair(1, 5, 7) # Focused status bar
|
||||||
|
curses.init_pair(2, curses.COLOR_WHITE, 8) # Unfocused status bar
|
||||||
|
|
||||||
|
# Initialize key variable
|
||||||
|
key = 0
|
||||||
|
|
||||||
commits = get_commits(filename)
|
commits = get_commits(filename)
|
||||||
selected_commit = 0
|
selected_commit = 0
|
||||||
divider_col = 40
|
divider_col = 40
|
||||||
focus = "left"
|
focus = "left"
|
||||||
scroll_offset = 0
|
scroll_offset = 0
|
||||||
|
left_scroll_offset = 0 # Scroll position for left pane
|
||||||
|
|
||||||
dragging_divider = False
|
dragging_divider = False
|
||||||
|
|
||||||
|
# Initialize file content
|
||||||
|
commit_hash = commits[selected_commit].split()[0]
|
||||||
|
file_lines = get_file_at_commit(commit_hash, filename)
|
||||||
|
|
||||||
|
# Enable nodelay for smoother scrolling
|
||||||
|
stdscr.nodelay(True)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
stdscr.clear()
|
stdscr.erase() # Use erase instead of clear for less flickering
|
||||||
height, width = stdscr.getmaxyx()
|
height, width = stdscr.getmaxyx()
|
||||||
|
|
||||||
# Fetch file content for selected commit
|
# Only fetch file content when commit changes
|
||||||
commit_hash = commits[selected_commit].split()[0]
|
if key in [curses.KEY_DOWN, curses.KEY_UP, ord('j'), ord('k')] and focus == "left":
|
||||||
file_lines = get_file_at_commit(commit_hash, filename)
|
commit_hash = commits[selected_commit].split()[0]
|
||||||
|
file_lines = get_file_at_commit(commit_hash, filename)
|
||||||
|
|
||||||
max_scroll = max(0, len(file_lines) - (height - 1))
|
max_scroll = max(0, len(file_lines) - (height - 1))
|
||||||
scroll_offset = min(scroll_offset, max_scroll)
|
scroll_offset = min(scroll_offset, max_scroll)
|
||||||
visible_lines = file_lines[scroll_offset:scroll_offset + height - 1]
|
visible_lines = file_lines[scroll_offset:scroll_offset + height - 1]
|
||||||
|
|
||||||
|
# Calculate visible commits for left pane
|
||||||
|
left_max_scroll = max(0, len(commits) - (height - 1))
|
||||||
|
left_scroll_offset = min(left_scroll_offset, left_max_scroll)
|
||||||
|
|
||||||
|
# Ensure selected commit is visible
|
||||||
|
if selected_commit < left_scroll_offset:
|
||||||
|
left_scroll_offset = selected_commit
|
||||||
|
elif selected_commit >= left_scroll_offset + height - 1:
|
||||||
|
left_scroll_offset = selected_commit - (height - 2)
|
||||||
|
|
||||||
# Draw commit list (left pane)
|
# Draw commit list (left pane)
|
||||||
for i in range(min(len(commits), height - 1)):
|
visible_commits = commits[left_scroll_offset:left_scroll_offset + height - 1]
|
||||||
line = commits[i]
|
for i, line in enumerate(visible_commits):
|
||||||
if i == selected_commit:
|
display_index = i + left_scroll_offset
|
||||||
|
if display_index == selected_commit:
|
||||||
stdscr.attron(curses.A_REVERSE) # Highlight selected commit
|
stdscr.attron(curses.A_REVERSE) # Highlight selected commit
|
||||||
stdscr.addnstr(i, 0, line, divider_col - 1)
|
stdscr.addnstr(i, 0, line, divider_col - 1)
|
||||||
if i == selected_commit:
|
if display_index == selected_commit:
|
||||||
stdscr.attroff(curses.A_REVERSE)
|
stdscr.attroff(curses.A_REVERSE)
|
||||||
|
|
||||||
# Vertical divider
|
# Vertical divider
|
||||||
|
|
@ -55,74 +85,89 @@ def main(stdscr, filename):
|
||||||
# Avoid errors when drawing at the last column
|
# Avoid errors when drawing at the last column
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Draw file content (right pane)
|
# Draw file content (right pane) - more efficiently
|
||||||
|
right_width = width - divider_col - 3
|
||||||
for i, line in enumerate(visible_lines):
|
for i, line in enumerate(visible_lines):
|
||||||
stdscr.addnstr(i, divider_col + 2, line, width - divider_col - 3)
|
# Only draw what fits in the window
|
||||||
|
if i < height - 1:
|
||||||
|
stdscr.addnstr(i, divider_col + 2, line, right_width)
|
||||||
|
|
||||||
# Status bar for right pane
|
# Status bars for both panes
|
||||||
visible_height = height - 1 # Reserve 1 line for the status bar
|
visible_height = height - 1 # Reserve 1 line for the status bar
|
||||||
|
|
||||||
|
# Right pane status
|
||||||
last_visible_line = scroll_offset + visible_height
|
last_visible_line = scroll_offset + visible_height
|
||||||
|
|
||||||
if len(file_lines) > 0:
|
if len(file_lines) > 0:
|
||||||
percent = int((last_visible_line / len(file_lines)) * 100)
|
right_percent = int((last_visible_line / len(file_lines)) * 100)
|
||||||
percent = 100 if last_visible_line >= len(file_lines) else percent
|
right_percent = 100 if last_visible_line >= len(file_lines) else right_percent
|
||||||
else:
|
else:
|
||||||
percent = 0
|
right_percent = 0
|
||||||
|
right_status = f"{right_percent}% "
|
||||||
status = f"{percent}%"
|
|
||||||
x = width - len(status) - 1
|
# Left pane status
|
||||||
stdscr.addnstr(height - 1, x, status, len(status), curses.A_REVERSE)
|
last_visible_commit = left_scroll_offset + visible_height
|
||||||
|
if len(commits) > 0:
|
||||||
|
left_percent = int((last_visible_commit / len(commits)) * 100)
|
||||||
|
left_percent = 100 if last_visible_commit >= len(commits) else left_percent
|
||||||
|
else:
|
||||||
|
left_percent = 0
|
||||||
|
left_status = f"{left_percent}%"
|
||||||
|
|
||||||
|
# Draw status bars - full width with different colors based on focus
|
||||||
|
left_attr = curses.color_pair(1) if focus == "left" else curses.color_pair(2)
|
||||||
|
right_attr = curses.color_pair(1) if focus == "right" else curses.color_pair(2)
|
||||||
|
|
||||||
|
# Fill the entire bottom row for each pane
|
||||||
|
for x in range(divider_col):
|
||||||
|
stdscr.addch(height - 1, x, ' ', left_attr)
|
||||||
|
for x in range(divider_col + 1, width - 1): # Avoid the last column
|
||||||
|
stdscr.addch(height - 1, x, ' ', right_attr)
|
||||||
|
|
||||||
|
# Add the percentage text
|
||||||
|
stdscr.addstr(height - 1, 1, left_status, left_attr)
|
||||||
|
stdscr.addstr(height - 1, width - len(right_status) - 1, right_status, right_attr)
|
||||||
|
|
||||||
|
# Add divider character at the bottom row
|
||||||
|
stdscr.addch(height - 1, divider_col, divider_char)
|
||||||
|
|
||||||
|
# Get input with a small timeout for smoother scrolling
|
||||||
|
stdscr.timeout(50) # 50ms timeout
|
||||||
key = stdscr.getch()
|
key = stdscr.getch()
|
||||||
|
|
||||||
|
# If no key was pressed, continue the loop
|
||||||
|
if key == -1:
|
||||||
|
continue
|
||||||
|
|
||||||
# Mouse interaction
|
# Mouse interaction
|
||||||
if key == curses.KEY_MOUSE:
|
if key == curses.KEY_MOUSE:
|
||||||
try:
|
try:
|
||||||
_, mx, my, _, bstate = curses.getmouse()
|
_, mx, my, _, bstate = curses.getmouse()
|
||||||
|
|
||||||
|
# Handle divider dragging
|
||||||
if bstate & curses.BUTTON1_PRESSED:
|
if bstate & curses.BUTTON1_PRESSED:
|
||||||
if mx == divider_col or (mx >= divider_col-1 and mx <= divider_col+1):
|
# Start dragging when clicked near divider
|
||||||
|
if abs(mx - divider_col) <= 1: # Allow clicking within 1 column of divider
|
||||||
dragging_divider = True
|
dragging_divider = True
|
||||||
|
|
||||||
# Always update divider position if dragging, regardless of button state
|
# If already dragging, update divider position
|
||||||
if dragging_divider:
|
if dragging_divider:
|
||||||
|
# Update divider position while dragging
|
||||||
min_col = 10
|
min_col = 10
|
||||||
max_col = width - 20 # leave space for right pane
|
max_col = width - 20 # leave space for right pane
|
||||||
divider_col = max(min_col, min(mx, max_col))
|
divider_col = max(min_col, min(mx, max_col))
|
||||||
|
|
||||||
|
# Handle mouse release
|
||||||
if bstate & curses.BUTTON1_RELEASED:
|
if bstate & curses.BUTTON1_RELEASED:
|
||||||
dragging_divider = False
|
if dragging_divider:
|
||||||
elif bstate & curses.BUTTON1_CLICKED and not dragging_divider:
|
dragging_divider = False
|
||||||
focus = "left" if mx < divider_col else "right"
|
else:
|
||||||
except curses.error:
|
# Change focus on mouse click (when not dragging)
|
||||||
pass
|
focus = "left" if mx < divider_col else "right"
|
||||||
|
|
||||||
# Set timeout for more responsive updates during dragging
|
|
||||||
if dragging_divider:
|
|
||||||
stdscr.timeout(1) # Very short timeout for responsive updates
|
|
||||||
else:
|
|
||||||
stdscr.timeout(-1) # Blocking mode when not dragging
|
|
||||||
|
|
||||||
# Handle divider dragging - check mouse position every iteration when dragging
|
|
||||||
if dragging_divider:
|
|
||||||
try:
|
|
||||||
# Get current mouse position
|
|
||||||
curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
|
|
||||||
_, mx, my, _, bstate = curses.getmouse()
|
|
||||||
|
|
||||||
# Update divider position based on mouse x position
|
|
||||||
min_col = 10
|
|
||||||
max_col = width - 20 # leave space for right pane
|
|
||||||
divider_col = max(min_col, min(mx, max_col))
|
|
||||||
|
|
||||||
# Check if mouse button has been released
|
|
||||||
if not (bstate & curses.BUTTON1_PRESSED):
|
|
||||||
dragging_divider = False
|
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Exit on 'q' or ESC
|
# Exit on 'q' or ESC
|
||||||
if key in [ord('q'), 27]:
|
elif key in [ord('q'), 27]:
|
||||||
break
|
break
|
||||||
|
|
||||||
# Left pane movement
|
# Left pane movement
|
||||||
|
|
@ -135,6 +180,18 @@ def main(stdscr, filename):
|
||||||
if selected_commit > 0:
|
if selected_commit > 0:
|
||||||
selected_commit -= 1
|
selected_commit -= 1
|
||||||
scroll_offset = 0
|
scroll_offset = 0
|
||||||
|
elif key in [curses.KEY_NPAGE, ord(' ')]:
|
||||||
|
# Page down in left pane
|
||||||
|
new_selected = min(selected_commit + height - 1, len(commits) - 1)
|
||||||
|
if new_selected != selected_commit:
|
||||||
|
selected_commit = new_selected
|
||||||
|
scroll_offset = 0
|
||||||
|
elif key in [curses.KEY_PPAGE, 8, 127, curses.KEY_SR]:
|
||||||
|
# Page up in left pane
|
||||||
|
new_selected = max(0, selected_commit - (height - 1))
|
||||||
|
if new_selected != selected_commit:
|
||||||
|
selected_commit = new_selected
|
||||||
|
scroll_offset = 0
|
||||||
|
|
||||||
# Right pane scrolling
|
# Right pane scrolling
|
||||||
elif focus == "right":
|
elif focus == "right":
|
||||||
|
|
@ -146,7 +203,9 @@ def main(stdscr, filename):
|
||||||
scroll_offset -= 1
|
scroll_offset -= 1
|
||||||
elif key in [curses.KEY_NPAGE, ord(' ')]:
|
elif key in [curses.KEY_NPAGE, ord(' ')]:
|
||||||
scroll_offset = min(scroll_offset + height - 1, max_scroll)
|
scroll_offset = min(scroll_offset + height - 1, max_scroll)
|
||||||
elif key in [curses.KEY_PPAGE, 8, 127]: # Page Up or Shift+Space (some terminals)
|
elif key in [curses.KEY_PPAGE, 8, 127, curses.KEY_SR]: # Page Up, Backspace, Delete, or Shift+Up
|
||||||
|
scroll_offset = max(0, scroll_offset - (height - 1))
|
||||||
|
elif key == curses.KEY_BACKSPACE: # Another way to detect Shift+Space in some terminals
|
||||||
scroll_offset = max(0, scroll_offset - (height - 1))
|
scroll_offset = max(0, scroll_offset - (height - 1))
|
||||||
|
|
||||||
# Pane switching
|
# Pane switching
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue