mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Add color scheme helper and apply to menus and stats
This commit is contained in:
105
src/main.py
105
src/main.py
@@ -12,6 +12,7 @@ import gzip
|
||||
import tomli
|
||||
from colorama import init as colorama_init
|
||||
from termcolor import colored
|
||||
from utils.color_scheme import color_text
|
||||
import traceback
|
||||
|
||||
from password_manager.manager import PasswordManager
|
||||
@@ -248,26 +249,28 @@ def print_matches(
|
||||
if data
|
||||
else EntryType.PASSWORD.value
|
||||
)
|
||||
print(colored(f"Index: {idx}", "cyan"))
|
||||
print(color_text(f"Index: {idx}", "index"))
|
||||
if etype == EntryType.TOTP.value:
|
||||
print(colored(f" Label: {data.get('label', website)}", "cyan"))
|
||||
print(colored(f" Derivation Index: {data.get('index', idx)}", "cyan"))
|
||||
print(color_text(f" Label: {data.get('label', website)}", "index"))
|
||||
print(color_text(f" Derivation Index: {data.get('index', idx)}", "index"))
|
||||
elif etype == EntryType.SEED.value:
|
||||
print(colored(" Type: Seed Phrase", "cyan"))
|
||||
print(color_text(" Type: Seed Phrase", "index"))
|
||||
elif etype == EntryType.SSH.value:
|
||||
print(colored(" Type: SSH Key", "cyan"))
|
||||
print(color_text(" Type: SSH Key", "index"))
|
||||
elif etype == EntryType.PGP.value:
|
||||
print(colored(" Type: PGP Key", "cyan"))
|
||||
print(color_text(" Type: PGP Key", "index"))
|
||||
elif etype == EntryType.NOSTR.value:
|
||||
print(colored(" Type: Nostr Key", "cyan"))
|
||||
print(color_text(" Type: Nostr Key", "index"))
|
||||
else:
|
||||
if website:
|
||||
print(colored(f" Label: {website}", "cyan"))
|
||||
print(color_text(f" Label: {website}", "index"))
|
||||
if username:
|
||||
print(colored(f" Username: {username}", "cyan"))
|
||||
print(color_text(f" Username: {username}", "index"))
|
||||
if url:
|
||||
print(colored(f" URL: {url}", "cyan"))
|
||||
print(colored(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan"))
|
||||
print(color_text(f" URL: {url}", "index"))
|
||||
print(
|
||||
color_text(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "index")
|
||||
)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
@@ -565,12 +568,12 @@ def handle_toggle_secret_mode(pm: PasswordManager) -> None:
|
||||
def handle_profiles_menu(password_manager: PasswordManager) -> None:
|
||||
"""Submenu for managing seed profiles."""
|
||||
while True:
|
||||
print("\nProfiles:")
|
||||
print("1. Switch Seed Profile")
|
||||
print("2. Add a New Seed Profile")
|
||||
print("3. Remove an Existing Seed Profile")
|
||||
print("4. List All Seed Profiles")
|
||||
print("5. Back")
|
||||
print(color_text("\nProfiles:", "menu"))
|
||||
print(color_text("1. Switch Seed Profile", "menu"))
|
||||
print(color_text("2. Add a New Seed Profile", "menu"))
|
||||
print(color_text("3. Remove an Existing Seed Profile", "menu"))
|
||||
print(color_text("4. List All Seed Profiles", "menu"))
|
||||
print(color_text("5. Back", "menu"))
|
||||
choice = input("Select an option: ").strip()
|
||||
password_manager.update_activity()
|
||||
if choice == "1":
|
||||
@@ -601,15 +604,15 @@ def handle_nostr_menu(password_manager: PasswordManager) -> None:
|
||||
return
|
||||
|
||||
while True:
|
||||
print("\nNostr Settings:")
|
||||
print("1. Backup to Nostr")
|
||||
print("2. Restore from Nostr")
|
||||
print("3. View current relays")
|
||||
print("4. Add a relay URL")
|
||||
print("5. Remove a relay by number")
|
||||
print("6. Reset to default relays")
|
||||
print("7. Display Nostr Public Key")
|
||||
print("8. Back")
|
||||
print(color_text("\nNostr Settings:", "menu"))
|
||||
print(color_text("1. Backup to Nostr", "menu"))
|
||||
print(color_text("2. Restore from Nostr", "menu"))
|
||||
print(color_text("3. View current relays", "menu"))
|
||||
print(color_text("4. Add a relay URL", "menu"))
|
||||
print(color_text("5. Remove a relay by number", "menu"))
|
||||
print(color_text("6. Reset to default relays", "menu"))
|
||||
print(color_text("7. Display Nostr Public Key", "menu"))
|
||||
print(color_text("8. Back", "menu"))
|
||||
choice = input("Select an option: ").strip()
|
||||
password_manager.update_activity()
|
||||
if choice == "1":
|
||||
@@ -635,22 +638,22 @@ def handle_nostr_menu(password_manager: PasswordManager) -> None:
|
||||
def handle_settings(password_manager: PasswordManager) -> None:
|
||||
"""Interactive settings menu with submenus for profiles and Nostr."""
|
||||
while True:
|
||||
print("\nSettings:")
|
||||
print("1. Profiles")
|
||||
print("2. Nostr")
|
||||
print("3. Change password")
|
||||
print("4. Verify Script Checksum")
|
||||
print("5. Generate Script Checksum")
|
||||
print("6. Backup Parent Seed")
|
||||
print("7. Export database")
|
||||
print("8. Import database")
|
||||
print("9. Export 2FA codes")
|
||||
print("10. Set additional backup location")
|
||||
print("11. Set inactivity timeout")
|
||||
print("12. Lock Vault")
|
||||
print("13. Stats")
|
||||
print("14. Toggle Secret Mode")
|
||||
print("15. Back")
|
||||
print(color_text("\nSettings:", "menu"))
|
||||
print(color_text("1. Profiles", "menu"))
|
||||
print(color_text("2. Nostr", "menu"))
|
||||
print(color_text("3. Change password", "menu"))
|
||||
print(color_text("4. Verify Script Checksum", "menu"))
|
||||
print(color_text("5. Generate Script Checksum", "menu"))
|
||||
print(color_text("6. Backup Parent Seed", "menu"))
|
||||
print(color_text("7. Export database", "menu"))
|
||||
print(color_text("8. Import database", "menu"))
|
||||
print(color_text("9. Export 2FA codes", "menu"))
|
||||
print(color_text("10. Set additional backup location", "menu"))
|
||||
print(color_text("11. Set inactivity timeout", "menu"))
|
||||
print(color_text("12. Lock Vault", "menu"))
|
||||
print(color_text("13. Stats", "menu"))
|
||||
print(color_text("14. Toggle Secret Mode", "menu"))
|
||||
print(color_text("15. Back", "menu"))
|
||||
choice = input("Select an option: ").strip()
|
||||
if choice == "1":
|
||||
handle_profiles_menu(password_manager)
|
||||
@@ -729,7 +732,7 @@ def display_menu(
|
||||
# Flush logging handlers
|
||||
for handler in logging.getLogger().handlers:
|
||||
handler.flush()
|
||||
print(colored(menu, "cyan"))
|
||||
print(color_text(menu, "menu"))
|
||||
try:
|
||||
choice = timed_input(
|
||||
"Enter your choice (1-8): ", inactivity_timeout
|
||||
@@ -750,14 +753,14 @@ def display_menu(
|
||||
continue # Re-display the menu without marking as invalid
|
||||
if choice == "1":
|
||||
while True:
|
||||
print("\nAdd Entry:")
|
||||
print("1. Password")
|
||||
print("2. 2FA (TOTP)")
|
||||
print("3. SSH Key")
|
||||
print("4. Seed Phrase")
|
||||
print("5. Nostr Key Pair")
|
||||
print("6. PGP Key")
|
||||
print("7. Back")
|
||||
print(color_text("\nAdd Entry:", "menu"))
|
||||
print(color_text("1. Password", "menu"))
|
||||
print(color_text("2. 2FA (TOTP)", "menu"))
|
||||
print(color_text("3. SSH Key", "menu"))
|
||||
print(color_text("4. Seed Phrase", "menu"))
|
||||
print(color_text("5. Nostr Key Pair", "menu"))
|
||||
print(color_text("6. PGP Key", "menu"))
|
||||
print(color_text("7. Back", "menu"))
|
||||
sub_choice = input("Select entry type: ").strip()
|
||||
password_manager.update_activity()
|
||||
if sub_choice == "1":
|
||||
|
@@ -20,6 +20,7 @@ import shutil
|
||||
import time
|
||||
import builtins
|
||||
from termcolor import colored
|
||||
from utils.color_scheme import color_text
|
||||
from utils.input_utils import timed_input
|
||||
|
||||
from password_manager.encryption import EncryptionManager
|
||||
@@ -1832,76 +1833,90 @@ class PasswordManager:
|
||||
return
|
||||
|
||||
etype = entry.get("type", entry.get("kind", EntryType.PASSWORD.value))
|
||||
print(colored(f"Index: {index}", "cyan"))
|
||||
print(color_text(f"Index: {index}", "index"))
|
||||
if etype == EntryType.TOTP.value:
|
||||
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||
print(color_text(f" Label: {entry.get('label', '')}", "index"))
|
||||
print(
|
||||
colored(
|
||||
color_text(f" Derivation Index: {entry.get('index', index)}", "index")
|
||||
)
|
||||
print(
|
||||
color_text(
|
||||
f" Period: {entry.get('period', 30)}s Digits: {entry.get('digits', 6)}",
|
||||
"cyan",
|
||||
"index",
|
||||
)
|
||||
)
|
||||
notes = entry.get("notes", "")
|
||||
if notes:
|
||||
print(colored(f" Notes: {notes}", "cyan"))
|
||||
print(color_text(f" Notes: {notes}", "index"))
|
||||
elif etype == EntryType.SEED.value:
|
||||
print(colored(" Type: Seed Phrase", "cyan"))
|
||||
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||
print(colored(f" Words: {entry.get('words', 24)}", "cyan"))
|
||||
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||
print(color_text(" Type: Seed Phrase", "index"))
|
||||
print(color_text(f" Label: {entry.get('label', '')}", "index"))
|
||||
print(color_text(f" Words: {entry.get('words', 24)}", "index"))
|
||||
print(
|
||||
color_text(f" Derivation Index: {entry.get('index', index)}", "index")
|
||||
)
|
||||
notes = entry.get("notes", "")
|
||||
if notes:
|
||||
print(colored(f" Notes: {notes}", "cyan"))
|
||||
print(color_text(f" Notes: {notes}", "index"))
|
||||
elif etype == EntryType.SSH.value:
|
||||
print(colored(" Type: SSH Key", "cyan"))
|
||||
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||
print(color_text(" Type: SSH Key", "index"))
|
||||
print(color_text(f" Label: {entry.get('label', '')}", "index"))
|
||||
print(
|
||||
color_text(f" Derivation Index: {entry.get('index', index)}", "index")
|
||||
)
|
||||
notes = entry.get("notes", "")
|
||||
if notes:
|
||||
print(colored(f" Notes: {notes}", "cyan"))
|
||||
print(color_text(f" Notes: {notes}", "index"))
|
||||
elif etype == EntryType.PGP.value:
|
||||
print(colored(" Type: PGP Key", "cyan"))
|
||||
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||
print(colored(f" Key Type: {entry.get('key_type', 'ed25519')}", "cyan"))
|
||||
print(color_text(" Type: PGP Key", "index"))
|
||||
print(color_text(f" Label: {entry.get('label', '')}", "index"))
|
||||
print(
|
||||
color_text(f" Key Type: {entry.get('key_type', 'ed25519')}", "index")
|
||||
)
|
||||
uid = entry.get("user_id", "")
|
||||
if uid:
|
||||
print(colored(f" User ID: {uid}", "cyan"))
|
||||
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||
print(color_text(f" User ID: {uid}", "index"))
|
||||
print(
|
||||
color_text(f" Derivation Index: {entry.get('index', index)}", "index")
|
||||
)
|
||||
notes = entry.get("notes", "")
|
||||
if notes:
|
||||
print(colored(f" Notes: {notes}", "cyan"))
|
||||
print(color_text(f" Notes: {notes}", "index"))
|
||||
elif etype == EntryType.NOSTR.value:
|
||||
print(colored(" Type: Nostr Key", "cyan"))
|
||||
print(colored(f" Label: {entry.get('label', '')}", "cyan"))
|
||||
print(colored(f" Derivation Index: {entry.get('index', index)}", "cyan"))
|
||||
print(color_text(" Type: Nostr Key", "index"))
|
||||
print(color_text(f" Label: {entry.get('label', '')}", "index"))
|
||||
print(
|
||||
color_text(f" Derivation Index: {entry.get('index', index)}", "index")
|
||||
)
|
||||
notes = entry.get("notes", "")
|
||||
if notes:
|
||||
print(colored(f" Notes: {notes}", "cyan"))
|
||||
print(color_text(f" Notes: {notes}", "index"))
|
||||
else:
|
||||
website = entry.get("label", entry.get("website", ""))
|
||||
username = entry.get("username", "")
|
||||
url = entry.get("url", "")
|
||||
blacklisted = entry.get("blacklisted", False)
|
||||
print(colored(f" Label: {website}", "cyan"))
|
||||
print(colored(f" Username: {username or 'N/A'}", "cyan"))
|
||||
print(colored(f" URL: {url or 'N/A'}", "cyan"))
|
||||
print(colored(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "cyan"))
|
||||
print(color_text(f" Label: {website}", "index"))
|
||||
print(color_text(f" Username: {username or 'N/A'}", "index"))
|
||||
print(color_text(f" URL: {url or 'N/A'}", "index"))
|
||||
print(
|
||||
color_text(f" Blacklisted: {'Yes' if blacklisted else 'No'}", "index")
|
||||
)
|
||||
print("-" * 40)
|
||||
|
||||
def handle_list_entries(self) -> None:
|
||||
"""List entries and optionally show details."""
|
||||
try:
|
||||
while True:
|
||||
print("\nList Entries:")
|
||||
print("1. All")
|
||||
print("2. Passwords")
|
||||
print("3. 2FA (TOTP)")
|
||||
print("4. SSH Key")
|
||||
print("5. Seed Phrase")
|
||||
print("6. Nostr Key Pair")
|
||||
print("7. PGP")
|
||||
print("8. Back")
|
||||
print(color_text("\nList Entries:", "menu"))
|
||||
print(color_text("1. All", "menu"))
|
||||
print(color_text("2. Passwords", "menu"))
|
||||
print(color_text("3. 2FA (TOTP)", "menu"))
|
||||
print(color_text("4. SSH Key", "menu"))
|
||||
print(color_text("5. Seed Phrase", "menu"))
|
||||
print(color_text("6. Nostr Key Pair", "menu"))
|
||||
print(color_text("7. PGP", "menu"))
|
||||
print(color_text("8. Back", "menu"))
|
||||
choice = input("Select entry type: ").strip()
|
||||
if choice == "1":
|
||||
filter_kind = None
|
||||
@@ -2602,34 +2617,37 @@ class PasswordManager:
|
||||
print(colored("No statistics available.", "red"))
|
||||
return
|
||||
|
||||
print(colored("\n=== Seed Profile Stats ===", "yellow"))
|
||||
print(colored(f"Total entries: {stats['total_entries']}", "cyan"))
|
||||
print(color_text("\n=== Seed Profile Stats ===", "stats"))
|
||||
print(color_text(f"Total entries: {stats['total_entries']}", "stats"))
|
||||
for etype, count in stats["entries"].items():
|
||||
print(colored(f" {etype}: {count}", "cyan"))
|
||||
print(colored(f"Relays configured: {stats['relay_count']}", "cyan"))
|
||||
print(color_text(f" {etype}: {count}", "stats"))
|
||||
print(color_text(f"Relays configured: {stats['relay_count']}", "stats"))
|
||||
print(
|
||||
colored(
|
||||
f"Backups: {stats['backup_count']} (dir: {stats['backup_dir']})", "cyan"
|
||||
color_text(
|
||||
f"Backups: {stats['backup_count']} (dir: {stats['backup_dir']})",
|
||||
"stats",
|
||||
)
|
||||
)
|
||||
if stats.get("additional_backup_path"):
|
||||
print(
|
||||
colored(f"Additional backup: {stats['additional_backup_path']}", "cyan")
|
||||
color_text(
|
||||
f"Additional backup: {stats['additional_backup_path']}", "stats"
|
||||
)
|
||||
)
|
||||
print(colored(f"Schema version: {stats['schema_version']}", "cyan"))
|
||||
print(color_text(f"Schema version: {stats['schema_version']}", "stats"))
|
||||
print(
|
||||
colored(
|
||||
color_text(
|
||||
f"Database checksum ok: {'yes' if stats['checksum_ok'] else 'no'}",
|
||||
"cyan",
|
||||
"stats",
|
||||
)
|
||||
)
|
||||
print(
|
||||
colored(
|
||||
color_text(
|
||||
f"Script checksum ok: {'yes' if stats['script_checksum_ok'] else 'no'}",
|
||||
"cyan",
|
||||
"stats",
|
||||
)
|
||||
)
|
||||
print(colored(f"Snapshot chunks: {stats['chunk_count']}", "cyan"))
|
||||
print(colored(f"Pending deltas: {stats['pending_deltas']}", "cyan"))
|
||||
print(color_text(f"Snapshot chunks: {stats['chunk_count']}", "stats"))
|
||||
print(color_text(f"Pending deltas: {stats['pending_deltas']}", "stats"))
|
||||
if stats.get("delta_since"):
|
||||
print(colored(f"Latest delta id: {stats['delta_since']}", "cyan"))
|
||||
print(color_text(f"Latest delta id: {stats['delta_since']}", "stats"))
|
||||
|
32
src/utils/color_scheme.py
Normal file
32
src/utils/color_scheme.py
Normal file
@@ -0,0 +1,32 @@
|
||||
"""Utility functions for SeedPass CLI color scheme."""
|
||||
|
||||
from termcolor import colored
|
||||
|
||||
|
||||
# ANSI escape for 256-color orange (color code 208)
|
||||
_ORANGE = "\033[38;5;208m"
|
||||
_RESET = "\033[0m"
|
||||
|
||||
|
||||
def _apply_orange(text: str) -> str:
|
||||
"""Return text wrapped in ANSI codes for orange."""
|
||||
return f"{_ORANGE}{text}{_RESET}"
|
||||
|
||||
|
||||
# Mapping of semantic color categories to actual colors
|
||||
_COLOR_MAP = {
|
||||
"deterministic": "red",
|
||||
"imported": "orange",
|
||||
"index": "yellow",
|
||||
"menu": "blue",
|
||||
"stats": "green",
|
||||
"default": "white",
|
||||
}
|
||||
|
||||
|
||||
def color_text(text: str, category: str = "default") -> str:
|
||||
"""Colorize ``text`` according to the given category."""
|
||||
color = _COLOR_MAP.get(category, "white")
|
||||
if color == "orange":
|
||||
return _apply_orange(text)
|
||||
return colored(text, color)
|
Reference in New Issue
Block a user