basic-password-manager 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/PKG-INFO +11 -4
  2. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/README.md +9 -2
  3. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/PKG-INFO +11 -4
  4. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/SOURCES.txt +1 -0
  5. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/cli.py +23 -1
  6. basic_password_manager-0.2.3/pw_manager/logo.py +88 -0
  7. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/tui.py +21 -3
  8. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pyproject.toml +5 -2
  9. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/LICENSE +0 -0
  10. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/dependency_links.txt +0 -0
  11. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/entry_points.txt +0 -0
  12. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/requires.txt +0 -0
  13. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/basic_password_manager.egg-info/top_level.txt +0 -0
  14. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/__init__.py +0 -0
  15. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/crypto.py +0 -0
  16. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/pw_manager/vault.py +0 -0
  17. {basic_password_manager-0.2.2 → basic_password_manager-0.2.3}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basic-password-manager
3
- Version: 0.2.2
4
- Summary: Local encrypted password manager that lives in your terminal
3
+ Version: 0.2.3
4
+ Summary: The Paladin — a local encrypted password manager that lives in your terminal
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/Daisentaur/password-manager
7
7
  Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
@@ -13,11 +13,17 @@ Requires-Dist: cryptography
13
13
  Requires-Dist: textual
14
14
  Dynamic: license-file
15
15
 
16
- # pw-manager
16
+ <p align="center">
17
+ <img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
18
+ </p>
19
+
20
+ <h1 align="center">The Paladin</h1>
21
+
22
+ <p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
17
23
 
18
24
  A local, encrypted password manager that lives in your terminal. No browser,
19
25
  no cloud, no daemon — one encrypted file on disk and a small Python CLI in
20
- front of it.
26
+ front of it. (Installs as `basic-password-manager`; answers to `pw`.)
21
27
 
22
28
  ```
23
29
  $ python pw.py get github
@@ -74,6 +80,7 @@ Or use the subcommands:
74
80
  | `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
75
81
  | `pw gen -l 32` | just print a random password |
76
82
  | `pw import passwords.csv` | import a browser CSV export (see below) |
83
+ | `pw about` | meet the knight |
77
84
 
78
85
  The vault lives at `~/.local/share/pw-manager/vault`. Set the `PW_VAULT`
79
86
  environment variable to put it somewhere else.
@@ -1,8 +1,14 @@
1
- # pw-manager
1
+ <p align="center">
2
+ <img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
3
+ </p>
4
+
5
+ <h1 align="center">The Paladin</h1>
6
+
7
+ <p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
2
8
 
3
9
  A local, encrypted password manager that lives in your terminal. No browser,
4
10
  no cloud, no daemon — one encrypted file on disk and a small Python CLI in
5
- front of it.
11
+ front of it. (Installs as `basic-password-manager`; answers to `pw`.)
6
12
 
7
13
  ```
8
14
  $ python pw.py get github
@@ -59,6 +65,7 @@ Or use the subcommands:
59
65
  | `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
60
66
  | `pw gen -l 32` | just print a random password |
61
67
  | `pw import passwords.csv` | import a browser CSV export (see below) |
68
+ | `pw about` | meet the knight |
62
69
 
63
70
  The vault lives at `~/.local/share/pw-manager/vault`. Set the `PW_VAULT`
64
71
  environment variable to put it somewhere else.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basic-password-manager
3
- Version: 0.2.2
4
- Summary: Local encrypted password manager that lives in your terminal
3
+ Version: 0.2.3
4
+ Summary: The Paladin — a local encrypted password manager that lives in your terminal
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/Daisentaur/password-manager
7
7
  Project-URL: Changelog, https://github.com/Daisentaur/password-manager/blob/main/CHANGELOG.md
@@ -13,11 +13,17 @@ Requires-Dist: cryptography
13
13
  Requires-Dist: textual
14
14
  Dynamic: license-file
15
15
 
16
- # pw-manager
16
+ <p align="center">
17
+ <img src="assets/logo.png" width="160" alt="The Paladin — a pixel-art knight" />
18
+ </p>
19
+
20
+ <h1 align="center">The Paladin</h1>
21
+
22
+ <p align="center"><em>Your passwords, guarded locally. No browser, no cloud, no mercy.</em></p>
17
23
 
18
24
  A local, encrypted password manager that lives in your terminal. No browser,
19
25
  no cloud, no daemon — one encrypted file on disk and a small Python CLI in
20
- front of it.
26
+ front of it. (Installs as `basic-password-manager`; answers to `pw`.)
21
27
 
