fix: Improve text selection and clipboard handling

This commit is contained in:
n loewen (aider) 2025-06-08 10:31:11 +01:00
parent bc10b41059
commit b8f49880f7
1 changed files with 54 additions and 21 deletions

71
gtm
View File

@ -351,7 +351,9 @@ def toggle_sidebar(state: AppState) -> AppState:
def move_commit_selection(state: AppState, delta: int) -> AppState:
new_idx = state.selected_commit_idx + delta
if 0 <= new_idx < len(state.commits):
new_state = replace(state, selected_commit_idx=new_idx)
# Clear selection when changing commits
new_state = replace(state, selected_commit_idx=new_idx, is_selecting=False,
selection_start_coord=None, selection_end_coord=None, click_position=None)
return load_commit_content(new_state)
return state
@ -371,6 +373,11 @@ def scroll_right_pane(state: AppState, delta: int) -> AppState:
new_offset = state.right_scroll_offset + delta
new_offset = max(0, min(new_offset, max_scroll))
# Clear selection when scrolling
if state.is_selecting:
return replace(state, right_scroll_offset=new_offset, is_selecting=False,
selection_start_coord=None, selection_end_coord=None, click_position=None)
return replace(state, right_scroll_offset=new_offset)
def page_right_pane(state: AppState, direction: int) -> AppState:
@ -1152,13 +1159,13 @@ def copy_selection_to_clipboard(stdscr, state):
line_num = i + state.right_scroll_offset + 1
# Add deleted lines if they should be shown
if (state.show_whole_diff or state.show_deletions) and line_num in deleted_line_map:
if state.show_deletions and line_num in deleted_line_map:
for del_content in deleted_line_map[line_num]:
display_lines.append({'type': 'deleted', 'content': del_content})
# Determine if this is an added line
is_added = False
if state.show_whole_diff or state.show_additions:
if state.show_additions:
for added_line_num, _ in state.added_lines:
if added_line_num == line_num:
is_added = True
@ -1204,18 +1211,37 @@ def copy_selection_to_clipboard(stdscr, state):
if selected_text_parts:
text_to_copy = "\n".join(selected_text_parts)
if text_to_copy.strip():
# Log the text being copied for debugging
with open("/tmp/gtm_clipboard.log", "w") as f:
f.write(f"Copying to clipboard: {text_to_copy}\n")
# Try macOS pbcopy first (most likely on this system)
try:
subprocess.run(['pbcopy'], input=text_to_copy, text=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
proc = subprocess.Popen(['pbcopy'], stdin=subprocess.PIPE)
proc.communicate(text_to_copy.encode('utf-8'))
proc.wait()
return # Return early if successful
except (FileNotFoundError, subprocess.SubprocessError):
pass
# Try Linux xclip
try:
# Try xclip for Linux systems
subprocess.run(['xclip', '-selection', 'clipboard'], input=text_to_copy, text=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
proc = subprocess.Popen(['xclip', '-selection', 'clipboard'], stdin=subprocess.PIPE)
proc.communicate(text_to_copy.encode('utf-8'))
proc.wait()
return # Return early if successful
except (FileNotFoundError, subprocess.SubprocessError):
pass
# Try Windows clip.exe
try:
# Try clip.exe for Windows
subprocess.run(['clip.exe'], input=text_to_copy, text=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
pass # Silently fail if no clipboard command is available
proc = subprocess.Popen(['clip.exe'], stdin=subprocess.PIPE)
proc.communicate(text_to_copy.encode('utf-8'))
proc.wait()
except (FileNotFoundError, subprocess.SubprocessError):
# Log failure
with open("/tmp/gtm_clipboard.log", "a") as f:
f.write("Failed to copy to clipboard - no clipboard command available\n")
def update_status_bar_height(state: AppState, my: int) -> AppState:
"""Update the status bar height based on mouse position."""
@ -1275,17 +1301,24 @@ def handle_mouse_input(stdscr, state: AppState) -> AppState:
if state.commit_hash_clicked:
# Copy commit hash to clipboard
if state.commit_hash:
# Try macOS pbcopy first (most likely on this system)
try:
subprocess.run(['pbcopy'], input=state.commit_hash, text=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
proc = subprocess.Popen(['pbcopy'], stdin=subprocess.PIPE)
proc.communicate(state.commit_hash.encode('utf-8'))
proc.wait()
except (FileNotFoundError, subprocess.SubprocessError):
try:
# Try xclip for Linux systems
subprocess.run(['xclip', '-selection', 'clipboard'], input=state.commit_hash, text=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
proc = subprocess.Popen(['xclip', '-selection', 'clipboard'], stdin=subprocess.PIPE)
proc.communicate(state.commit_hash.encode('utf-8'))
proc.wait()
except (FileNotFoundError, subprocess.SubprocessError):
try:
# Try clip.exe for Windows
subprocess.run(['clip.exe'], input=state.commit_hash, text=True, check=True)
except (FileNotFoundError, subprocess.CalledProcessError):
proc = subprocess.Popen(['clip.exe'], stdin=subprocess.PIPE)
proc.communicate(state.commit_hash.encode('utf-8'))
proc.wait()
except (FileNotFoundError, subprocess.SubprocessError):
pass # Silently fail if no clipboard command is available
# Reset the clicked state
@ -1356,7 +1389,7 @@ def handle_keyboard_input(key, state: AppState) -> AppState:
if state.search_mode:
new_state = replace(new_state, search_mode=False)
elif state.is_selecting:
new_state = replace(new_state, is_selecting=False, selection_start_coord=None, selection_end_coord=None)
new_state = replace(new_state, is_selecting=False, selection_start_coord=None, selection_end_coord=None, click_position=None)
# Return the updated state with all changes
return new_state