mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-07 14:58:56 +00:00
Add custom seed profile names
This commit is contained in:
@@ -378,8 +378,12 @@ SeedPass allows you to manage multiple seed profiles (previously referred to as
|
||||
3. Enter the number corresponding to the seed profile you wish to switch to.
|
||||
4. Enter the master password associated with that seed profile.
|
||||
|
||||
- **List All Seed Profiles:**
|
||||
- **List All Seed Profiles:**
|
||||
In the **Profiles** menu, choose "List All Seed Profiles" to view all existing profiles.
|
||||
- **Set Seed Profile Name:**
|
||||
In the **Profiles** menu, choose "Set Seed Profile Name" to assign an optional
|
||||
label to the currently selected profile. The name is stored locally and shown
|
||||
alongside the fingerprint in menus.
|
||||
|
||||
**Note:** The term "seed profile" is used to represent different sets of seeds you can manage within SeedPass. This provides an intuitive way to handle multiple identities or sets of passwords.
|
||||
|
||||
|
@@ -368,6 +368,8 @@ SeedPass allows you to manage multiple seed profiles (previously referred to as
|
||||
|
||||
- **List All Seed Profiles:**
|
||||
- In the **Profiles** menu, choose "List All Seed Profiles" to view all existing profiles.
|
||||
- **Set Seed Profile Name:**
|
||||
- In the **Profiles** menu, choose "Set Seed Profile Name" to assign a label to the current profile. The name is stored locally and shown next to the fingerprint.
|
||||
|
||||
**Note:** The term "seed profile" is used to represent different sets of seeds you can manage within SeedPass. This provides an intuitive way to handle multiple identities or sets of passwords.
|
||||
|
||||
|
31
src/main.py
31
src/main.py
@@ -151,7 +151,8 @@ def handle_switch_fingerprint(password_manager: PasswordManager):
|
||||
|
||||
print(colored("Available Seed Profiles:", "cyan"))
|
||||
for idx, fp in enumerate(fingerprints, start=1):
|
||||
print(colored(f"{idx}. {fp}", "cyan"))
|
||||
label = password_manager.fingerprint_manager.display_name(fp)
|
||||
print(colored(f"{idx}. {label}", "cyan"))
|
||||
|
||||
choice = input("Select a seed profile by number to switch: ").strip()
|
||||
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints)):
|
||||
@@ -195,7 +196,8 @@ def handle_remove_fingerprint(password_manager: PasswordManager):
|
||||
|
||||
print(colored("Available Seed Profiles:", "cyan"))
|
||||
for idx, fp in enumerate(fingerprints, start=1):
|
||||
print(colored(f"{idx}. {fp}", "cyan"))
|
||||
label = password_manager.fingerprint_manager.display_name(fp)
|
||||
print(colored(f"{idx}. {label}", "cyan"))
|
||||
|
||||
choice = input("Select a seed profile by number to remove: ").strip()
|
||||
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints)):
|
||||
@@ -239,7 +241,8 @@ def handle_list_fingerprints(password_manager: PasswordManager):
|
||||
|
||||
print(colored("Available Seed Profiles:", "cyan"))
|
||||
for fp in fingerprints:
|
||||
print(colored(f"- {fp}", "cyan"))
|
||||
label = password_manager.fingerprint_manager.display_name(fp)
|
||||
print(colored(f"- {label}", "cyan"))
|
||||
pause()
|
||||
except Exception as e:
|
||||
logging.error(f"Error listing seed profiles: {e}", exc_info=True)
|
||||
@@ -641,6 +644,25 @@ def handle_set_additional_backup_location(pm: PasswordManager) -> None:
|
||||
print(colored(f"Error: {e}", "red"))
|
||||
|
||||
|
||||
def handle_set_profile_name(pm: PasswordManager) -> None:
|
||||
"""Set or clear the custom name for the current seed profile."""
|
||||
fp = getattr(pm.fingerprint_manager, "current_fingerprint", None)
|
||||
if not fp:
|
||||
print(colored("No seed profile selected.", "red"))
|
||||
return
|
||||
current = pm.fingerprint_manager.get_name(fp)
|
||||
if current:
|
||||
print(colored(f"Current name: {current}", "cyan"))
|
||||
else:
|
||||
print(colored("No custom name set.", "cyan"))
|
||||
value = input("Enter new name (leave blank to remove): ").strip()
|
||||
if pm.fingerprint_manager.set_name(fp, value or None):
|
||||
if value:
|
||||
print(colored("Name updated.", "green"))
|
||||
else:
|
||||
print(colored("Name removed.", "green"))
|
||||
|
||||
|
||||
def handle_toggle_secret_mode(pm: PasswordManager) -> None:
|
||||
"""Toggle secret mode and adjust clipboard delay."""
|
||||
cfg = pm.config_manager
|
||||
@@ -756,6 +778,7 @@ def handle_profiles_menu(password_manager: PasswordManager) -> None:
|
||||
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. Set Seed Profile Name", "menu"))
|
||||
choice = input("Select an option or press Enter to go back: ").strip()
|
||||
password_manager.update_activity()
|
||||
if choice == "1":
|
||||
@@ -767,6 +790,8 @@ def handle_profiles_menu(password_manager: PasswordManager) -> None:
|
||||
handle_remove_fingerprint(password_manager)
|
||||
elif choice == "4":
|
||||
handle_list_fingerprints(password_manager)
|
||||
elif choice == "5":
|
||||
handle_set_profile_name(password_manager)
|
||||
elif not choice:
|
||||
break
|
||||
else:
|
||||
|
@@ -329,8 +329,13 @@ class PasswordManager:
|
||||
|
||||
print(colored("\nAvailable Seed Profiles:", "cyan"))
|
||||
for idx, fp in enumerate(fingerprints, start=1):
|
||||
label = (
|
||||
self.fingerprint_manager.display_name(fp)
|
||||
if hasattr(self.fingerprint_manager, "display_name")
|
||||
else fp
|
||||
)
|
||||
marker = " *" if fp == current else ""
|
||||
print(colored(f"{idx}. {fp}{marker}", "cyan"))
|
||||
print(colored(f"{idx}. {label}{marker}", "cyan"))
|
||||
|
||||
print(colored(f"{len(fingerprints)+1}. Add a new seed profile", "cyan"))
|
||||
|
||||
@@ -532,7 +537,12 @@ class PasswordManager:
|
||||
print(colored("\nAvailable Seed Profiles:", "cyan"))
|
||||
fingerprints = self.fingerprint_manager.list_fingerprints()
|
||||
for idx, fp in enumerate(fingerprints, start=1):
|
||||
print(colored(f"{idx}. {fp}", "cyan"))
|
||||
display = (
|
||||
self.fingerprint_manager.display_name(fp)
|
||||
if hasattr(self.fingerprint_manager, "display_name")
|
||||
else fp
|
||||
)
|
||||
print(colored(f"{idx}. {display}", "cyan"))
|
||||
|
||||
choice = input("Select a seed profile by number to switch: ").strip()
|
||||
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints)):
|
||||
@@ -680,7 +690,12 @@ class PasswordManager:
|
||||
|
||||
print(colored("Available Seed Profiles:", "cyan"))
|
||||
for idx, fp in enumerate(fingerprints, start=1):
|
||||
print(colored(f"{idx}. {fp}", "cyan"))
|
||||
label = (
|
||||
self.fingerprint_manager.display_name(fp)
|
||||
if hasattr(self.fingerprint_manager, "display_name")
|
||||
else fp
|
||||
)
|
||||
print(colored(f"{idx}. {label}", "cyan"))
|
||||
|
||||
choice = input("Select a seed profile by number: ").strip()
|
||||
if not choice.isdigit() or not (1 <= int(choice) <= len(fingerprints)):
|
||||
|
@@ -34,7 +34,11 @@ class FingerprintManager:
|
||||
self.app_dir = app_dir
|
||||
self.fingerprints_file = self.app_dir / "fingerprints.json"
|
||||
self._ensure_app_directory()
|
||||
self.fingerprints, self.current_fingerprint = self._load_fingerprints()
|
||||
(
|
||||
self.fingerprints,
|
||||
self.current_fingerprint,
|
||||
self.names,
|
||||
) = self._load_fingerprints()
|
||||
|
||||
def get_current_fingerprint_dir(self) -> Optional[Path]:
|
||||
"""
|
||||
@@ -62,25 +66,26 @@ class FingerprintManager:
|
||||
)
|
||||
raise
|
||||
|
||||
def _load_fingerprints(self) -> tuple[list[str], Optional[str]]:
|
||||
"""Return stored fingerprints and the last used fingerprint."""
|
||||
def _load_fingerprints(self) -> tuple[list[str], Optional[str], dict[str, str]]:
|
||||
"""Return stored fingerprints, the last used fingerprint, and name mapping."""
|
||||
try:
|
||||
if self.fingerprints_file.exists():
|
||||
with open(self.fingerprints_file, "r") as f:
|
||||
data = json.load(f)
|
||||
fingerprints = data.get("fingerprints", [])
|
||||
current = data.get("last_used")
|
||||
names = data.get("names", {})
|
||||
logger.debug(
|
||||
f"Loaded fingerprints: {fingerprints} (last used: {current})"
|
||||
)
|
||||
return fingerprints, current
|
||||
return fingerprints, current, names
|
||||
logger.debug(
|
||||
"fingerprints.json not found. Initializing empty fingerprint list."
|
||||
)
|
||||
return [], None
|
||||
return [], None, {}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load fingerprints: {e}", exc_info=True)
|
||||
return [], None
|
||||
return [], None, {}
|
||||
|
||||
def _save_fingerprints(self):
|
||||
"""
|
||||
@@ -92,6 +97,7 @@ class FingerprintManager:
|
||||
{
|
||||
"fingerprints": self.fingerprints,
|
||||
"last_used": self.current_fingerprint,
|
||||
"names": self.names,
|
||||
},
|
||||
f,
|
||||
indent=4,
|
||||
@@ -116,6 +122,7 @@ class FingerprintManager:
|
||||
fingerprint = generate_fingerprint(seed_phrase)
|
||||
if fingerprint and fingerprint not in self.fingerprints:
|
||||
self.fingerprints.append(fingerprint)
|
||||
self.names.setdefault(fingerprint, "")
|
||||
self.current_fingerprint = fingerprint
|
||||
self._save_fingerprints()
|
||||
logger.info(f"Fingerprint {fingerprint} added successfully.")
|
||||
@@ -144,6 +151,7 @@ class FingerprintManager:
|
||||
if fingerprint in self.fingerprints:
|
||||
try:
|
||||
self.fingerprints.remove(fingerprint)
|
||||
self.names.pop(fingerprint, None)
|
||||
if self.current_fingerprint == fingerprint:
|
||||
self.current_fingerprint = (
|
||||
self.fingerprints[0] if self.fingerprints else None
|
||||
@@ -198,6 +206,26 @@ class FingerprintManager:
|
||||
logger.error(f"Fingerprint {fingerprint} not found.")
|
||||
return False
|
||||
|
||||
def set_name(self, fingerprint: str, name: str | None) -> bool:
|
||||
"""Set a custom name for a fingerprint."""
|
||||
if fingerprint not in self.fingerprints:
|
||||
return False
|
||||
if name:
|
||||
self.names[fingerprint] = name
|
||||
else:
|
||||
self.names.pop(fingerprint, None)
|
||||
self._save_fingerprints()
|
||||
return True
|
||||
|
||||
def get_name(self, fingerprint: str) -> Optional[str]:
|
||||
"""Return the custom name for ``fingerprint`` if set."""
|
||||
return self.names.get(fingerprint) or None
|
||||
|
||||
def display_name(self, fingerprint: str) -> str:
|
||||
"""Return name and fingerprint for display."""
|
||||
name = self.get_name(fingerprint)
|
||||
return f"{name} ({fingerprint})" if name else fingerprint
|
||||
|
||||
def get_fingerprint_directory(self, fingerprint: str) -> Optional[Path]:
|
||||
"""
|
||||
Retrieves the directory path for a given fingerprint.
|
||||
|
@@ -8,6 +8,20 @@ from termcolor import colored
|
||||
from utils.color_scheme import color_text
|
||||
|
||||
|
||||
def format_profile(fingerprint: str | None, pm=None) -> str | None:
|
||||
"""Return display string for a fingerprint with optional custom name."""
|
||||
if not fingerprint:
|
||||
return None
|
||||
if pm and getattr(pm, "fingerprint_manager", None):
|
||||
try:
|
||||
name = pm.fingerprint_manager.get_name(fingerprint)
|
||||
if name:
|
||||
return f"{name} ({fingerprint})"
|
||||
except Exception:
|
||||
pass
|
||||
return fingerprint
|
||||
|
||||
|
||||
def clear_screen() -> None:
|
||||
"""Clear the terminal screen using an ANSI escape code."""
|
||||
print("\033c", end="")
|
||||
@@ -18,16 +32,17 @@ def clear_and_print_fingerprint(
|
||||
breadcrumb: str | None = None,
|
||||
parent_fingerprint: str | None = None,
|
||||
child_fingerprint: str | None = None,
|
||||
pm=None,
|
||||
) -> None:
|
||||
"""Clear the screen and optionally display the current fingerprint and path."""
|
||||
clear_screen()
|
||||
header_fp = None
|
||||
if parent_fingerprint and child_fingerprint:
|
||||
header_fp = f"{parent_fingerprint} > Managed Account > {child_fingerprint}"
|
||||
header_fp = f"{format_profile(parent_fingerprint, pm)} > Managed Account > {format_profile(child_fingerprint, pm)}"
|
||||
elif fingerprint:
|
||||
header_fp = fingerprint
|
||||
header_fp = format_profile(fingerprint, pm)
|
||||
elif parent_fingerprint or child_fingerprint:
|
||||
header_fp = parent_fingerprint or child_fingerprint
|
||||
header_fp = format_profile(parent_fingerprint or child_fingerprint, pm)
|
||||
if header_fp:
|
||||
header = f"Seed Profile: {header_fp}"
|
||||
if breadcrumb:
|
||||
@@ -36,15 +51,15 @@ def clear_and_print_fingerprint(
|
||||
|
||||
|
||||
def clear_and_print_profile_chain(
|
||||
fingerprints: list[str] | None, breadcrumb: str | None = None
|
||||
fingerprints: list[str] | None, breadcrumb: str | None = None, pm=None
|
||||
) -> None:
|
||||
"""Clear the screen and display a chain of fingerprints."""
|
||||
clear_screen()
|
||||
if not fingerprints:
|
||||
return
|
||||
chain = fingerprints[0]
|
||||
chain = format_profile(fingerprints[0], pm)
|
||||
for fp in fingerprints[1:]:
|
||||
chain += f" > Managed Account > {fp}"
|
||||
chain += f" > Managed Account > {format_profile(fp, pm)}"
|
||||
header = f"Seed Profile: {chain}"
|
||||
if breadcrumb:
|
||||
header += f" > {breadcrumb}"
|
||||
@@ -63,11 +78,11 @@ def clear_header_with_notification(
|
||||
clear_screen()
|
||||
header_fp = None
|
||||
if parent_fingerprint and child_fingerprint:
|
||||
header_fp = f"{parent_fingerprint} > Managed Account > {child_fingerprint}"
|
||||
header_fp = f"{format_profile(parent_fingerprint, pm)} > Managed Account > {format_profile(child_fingerprint, pm)}"
|
||||
elif fingerprint:
|
||||
header_fp = fingerprint
|
||||
header_fp = format_profile(fingerprint, pm)
|
||||
elif parent_fingerprint or child_fingerprint:
|
||||
header_fp = parent_fingerprint or child_fingerprint
|
||||
header_fp = format_profile(parent_fingerprint or child_fingerprint, pm)
|
||||
if header_fp:
|
||||
header = f"Seed Profile: {header_fp}"
|
||||
if breadcrumb:
|
||||
|
Reference in New Issue
Block a user