mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-09 15:58:48 +00:00
Merge pull request #229 from PR0M3TH3AN/codex/automate-installation-of-xclip-or-alternative
Improve clipboard handling
This commit is contained in:
10
README.md
10
README.md
@@ -110,6 +110,16 @@ pip install --upgrade pip
|
|||||||
pip install -r src/requirements.txt
|
pip install -r src/requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Linux Clipboard Support
|
||||||
|
|
||||||
|
On Linux, `pyperclip` relies on external utilities like `xclip` or `xsel`.
|
||||||
|
SeedPass will attempt to install **xclip** automatically if neither tool is
|
||||||
|
available. If the automatic installation fails, you can install it manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get install xclip
|
||||||
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
After installing dependencies and activating your virtual environment, launch
|
After installing dependencies and activating your virtual environment, launch
|
||||||
|
@@ -19,9 +19,8 @@ from nostr.client import NostrClient
|
|||||||
from password_manager.entry_types import EntryType
|
from password_manager.entry_types import EntryType
|
||||||
from constants import INACTIVITY_TIMEOUT, initialize_app
|
from constants import INACTIVITY_TIMEOUT, initialize_app
|
||||||
from utils.password_prompt import PasswordPromptError
|
from utils.password_prompt import PasswordPromptError
|
||||||
from utils import timed_input
|
from utils import timed_input, copy_to_clipboard
|
||||||
from local_bip85.bip85 import Bip85Error
|
from local_bip85.bip85 import Bip85Error
|
||||||
import pyperclip
|
|
||||||
|
|
||||||
|
|
||||||
colorama_init()
|
colorama_init()
|
||||||
@@ -852,7 +851,7 @@ def main(argv: list[str] | None = None) -> int:
|
|||||||
)
|
)
|
||||||
print(code)
|
print(code)
|
||||||
try:
|
try:
|
||||||
pyperclip.copy(code)
|
copy_to_clipboard(code, password_manager.clipboard_clear_delay)
|
||||||
print(colored("Code copied to clipboard", "green"))
|
print(colored("Code copied to clipboard", "green"))
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.warning(f"Clipboard copy failed: {exc}")
|
logging.warning(f"Clipboard copy failed: {exc}")
|
||||||
|
@@ -21,6 +21,7 @@ def make_pm(search_results, entry=None, totp_code="123456"):
|
|||||||
nostr_client=SimpleNamespace(close_client_pool=lambda: None),
|
nostr_client=SimpleNamespace(close_client_pool=lambda: None),
|
||||||
parent_seed="seed",
|
parent_seed="seed",
|
||||||
inactivity_timeout=1,
|
inactivity_timeout=1,
|
||||||
|
clipboard_clear_delay=45,
|
||||||
)
|
)
|
||||||
return pm
|
return pm
|
||||||
|
|
||||||
@@ -58,7 +59,9 @@ def test_totp_command(monkeypatch, capsys):
|
|||||||
monkeypatch.setattr(main, "configure_logging", lambda: None)
|
monkeypatch.setattr(main, "configure_logging", lambda: None)
|
||||||
monkeypatch.setattr(main, "initialize_app", lambda: None)
|
monkeypatch.setattr(main, "initialize_app", lambda: None)
|
||||||
monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None)
|
monkeypatch.setattr(main.signal, "signal", lambda *a, **k: None)
|
||||||
monkeypatch.setattr(main.pyperclip, "copy", lambda v: called.setdefault("val", v))
|
monkeypatch.setattr(
|
||||||
|
main, "copy_to_clipboard", lambda v, d: called.setdefault("val", v)
|
||||||
|
)
|
||||||
rc = main.main(["totp", "ex"])
|
rc = main.main(["totp", "ex"])
|
||||||
assert rc == 0
|
assert rc == 0
|
||||||
out = capsys.readouterr().out
|
out = capsys.readouterr().out
|
||||||
|
@@ -1,10 +1,40 @@
|
|||||||
import threading
|
import threading
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
|
||||||
import pyperclip
|
import pyperclip
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_clipboard() -> None:
|
||||||
|
"""Attempt to ensure a clipboard mechanism is available."""
|
||||||
|
try:
|
||||||
|
pyperclip.copy("")
|
||||||
|
except pyperclip.PyperclipException as exc:
|
||||||
|
if sys.platform.startswith("linux"):
|
||||||
|
if shutil.which("xclip") is None and shutil.which("xsel") is None:
|
||||||
|
apt = shutil.which("apt-get") or shutil.which("apt")
|
||||||
|
if apt:
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
["sudo", apt, "install", "-y", "xclip"], check=True
|
||||||
|
)
|
||||||
|
pyperclip.copy("")
|
||||||
|
return
|
||||||
|
except Exception as install_exc: # pragma: no cover - system dep
|
||||||
|
logger.warning(
|
||||||
|
"Automatic xclip installation failed: %s", install_exc
|
||||||
|
)
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
|
||||||
def copy_to_clipboard(text: str, timeout: int) -> None:
|
def copy_to_clipboard(text: str, timeout: int) -> None:
|
||||||
"""Copy text to the clipboard and clear after timeout seconds if unchanged."""
|
"""Copy text to the clipboard and clear after timeout seconds if unchanged."""
|
||||||
|
|
||||||
|
_ensure_clipboard()
|
||||||
pyperclip.copy(text)
|
pyperclip.copy(text)
|
||||||
|
|
||||||
def clear_clipboard() -> None:
|
def clear_clipboard() -> None:
|
||||||
|
Reference in New Issue
Block a user