Merge pull request #20 from overcuriousity/claude/show-pgp-signature-llNQW

Display raw PGP signature in verification dialog
This commit is contained in:
overcuriousity
2025-12-14 20:34:56 +01:00
committed by GitHub

View File

@@ -119,7 +119,7 @@ class TUI:
self.flash_time = time.time() self.flash_time = time.time()
def verify_note_signature(self): def verify_note_signature(self):
"""Show detailed verification dialog for current note""" """Show detailed verification dialog for current note with raw signature"""
if not self.current_note: if not self.current_note:
return return
@@ -134,6 +134,7 @@ class TUI:
"To sign notes, enable GPG signing in settings", "To sign notes, enable GPG signing in settings",
"and ensure you have a GPG key configured." "and ensure you have a GPG key configured."
] ]
signature_lines = []
elif verified: elif verified:
title = "✓ Signature Verified" title = "✓ Signature Verified"
message = [ message = [
@@ -141,8 +142,13 @@ class TUI:
"", "",
f"Signed by: {info}", f"Signed by: {info}",
"", "",
"This note has not been tampered with since signing." "This note has not been tampered with since signing.",
"",
"" * 60,
"RAW PGP SIGNATURE (copy/paste for external verification):",
"" * 60,
] ]
signature_lines = self.current_note.signature.split('\n')
else: else:
title = "✗ Signature Verification Failed" title = "✗ Signature Verification Failed"
message = [ message = [
@@ -153,37 +159,79 @@ class TUI:
"Possible causes:", "Possible causes:",
"- Public key not in keyring", "- Public key not in keyring",
"- Note content was modified after signing", "- Note content was modified after signing",
"- Signature is corrupted" "- Signature is corrupted",
"",
"" * 60,
"RAW PGP SIGNATURE (copy/paste for external verification):",
"" * 60,
] ]
signature_lines = self.current_note.signature.split('\n')
# Display dialog (reuse pattern from other dialogs) # Combine message and signature
all_lines = message + signature_lines
# Display scrollable dialog
h, w = self.stdscr.getmaxyx() h, w = self.stdscr.getmaxyx()
dialog_h = min(len(message) + 6, h - 4) dialog_h = min(h - 4, 40) # Max height for dialog
dialog_w = min(max(len(line) for line in [title] + message) + 6, w - 4) dialog_w = min(w - 4, 100) # Max width for dialog
start_y = (h - dialog_h) // 2 start_y = (h - dialog_h) // 2
start_x = (w - dialog_w) // 2 start_x = (w - dialog_w) // 2
# Create dialog window # Create dialog window
dialog = curses.newwin(dialog_h, dialog_w, start_y, start_x) dialog = curses.newwin(dialog_h, dialog_w, start_y, start_x)
dialog.box()
# Title scroll_offset = 0
dialog.attron(curses.A_BOLD) max_scroll = max(0, len(all_lines) - (dialog_h - 6))
title_x = (dialog_w - len(title)) // 2
dialog.addstr(1, title_x, title)
dialog.attroff(curses.A_BOLD)
# Message while True:
for i, line in enumerate(message): dialog.clear()
dialog.addstr(3 + i, 2, line) dialog.box()
# Footer # Title
footer = "Press any key to close" dialog.attron(curses.A_BOLD)
footer_x = (dialog_w - len(footer)) // 2 title_x = (dialog_w - len(title)) // 2
dialog.addstr(dialog_h - 2, footer_x, footer, curses.color_pair(3)) try:
dialog.addstr(1, title_x, title)
except curses.error:
pass
dialog.attroff(curses.A_BOLD)
dialog.refresh() # Display visible lines
dialog.getch() visible_lines = all_lines[scroll_offset:scroll_offset + dialog_h - 6]
for i, line in enumerate(visible_lines):
try:
# Truncate line if too long for dialog width
truncated_line = line[:dialog_w - 4]
dialog.addstr(3 + i, 2, truncated_line)
except curses.error:
pass
# Footer with scroll indicators
if max_scroll > 0:
footer = f"↑/↓ Scroll ({scroll_offset + 1}/{len(all_lines)}) Any other key to close"
else:
footer = "Press any key to close"
footer_x = max(2, (dialog_w - len(footer)) // 2)
try:
dialog.addstr(dialog_h - 2, footer_x, footer[:dialog_w - 4], curses.color_pair(3))
except curses.error:
pass
dialog.refresh()
# Handle input
key = dialog.getch()
if key == curses.KEY_UP and scroll_offset > 0:
scroll_offset -= 1
elif key == curses.KEY_DOWN and scroll_offset < max_scroll:
scroll_offset += 1
elif key == curses.KEY_PPAGE: # Page Up
scroll_offset = max(0, scroll_offset - (dialog_h - 6))
elif key == curses.KEY_NPAGE: # Page Down
scroll_offset = min(max_scroll, scroll_offset + (dialog_h - 6))
else:
# Any other key closes the dialog
break
def _save_nav_position(self): def _save_nav_position(self):
"""Save current navigation position before changing views""" """Save current navigation position before changing views"""