22
28
  ```
23
29
  $ python pw.py get github
@@ -74,6 +80,7 @@ Or use the subcommands:
74
80
  | `pw find "dt bank"` | search names, usernames, notes — every word must match, any order |
75
81
  | `pw gen -l 32` | just print a random password |
76
82
  | `pw import passwords.csv` | import a browser CSV export (see below) |
83
+ | `pw about` | meet the knight |
77
84
 
78
85
  The vault lives at `~/.local/share/pw-manager/vault`. Set the `PW_VAULT`
79
86
  environment variable to put it somewhere else.
@@ -10,5 +10,6 @@ basic_password_manager.egg-info/top_level.txt
10
10
  pw_manager/__init__.py
11
11
  pw_manager/cli.py
12
12
  pw_manager/crypto.py
13
+ pw_manager/logo.py
13
14
  pw_manager/tui.py
14
15
  pw_manager/vault.py
@@ -47,7 +47,28 @@ def cmd_init(args):
47
47
  sys.exit(f"vault already exists at {VAULT_PATH}")
48
48
  os.makedirs(os.path.dirname(VAULT_PATH), exist_ok=True)
49
49
  vault.save(VAULT_PATH, ask_master(confirm=True), {})
50
- print(f"empty vault created at {VAULT_PATH}")
50
+ if sys.stdout.isatty():
51
+ from . import logo
52
+
53
+ print(logo.says(f"empty vault created at {VAULT_PATH}"))
54
+ else:
55
+ print(f"empty vault created at {VAULT_PATH}")
56
+
57
+
58
+ def cmd_about(args):
59
+ from importlib.metadata import PackageNotFoundError, version
60
+
61
+ try:
62
+ v = version("basic-password-manager")
63
+ except PackageNotFoundError:
64
+ v = "dev" # running from source without an install
65
+ if sys.stdout.isatty():
66
+ from . import logo
67
+
68
+ print(logo.ansi())
69
+ print(f"The Paladin ♞ v{v}")
70
+ print("your passwords, guarded locally — no browser, no cloud, no mercy")
71
+ print("https://github.com/Daisentaur/password-manager")
51
72
 
52
73
 
53
74
  def cmd_add(args):
@@ -198,6 +219,7 @@ def main():
198
219
  f.add_argument("term")
199
220
  i = sub.add_parser("import", help="import a browser CSV export")
200
221
  i.add_argument("csv_file")
222
+ sub.add_parser("about", help="meet the knight")
201
223
 
202
224
  args = p.parse_args()
203
225
  if args.command is None:
@@ -0,0 +1,88 @@
1
+ """The Paladin, as terminal pixel art.
2
+
3
+ Extracted once from assets/logo.png (each letter below is one pixel; dev-time
4
+ script, no image libraries at runtime). Rendered with half-block characters:
5
+ one character cell shows two pixels — top half foreground, bottom half
6
+ background.
7
+ """
8
+
9
+ from rich.style import Style
10
+ from rich.text import Text
11
+
12
+ PALETTE = {
13
+ "k": (0, 0, 0), "s": (140, 139, 147), "w": (194, 191, 198),
14
+ "o": (243, 243, 245), "g": (86, 85, 91), "d": (32, 32, 32),
15
+ "l": (158, 157, 163), "b": (246, 120, 59), "r": (143, 64, 47),
16
+ }
17
+
18
+ ROWS = [
19
+ "......kkk.........",
20
+ ".....kswwkk.......",
21
+ "....wwwwwwwk......",
22
+ "...kwwwwwwwok.....",
23
+ "...kowwwwwwwk.....",
24
+ "...kgdooodgwk.....",
25
+ "...kdddddddwk...k.",
26
+ "...kwwdddwwwk..kok",
27
+ "..kkwwwddwwwk.kgl.",
28
+ ".ookoowddookk.glk.",
29
+ "kgskkkwookkkbblk..",
30
+ "kssswwkkksskkrk...",
31
+ ".kkkssgggwwksk....",
32
+ "...ksswwwwwkk.....",
33
+ "...kgkkkkggk......",
34
+ "....k.....k.......",
35
+ ]
36
+
37
+
38
+ def _pixel_pairs():
39
+ """Yield rows of (top_color, bottom_color) tuples, one per char cell."""
40
+ for top, bottom in zip(ROWS[::2], ROWS[1::2]):
41
+ yield [
42
+ (PALETTE.get(t), PALETTE.get(b))
43
+ for t, b in zip(top, bottom)
44
+ ]
45
+
46
+
47
+ def ansi(indent: str = "") -> str:
48
+ """The knight as a raw ANSI truecolor string, for print()."""
49
+ out = []
50
+ for row in _pixel_pairs():
51
+ line = indent
52
+ for top, bottom in row:
53
+ if top is None and bottom is None:
54
+ line += "\x1b[0m "
55
+ elif bottom is None:
56
+ line += "\x1b[0m\x1b[38;2;%d;%d;%dm▀" % top
57
+ elif top is None:
58
+ line += "\x1b[0m\x1b[38;2;%d;%d;%dm▄" % bottom
59
+ else:
60
+ line += "\x1b[38;2;%d;%d;%dm\x1b[48;2;%d;%d;%dm▀" % (top + bottom)
61
+ out.append(line + "\x1b[0m")
62
+ return "\n".join(out)
63
+
64
+
65
+ def rich_text() -> Text:
66
+ """The knight as a Rich Text, for Textual widgets."""
67
+ text = Text()
68
+ for row in _pixel_pairs():
69
+ for top, bottom in row:
70
+ if top is None and bottom is None:
71
+ text.append(" ")
72
+ elif bottom is None:
73
+ text.append("▀", Style(color="rgb(%d,%d,%d)" % top))
74
+ elif top is None:
75
+ text.append("▄", Style(color="rgb(%d,%d,%d)" % bottom))
76
+ else:
77
+ text.append("▀", Style(color="rgb(%d,%d,%d)" % top, bgcolor="rgb(%d,%d,%d)" % bottom))
78
+ text.append("\n")
79
+ return text
80
+
81
+
82
+ def says(message: str) -> str:
83
+ """Cowsay, but it's a knight."""
84
+ border = "_" * (len(message) + 2)
85
+ return (
86
+ f" {border}\n< {message} >\n {'-' * (len(message) + 2)}\n"
87
+ f" \\\n" + ansi(indent=" ")
88
+ )
@@ -17,11 +17,14 @@ from textual.screen import ModalScreen, Screen
17
17
  from textual.theme import Theme
18
18
  from textual.widgets import DataTable, Footer, Input, Label, Static
19
19
 
20
- from . import vault
20
+ from . import logo, vault
21
21
  from .cli import VAULT_PATH, generate, import_csv, to_clipboard
22
22
 
23
23
  THEME_FILE = os.path.expanduser("~/.local/share/pw-manager/theme")
24
24
 
25
+ APP_NAME = "The Paladin"
26
+ APP_ICON = "♞"
27
+
25
28
  TUXEDO_THEMES = [
26
29
  Theme(
27
30
  name="muted-slate",
@@ -73,7 +76,8 @@ class UnlockScreen(Screen):
73
76
 
74
77
  def compose(self) -> ComposeResult:
75
78
  with Vertical(id="unlock-box"):
76
- yield Label("pw-manager", id="unlock-title")
79
+ yield Static(logo.rich_text(), id="unlock-logo")
80
+ yield Label(f"{APP_ICON} {APP_NAME}", id="unlock-title")
77
81
  yield Input(password=True, placeholder="master password", id="master")
78
82
  yield Static("", id="unlock-error")
79
83
 
@@ -212,6 +216,7 @@ class MainScreen(Screen):
212
216
  ]
213
217
 
214
218
  def compose(self) -> ComposeResult:
219
+ yield Static(f" {APP_ICON} {APP_NAME}", id="topbar")
215
220
  yield Input(placeholder="/ to search", id="search")
216
221
  yield DataTable(id="table", cursor_type="row")
217
222
  yield Footer()
@@ -362,13 +367,19 @@ class VaultCommands(Provider):
362
367
 
363
368
 
364
369
  class PwApp(App):
365
- TITLE = "pw-manager"
370
+ TITLE = APP_NAME
366
371
  COMMANDS = App.COMMANDS | {VaultCommands}
367
372
 
368
373
  # Colors come from $theme-variables so every theme applies everywhere;
369
374
  # widgets without rules here (table, footer, toasts) use Textual's own
370
375
  # theme-aware defaults.
371
376
  CSS = """
377
+ #topbar {
378
+ height: 1;
379
+ background: $panel;
380
+ color: $primary;
381
+ text-style: bold;
382
+ }
372
383
  #unlock-box {
373
384
  align: center middle;
374
385
  width: 44;
@@ -377,7 +388,14 @@ class PwApp(App):
377
388
  background: $surface;
378
389
  border: round $panel;
379
390
  }
391
+ #unlock-logo {
392
+ width: 100%;
393
+ text-align: center;
394
+ margin-bottom: 1;
395
+ }
380
396
  #unlock-title {
397
+ width: 100%;
398
+ text-align: center;
381
399
  color: $primary;
382
400
  text-style: bold;
383
401
  margin-bottom: 1;
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "basic-password-manager"
7
- version = "0.2.2"
8
- description = "Local encrypted password manager that lives in your terminal"
7
+ version = "0.2.3"
8
+ description = "The Paladin — a local encrypted password manager that lives in your terminal"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  requires-python = ">=3.10"
@@ -15,6 +15,9 @@ dependencies = [
15
15
  "textual",
16
16
  ]
17
17
 
18
+ [tool.setuptools]
19
+ packages = ["pw_manager"]
20
+
18
21
  [project.scripts]
19
22
  pw = "pw_manager.cli:main"
20
23