diff --git a/docs/dev_control_center.md b/docs/dev_control_center.md index 2241c9e3..c69db5aa 100644 --- a/docs/dev_control_center.md +++ b/docs/dev_control_center.md @@ -91,8 +91,12 @@ Progress update (2026-03-02): - `nostr-reset-sync-state` - `nostr-fresh-namespace` - service wrappers and tests added +- Nostr pubkey utility parity landed in TUI v2: + - `npub` (alias: `nostr-pubkey`) + - displays active profile npub (+ QR payload) in sensitive panel + - tests added for success and validation paths - Current next implementation target: - - dedicated utility-style `npub` display command parity + - close “Full legacy workflow coverage” in parity checklist with bug-bash evidence ## 6) Working Rule diff --git a/docs/tui_v2_legacy_parity_matrix.md b/docs/tui_v2_legacy_parity_matrix.md index 90f95955..8f670d0c 100644 --- a/docs/tui_v2_legacy_parity_matrix.md +++ b/docs/tui_v2_legacy_parity_matrix.md @@ -50,7 +50,7 @@ Purpose: map legacy interactive workflows to Textual TUI v2 equivalents and isol | Export/import database | Settings | `db-export`, `db-import` | Implemented | | | Export TOTP codes | Settings | `totp-export` | Implemented | | | Backup/reveal parent seed | Settings | `parent-seed-backup [path] [password]` | Implemented | | -| Display npub | Main utility flow | No direct dedicated command | Partial | Possible via entry reveal for nostr entry; direct utility-style command absent. | +| Display npub | Main utility flow | `npub` (alias: `nostr-pubkey`) | Implemented | Displays active profile npub and QR payload in the sensitive panel. | | View archived-only list | Settings/List flow | `h` cycle archive scope or `archive-filter ` | Implemented | Supports active-only/default, all, and archived-only views. | | Reset Nostr sync state | Settings > Nostr | `nostr-reset-sync-state` | Implemented | Added to TUI v2 palette + tests. | | Start fresh Nostr namespace | Settings > Nostr | `nostr-fresh-namespace` | Implemented | Added to TUI v2 palette + tests. | @@ -59,8 +59,8 @@ Purpose: map legacy interactive workflows to Textual TUI v2 equivalents and isol ## Prioritized Gap Queue -1. Low: dedicated utility-style npub display command in v2. +1. Revalidate and close “Full legacy workflow coverage” in `docs/tui_v2_parity_checklist.md`. ## Suggested implementation order -1. Dedicated `npub` utility command + tests. +1. Run parity bug-bash + checklist update to move full legacy workflow coverage to complete. diff --git a/memory-update.md b/memory-update.md index 9714b0d3..9d8291bd 100644 --- a/memory-update.md +++ b/memory-update.md @@ -651,3 +651,25 @@ - Planning docs updated: - `docs/tui_v2_legacy_parity_matrix.md` marks archived-only view parity implemented. - `docs/dev_control_center.md` next target updated to dedicated `npub` utility command parity. + +## 2026-03-02 Next parity slice landed: dedicated Nostr npub utility command +- Implemented utility-style Nostr pubkey command in TUI v2 palette: + - `npub` + - alias: `nostr-pubkey` +- Behavior in `src/seedpass/tui_v2/app.py`: + - validates no-arg usage (`Usage: npub`) + - requires Nostr service availability and `get_pubkey()` support + - fetches active profile pubkey and renders it in `#secret-detail` + - includes QR payload rendering for `nostr:` when QR runtime is available + - status line reports `Displayed active Nostr pubkey` +- Help/discovery text updated to include `npub` in palette placeholder/help/actions copy. +- Test coverage updates: + - `src/tests/test_tui_v2_textual_interactions.py` + - success path in profiles/settings command flow (`npub` status + panel assertion) + - unavailable service path (`Nostr service unavailable`) + - invalid usage path (`npub now` -> `Usage: npub`) + - `src/tests/test_tui_v2_action_matrix.py` + - matrix command-path coverage invokes `npub`. +- Parity docs update: + - `docs/tui_v2_legacy_parity_matrix.md` now marks `Display npub` as Implemented. + - `docs/dev_control_center.md` progress notes now include `npub` parity completion and shift next target to checklist closure. diff --git a/src/seedpass/tui_v2/app.py b/src/seedpass/tui_v2/app.py index b4c75491..2e3d7c85 100644 --- a/src/seedpass/tui_v2/app.py +++ b/src/seedpass/tui_v2/app.py @@ -301,7 +301,7 @@ def launch_tui2( "setting-quick-unlock | setting-timeout | " "setting-kdf-iterations | setting-kdf-mode | " "relay-list | relay-add | relay-rm | " - "relay-reset | nostr-reset-sync-state | nostr-fresh-namespace | " + "relay-reset | npub | nostr-reset-sync-state | nostr-fresh-namespace | " "sync-now | sync-bg | " "checksum-verify | checksum-update | db-export | db-import | " "totp-export | parent-seed-backup [path] [password] | " @@ -630,7 +630,7 @@ def launch_tui2( "- docs: edit/save/export", "- 6 toggle dedicated 2FA board", "- profile/relay/sync/settings", - "- nostr reset/fresh namespace", + "- npub + nostr reset/fresh namespace", "- checksum/db/totp/seed backup", "- managed-load/managed-exit session", "- l cycle link relation", @@ -1403,7 +1403,7 @@ def launch_tui2( "setting-secret, setting-offline, setting-quick-unlock, setting-timeout, " "setting-kdf-iterations, setting-kdf-mode, " "relay-list, relay-add, relay-rm, relay-reset, " - "nostr-reset-sync-state, nostr-fresh-namespace, " + "npub, nostr-reset-sync-state, nostr-fresh-namespace, " "sync-now, sync-bg, " "checksum-verify, checksum-update, db-export, db-import, " "totp-export, parent-seed-backup, managed-load, managed-exit, " @@ -2542,6 +2542,42 @@ def launch_tui2( ) return + if cmd in {"npub", "nostr-pubkey"}: + if self._nostr_service is None: + self._set_status("Nostr service unavailable") + return + if args: + self._set_status("Usage: npub") + return + getter = getattr(self._nostr_service, "get_pubkey", None) + if not callable(getter): + self._set_status("Nostr service does not support pubkey display") + return + try: + pubkey = str(getter()).strip() + if not pubkey: + self._set_status("No active Nostr pubkey available") + return + panel = f"Active Nostr pubkey\n\nnpub: {pubkey}" + try: + panel = ( + f"{panel}\n\nQR (nostr:npub)\n\n" + f"{render_qr_ascii(f'nostr:{pubkey}')}" + ) + except Exception: + pass + self._set_secret_panel(panel) + self._clear_failure() + self._set_status("Displayed active Nostr pubkey") + except Exception as exc: + self._record_failure( + "npub failed", + exc, + retry=lambda: self._run_palette_command(raw), + hint="Press 'x' to retry.", + ) + return + if cmd in {"relay-rm", "relay-remove"}: if self._nostr_service is None: self._set_status("Nostr service unavailable") diff --git a/src/tests/test_tui_v2_action_matrix.py b/src/tests/test_tui_v2_action_matrix.py index 830c2b93..40b10e4c 100644 --- a/src/tests/test_tui_v2_action_matrix.py +++ b/src/tests/test_tui_v2_action_matrix.py @@ -552,6 +552,7 @@ async def test_tui2_matrix_actions_palette_events_and_guards() -> None: app._run_palette_command("setting-kdf-iterations 100000") app._run_palette_command("setting-kdf-mode pbkdf2") app._run_palette_command("relay-list") + app._run_palette_command("npub") app._run_palette_command("relay-add wss://relay.example") app._run_palette_command("relay-rm 1") app._run_palette_command("relay-reset") diff --git a/src/tests/test_tui_v2_textual_interactions.py b/src/tests/test_tui_v2_textual_interactions.py index 66a832d8..78df0499 100644 --- a/src/tests/test_tui_v2_textual_interactions.py +++ b/src/tests/test_tui_v2_textual_interactions.py @@ -457,6 +457,10 @@ class FakeNostrService: def __init__(self, relays: list[str]) -> None: self.relays = list(relays) self.account_idx = 0 + self.pubkey = "npub1seedpassprofile" + + def get_pubkey(self) -> str: + return str(self.pubkey) def list_relays(self) -> list[str]: return list(self.relays) @@ -1413,6 +1417,11 @@ async def test_tui2_textual_profiles_and_settings_palette_commands() -> None: await pilot.pause() assert "Relays (2): wss://r1, wss://r2" in _status_text(app) + app._run_palette_command("npub") + await pilot.pause() + assert "Displayed active Nostr pubkey" in _status_text(app) + assert "npub: npub1seedpassprofile" in _widget_text(app, "#secret-detail") + app._run_palette_command("relay-add wss://r3") await pilot.pause() assert "Added relay wss://r3" in _status_text(app) @@ -1588,6 +1597,10 @@ async def test_tui2_textual_profiles_and_settings_palette_validation() -> None: await pilot.pause() assert "Nostr service unavailable" in _status_text(app) + app._run_palette_command("npub") + await pilot.pause() + assert "Nostr service unavailable" in _status_text(app) + app._run_palette_command("relay-reset") await pilot.pause() assert "Nostr service unavailable" in _status_text(app) @@ -1659,6 +1672,10 @@ async def test_tui2_textual_profiles_and_settings_palette_validation() -> None: await pilot.pause() assert "Usage: nostr-fresh-namespace" in _status_text(app) + app._run_palette_command("npub now") + await pilot.pause() + assert "Usage: npub" in _status_text(app) + app._run_palette_command("managed-load 1 2") await pilot.pause() assert "Usage: managed-load (optional: entry_id)" in _status_text(app)