From b8a5ed9f6688d9f2ac8a60384127ac0d5d715244 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Mon, 14 Jul 2025 15:23:37 -0400 Subject: [PATCH] Add header clearing with notifications --- src/main.py | 14 ++++---- src/password_manager/manager.py | 48 ++++++++++++++-------------- src/tests/test_menu_notifications.py | 4 +-- src/utils/__init__.py | 8 ++++- src/utils/terminal_utils.py | 40 +++++++++++++++++++++++ 5 files changed, 80 insertions(+), 34 deletions(-) diff --git a/src/main.py b/src/main.py index cc9cafe..a10b296 100644 --- a/src/main.py +++ b/src/main.py @@ -25,7 +25,7 @@ from utils import ( copy_to_clipboard, clear_screen, pause, - clear_and_print_fingerprint, + clear_header_with_notification, ) import queue from local_bip85.bip85 import Bip85Error @@ -737,7 +737,7 @@ def handle_profiles_menu(password_manager: PasswordManager) -> None: "header_fingerprint_args", (getattr(password_manager, "current_fingerprint", None), None, None), ) - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Profiles", parent_fingerprint=parent_fp, @@ -783,7 +783,7 @@ def handle_nostr_menu(password_manager: PasswordManager) -> None: "header_fingerprint_args", (getattr(password_manager, "current_fingerprint", None), None, None), ) - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Nostr", parent_fingerprint=parent_fp, @@ -827,7 +827,7 @@ def handle_settings(password_manager: PasswordManager) -> None: "header_fingerprint_args", (getattr(password_manager, "current_fingerprint", None), None, None), ) - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings", parent_fingerprint=parent_fp, @@ -940,7 +940,7 @@ def display_menu( "header_fingerprint_args", (getattr(password_manager, "current_fingerprint", None), None, None), ) - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu", parent_fingerprint=parent_fp, @@ -1004,7 +1004,7 @@ def display_menu( None, ), ) - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry", parent_fingerprint=parent_fp, @@ -1059,7 +1059,7 @@ def display_menu( "header_fingerprint_args", (getattr(password_manager, "current_fingerprint", None), None, None), ) - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu", parent_fingerprint=parent_fp, diff --git a/src/password_manager/manager.py b/src/password_manager/manager.py index c6d10c3..771ea12 100644 --- a/src/password_manager/manager.py +++ b/src/password_manager/manager.py @@ -59,8 +59,8 @@ from utils.clipboard import copy_to_clipboard from utils.terminal_utils import ( clear_screen, pause, - clear_and_print_fingerprint, clear_and_print_profile_chain, + clear_header_with_notification, ) from utils.fingerprint import generate_fingerprint from constants import MIN_HEALTHY_RELAYS @@ -1177,7 +1177,7 @@ class PasswordManager: def handle_add_password(self) -> None: try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > Password", parent_fingerprint=parent_fp, @@ -1277,7 +1277,7 @@ class PasswordManager: """Add a TOTP entry either derived from the seed or imported.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > 2FA (TOTP)", parent_fingerprint=parent_fp, @@ -1405,7 +1405,7 @@ class PasswordManager: """Add an SSH key pair entry and display the derived keys.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > SSH Key", parent_fingerprint=parent_fp, @@ -1461,7 +1461,7 @@ class PasswordManager: """Add a derived BIP-39 seed phrase entry.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > Seed Phrase", parent_fingerprint=parent_fp, @@ -1529,7 +1529,7 @@ class PasswordManager: """Add a PGP key entry and display the generated key.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > PGP Key", parent_fingerprint=parent_fp, @@ -1595,7 +1595,7 @@ class PasswordManager: """Add a Nostr key entry and display the derived keys.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > Nostr Key Pair", parent_fingerprint=parent_fp, @@ -1651,7 +1651,7 @@ class PasswordManager: """Add a generic key/value entry.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > Key/Value", parent_fingerprint=parent_fp, @@ -1726,7 +1726,7 @@ class PasswordManager: """Add a managed account seed entry.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Add Entry > Managed Account", parent_fingerprint=parent_fp, @@ -2015,7 +2015,7 @@ class PasswordManager: """ try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Retrieve Entry", parent_fingerprint=parent_fp, @@ -2468,7 +2468,7 @@ class PasswordManager: """ try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Modify Entry", parent_fingerprint=parent_fp, @@ -2829,7 +2829,7 @@ class PasswordManager: """Prompt for a query, list matches and optionally show details.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Search Entries", parent_fingerprint=parent_fp, @@ -2849,7 +2849,7 @@ class PasswordManager: while True: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Search Entries", parent_fingerprint=parent_fp, @@ -2980,7 +2980,7 @@ class PasswordManager: try: while True: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > List Entries", parent_fingerprint=parent_fp, @@ -3028,7 +3028,7 @@ class PasswordManager: continue while True: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > List Entries", parent_fingerprint=parent_fp, @@ -3120,7 +3120,7 @@ class PasswordManager: return while True: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Archived Entries", parent_fingerprint=parent_fp, @@ -3177,7 +3177,7 @@ class PasswordManager: """Display all stored TOTP codes with a countdown progress bar.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > 2FA Codes", parent_fingerprint=parent_fp, @@ -3203,7 +3203,7 @@ class PasswordManager: print(colored("Press Enter to return to the menu.", "cyan")) while True: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > 2FA Codes", parent_fingerprint=parent_fp, @@ -3264,7 +3264,7 @@ class PasswordManager: """ try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Verify Script Checksum", parent_fingerprint=parent_fp, @@ -3305,7 +3305,7 @@ class PasswordManager: return try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Generate Script Checksum", parent_fingerprint=parent_fp, @@ -3423,7 +3423,7 @@ class PasswordManager: """Export the current database to an encrypted portable file.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Export database", parent_fingerprint=parent_fp, @@ -3446,7 +3446,7 @@ class PasswordManager: """Import a portable database file, replacing the current index.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Import database", parent_fingerprint=parent_fp, @@ -3468,7 +3468,7 @@ class PasswordManager: """Export all 2FA codes to a JSON file for other authenticator apps.""" try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Export 2FA codes", parent_fingerprint=parent_fp, @@ -3541,7 +3541,7 @@ class PasswordManager: """ try: fp, parent_fp, child_fp = self.header_fingerprint_args - clear_and_print_fingerprint( + clear_header_with_notification( fp, "Main Menu > Settings > Backup Parent Seed", parent_fingerprint=parent_fp, diff --git a/src/tests/test_menu_notifications.py b/src/tests/test_menu_notifications.py index 123f4b4..786c6e3 100644 --- a/src/tests/test_menu_notifications.py +++ b/src/tests/test_menu_notifications.py @@ -39,7 +39,7 @@ def _make_pm(msg): def test_display_menu_prints_notifications(monkeypatch, capsys): pm = _make_pm("hello") monkeypatch.setattr(main, "_display_live_stats", lambda *_: None) - monkeypatch.setattr(main, "clear_and_print_fingerprint", lambda *a, **k: None) + monkeypatch.setattr(main, "clear_header_with_notification", lambda *a, **k: None) monkeypatch.setattr(main, "timed_input", lambda *a, **k: "") with pytest.raises(SystemExit): main.display_menu(pm, sync_interval=1000, inactivity_timeout=1000) @@ -52,7 +52,7 @@ def test_display_menu_reuses_notification_line(monkeypatch, capsys): pm = _make_pm(None) msgs = iter(["first", "second"]) monkeypatch.setattr(main, "_display_live_stats", lambda *_: None) - monkeypatch.setattr(main, "clear_and_print_fingerprint", lambda *a, **k: None) + monkeypatch.setattr(main, "clear_header_with_notification", lambda *a, **k: None) inputs = iter(["9", ""]) monkeypatch.setattr(main, "timed_input", lambda *a, **k: next(inputs)) monkeypatch.setattr(main, "drain_notifications", lambda _pm: next(msgs, None)) diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 25a2ca0..01e058c 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -28,7 +28,12 @@ try: from .input_utils import timed_input from .memory_protection import InMemorySecret from .clipboard import copy_to_clipboard - from .terminal_utils import clear_screen, pause, clear_and_print_fingerprint + from .terminal_utils import ( + clear_screen, + pause, + clear_and_print_fingerprint, + clear_header_with_notification, + ) if logger.isEnabledFor(logging.DEBUG): logger.info("Modules imported successfully.") @@ -58,5 +63,6 @@ __all__ = [ "copy_to_clipboard", "clear_screen", "clear_and_print_fingerprint", + "clear_header_with_notification", "pause", ] diff --git a/src/utils/terminal_utils.py b/src/utils/terminal_utils.py index 8c856ed..c0b370c 100644 --- a/src/utils/terminal_utils.py +++ b/src/utils/terminal_utils.py @@ -5,6 +5,8 @@ import sys from termcolor import colored +from utils.color_scheme import color_text + def clear_screen() -> None: """Clear the terminal screen using an ANSI escape code.""" @@ -49,6 +51,44 @@ def clear_and_print_profile_chain( print(colored(header, "green")) +def clear_header_with_notification( + pm, + fingerprint: str | None = None, + breadcrumb: str | None = None, + parent_fingerprint: str | None = None, + child_fingerprint: str | None = None, +) -> None: + """Clear the screen, print the header, then show the current notification.""" + + clear_screen() + header_fp = None + if parent_fingerprint and child_fingerprint: + header_fp = f"{parent_fingerprint} > Managed Account > {child_fingerprint}" + elif fingerprint: + header_fp = fingerprint + elif parent_fingerprint or child_fingerprint: + header_fp = parent_fingerprint or child_fingerprint + if header_fp: + header = f"Seed Profile: {header_fp}" + if breadcrumb: + header += f" > {breadcrumb}" + print(colored(header, "green")) + + note = None + if hasattr(pm, "get_current_notification"): + try: + note = pm.get_current_notification() + except Exception: + note = None + if note: + category = getattr(note, "level", "info").lower() + if category not in ("info", "warning", "error"): + category = "info" + print(color_text(getattr(note, "message", ""), category)) + else: + print() + + def pause(message: str = "Press Enter to continue...") -> None: """Wait for the user to press Enter before proceeding.""" if not sys.stdin or not sys.stdin.isatty():