mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-06 12:52:04 +00:00
feat(tui-v2): polish action strip copy and rebalance dense layout
This commit is contained in:
1 parent
64971851ae
commit
0ec4b86076
4 files changed
+306
-34
No files matched your search
@@ -1073,3 +1073,96 @@ Still needed to match mockup behavior intent:
|
||||
- denser row/column presentation,
|
||||
- reduced decorative padding.
|
||||
3. Next implementation slices should target this density path to effectively fit more content per viewport.
|
||||
|
||||
## 29) Progress Update (2026-03-03, Slice: Password/Stored Password/Note Card Geometry)
|
||||
|
||||
Completed:
|
||||
|
||||
1. Password and Stored Password inspector boards now use stronger card sections:
|
||||
- `Credentials`
|
||||
- `Quick Actions`
|
||||
2. Note/Document board now uses the same card section framing:
|
||||
- `Content`
|
||||
- `Quick Actions`
|
||||
3. Increased note preview payload from 120 to 180 chars for better content-card parity in standard density.
|
||||
|
||||
Code references:
|
||||
|
||||
1. `src/seedpass/tui_v2/app.py` (`_board_card`, password/stored_password board, note/document board)
|
||||
|
||||
Regression coverage:
|
||||
|
||||
1. Updated note board metadata test to assert card framing.
|
||||
2. Added password board card-section regression test.
|
||||
|
||||
Validation:
|
||||
|
||||
1. `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
2. Result: `57 passed`.
|
||||
|
||||
Remaining strict-closeout slice order unchanged:
|
||||
|
||||
1. Final UI-board density pass (table rows + lower inspector balance)
|
||||
2. Final action-strip verb/microcopy polish
|
||||
|
||||
## 30) Progress Update (2026-03-03, Slice: UI-Board Density Rebalance)
|
||||
|
||||
Completed:
|
||||
|
||||
1. Dense/high-res layout now rebalances vertical space by context:
|
||||
- Idle (no selected entry, no editor, no 2FA board): `top-work=9fr`, `right=3fr`
|
||||
- Active inspect/edit/2FA: `top-work=8fr`, `right=4fr`
|
||||
2. Added explicit layout rebalance hook so selection and pane-mode transitions re-apply split without requiring window resize.
|
||||
3. Rebalance path wired through:
|
||||
- page render transitions (empty/non-empty)
|
||||
- entry selection/open flow
|
||||
- right-pane mode switches (view/edit/2FA)
|
||||
|
||||
Code references:
|
||||
|
||||
1. `src/seedpass/tui_v2/app.py` (`_update_responsive_layout`, `_refresh_layout_balance`, `_set_right_pane_mode`, `_render_current_page`, `_show_entry`)
|
||||
|
||||
Regression coverage:
|
||||
|
||||
1. Added dense-mode split behavior test:
|
||||
- `test_tui2_textual_hires_density_rebalances_idle_vs_selected`
|
||||
|
||||
Validation:
|
||||
|
||||
1. `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
2. Result: `58 passed`.
|
||||
|
||||
Remaining strict-closeout slice:
|
||||
|
||||
1. Final action-strip verb/microcopy polish.
|
||||
|
||||
## 31) Progress Update (2026-03-03, Slice: Action-Strip Verb/Microcopy Polish)
|
||||
|
||||
Completed:
|
||||
|
||||
1. Context row wording standardized to verb-first labels with key hints:
|
||||
- `Reveal (v)`
|
||||
- `QR (g)`
|
||||
- `Edit (e)` / `Edit Doc (e)`
|
||||
- `Archive (a)`
|
||||
- `Save (Ctrl+S)`
|
||||
- `2FA Board (6)`
|
||||
2. Managed account row now retains explicit session controls while matching the same style:
|
||||
- `managed-load`
|
||||
- `managed-exit`
|
||||
- plus reveal/qr/edit/archive with key hints.
|
||||
3. Click routing remains compatible after copy update:
|
||||
- row-2 parser now recognizes both symbol-led and verb-led tokens (`v`/`Reveal`, `g`/`QR`, etc.).
|
||||
|
||||
Code references:
|
||||
|
||||
1. `src/seedpass/tui_v2/app.py` (`_update_action_strip`, `_action_strip_context_action`)
|
||||
|
||||
Regression coverage:
|
||||
|
||||
1. Updated action-strip click + context-update tests to assert verb-led labels.
|
||||
|
||||
Validation:
|
||||
|
||||
1. `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
2. Result: `58 passed`.
|
||||
@@ -110,3 +110,68 @@
|
||||
- Focused and gate suite green:
|
||||
- `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
- Result: `56 passed`.
|
||||
|
||||
## Session Update (2026-03-03, Slice 2)
|
||||
|
||||
### UI-board density and hierarchy polish
|
||||
- Dense high-resolution mode now keeps `#grid-heading` at 4 rows instead of 3 to avoid header/divider clipping in large viewports.
|
||||
- Refined dense top-left header hierarchy to reduce line noise:
|
||||
- retained explicit `Fingerprint` and `Path`
|
||||
- consolidated session/profile/managed state into one line
|
||||
- kept scope line focused on active filters only
|
||||
|
||||
### Regression updates
|
||||
- Updated `test_tui2_textual_hires_density_compacts_vertical_chrome` to assert the new dense grid-heading height (`4`).
|
||||
|
||||
### Verification
|
||||
- Focused TUI v2 gate remains green:
|
||||
- `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
- Result: `56 passed`.
|
||||
|
||||
## Session Update (2026-03-03, Slice 3)
|
||||
|
||||
### Board geometry parity pass
|
||||
- Added reusable ASCII card renderer for inspector sections (`_board_card`).
|
||||
- Password and stored-password boards now render framed `Credentials` and `Quick Actions` blocks.
|
||||
- Note/document boards now render framed `Content` and `Quick Actions` blocks.
|
||||
- Increased note/document preview length (120 -> 180 chars) for better mockup-like content emphasis.
|
||||
|
||||
### Regression coverage
|
||||
- Extended note metadata board test to assert card framing.
|
||||
- Added password board card-section test.
|
||||
|
||||
### Verification
|
||||
- Focused TUI v2 gate:
|
||||
- `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
- Result: `57 passed`.
|
||||
|
||||
## Session Update (2026-03-03, Slice 4)
|
||||
|
||||
### UI-board density rebalancing
|
||||
- Added dynamic dense/high-res vertical split:
|
||||
- idle: `top-work=9fr`, `right=3fr`
|
||||
- active inspect/edit/2FA: `top-work=8fr`, `right=4fr`
|
||||
- Added `_refresh_layout_balance()` and invoked it during key state transitions so layout rebalance is state-driven, not resize-driven.
|
||||
|
||||
### Regression coverage
|
||||
- Added `test_tui2_textual_hires_density_rebalances_idle_vs_selected`.
|
||||
|
||||
### Verification
|
||||
- Focused TUI v2 gate:
|
||||
- `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
- Result: `58 passed`.
|
||||
|
||||
## Session Update (2026-03-03, Slice 5)
|
||||
|
||||
### Action-strip microcopy polish
|
||||
- Standardized context action wording to verb-first with key hints across entry kinds.
|
||||
- Preserved managed account session actions while aligning adjacent wording style.
|
||||
- Expanded row-2 click parser to recognize verb-led labels so copy changes do not break mouse interactions.
|
||||
|
||||
### Regression coverage
|
||||
- Updated action-strip context tests to match polished copy.
|
||||
|
||||
### Verification
|
||||
- Focused TUI v2 gate:
|
||||
- `poetry run pytest -q src/tests/test_tui_v2_textual_interactions.py src/tests/test_tui_v2_action_matrix.py`
|
||||
- Result: `58 passed`.
|
||||
+82
-29
@@ -884,21 +884,23 @@ def launch_tui2(
|
||||
kind_l = self._entry_kind(self._selected_entry)
|
||||
kind = kind_l
|
||||
if kind_l in {"password", "stored_password"}:
|
||||
context = "Entry ▣ v Reveal ▣ g QR ▣ e Edit ▣ a Archive"
|
||||
context = "Entry ▣ Reveal (v) ▣ QR (g) ▣ Edit (e) ▣ Archive (a)"
|
||||
elif kind_l == "managed_account":
|
||||
context = "Entry ▣ managed-load ▣ managed-exit ▣ v Reveal (confirm) ▣ g QR ▣ e Edit ▣ a Archive"
|
||||
context = "Entry ▣ managed-load ▣ managed-exit ▣ Reveal (v confirm) ▣ QR (g) ▣ Edit (e) ▣ Archive (a)"
|
||||
elif kind_l == "seed":
|
||||
context = "Entry ▣ v Reveal (confirm) ▣ g QR ▣ e Edit ▣ a Archive"
|
||||
context = "Entry ▣ Reveal (v confirm) ▣ QR (g) ▣ Edit (e) ▣ Archive (a)"
|
||||
elif kind_l == "totp":
|
||||
context = "Entry ▣ 6 2FA Board ▣ v Reveal ▣ g QR ▣ a Archive"
|
||||
context = "Entry ▣ 2FA Board (6) ▣ Reveal (v) ▣ QR (g) ▣ Archive (a)"
|
||||
elif kind_l in {"ssh", "pgp"}:
|
||||
context = "Entry ▣ v Reveal (confirm) ▣ a Archive ▣ copy/export-field"
|
||||
context = "Entry ▣ Reveal (v confirm) ▣ Archive (a) ▣ copy/export-field"
|
||||
elif kind_l == "nostr":
|
||||
context = "Entry ▣ v Reveal ▣ g QR (public/private) ▣ a Archive"
|
||||
context = "Entry ▣ Reveal (v) ▣ QR (g public/private) ▣ Archive (a)"
|
||||
elif kind_l in {"document", "note"}:
|
||||
context = "Entry ▣ e Edit Doc ▣ Ctrl+S Save ▣ a Archive ▣ doc-export"
|
||||
context = (
|
||||
"Entry ▣ Edit Doc (e) ▣ Save (Ctrl+S) ▣ Archive (a) ▣ doc-export"
|
||||
)
|
||||
elif kind_l == "key_value":
|
||||
context = "Entry ▣ set-field/clear-field ▣ a Archive ▣ notes/tags"
|
||||
context = "Entry ▣ set-field/clear-field ▣ Archive (a) ▣ notes/tags"
|
||||
else:
|
||||
context = "Select an entry to view context actions."
|
||||
if kind:
|
||||
@@ -985,15 +987,15 @@ def launch_tui2(
|
||||
if not token:
|
||||
return None
|
||||
lead = token.split()[0].strip().lower()
|
||||
if lead == "v":
|
||||
if lead in {"v", "reveal", "rev"}:
|
||||
return "reveal"
|
||||
if lead == "g":
|
||||
if lead in {"g", "qr"}:
|
||||
return "qr"
|
||||
if lead == "e":
|
||||
if lead in {"e", "edit"}:
|
||||
return "edit"
|
||||
if lead == "a":
|
||||
if lead in {"a", "archive", "arch"}:
|
||||
return "archive"
|
||||
if lead == "6":
|
||||
if lead in {"6", "2fa"}:
|
||||
return "totp_board"
|
||||
if lead in {"managed-load", "managed_load", "ml"}:
|
||||
return "managed_load"
|
||||
@@ -1243,13 +1245,30 @@ def launch_tui2(
|
||||
right.styles.height = "5fr"
|
||||
activity.add_class("hidden")
|
||||
elif height >= 52:
|
||||
top_work.styles.height = "8fr"
|
||||
right.styles.height = "4fr"
|
||||
if dense_hires and (
|
||||
self._selected_entry_id is None
|
||||
and not self.editing_document
|
||||
and not self.totp_board_open
|
||||
):
|
||||
# In dense high-res idle state, bias vertical space to the grid.
|
||||
top_work.styles.height = "9fr"
|
||||
right.styles.height = "3fr"
|
||||
else:
|
||||
top_work.styles.height = "8fr"
|
||||
right.styles.height = "4fr"
|
||||
activity.remove_class("hidden")
|
||||
activity.styles.height = 4
|
||||
else:
|
||||
top_work.styles.height = "7fr"
|
||||
right.styles.height = "4fr"
|
||||
if dense_hires and (
|
||||
self._selected_entry_id is None
|
||||
and not self.editing_document
|
||||
and not self.totp_board_open
|
||||
):
|
||||
top_work.styles.height = "8fr"
|
||||
right.styles.height = "3fr"
|
||||
else:
|
||||
top_work.styles.height = "7fr"
|
||||
right.styles.height = "4fr"
|
||||
activity.remove_class("hidden")
|
||||
activity.styles.height = 3
|
||||
|
||||
@@ -1260,7 +1279,7 @@ def launch_tui2(
|
||||
ribbon.styles.height = 2
|
||||
status.styles.height = 2
|
||||
action_strip.styles.height = 2
|
||||
grid_heading.styles.height = 3
|
||||
grid_heading.styles.height = 4
|
||||
inspector_heading.styles.height = 2
|
||||
else:
|
||||
brand.styles.height = 3
|
||||
@@ -1280,6 +1299,11 @@ def launch_tui2(
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _refresh_layout_balance(self) -> None:
|
||||
self._update_responsive_layout(
|
||||
width=self._viewport_width, height=self._viewport_height
|
||||
)
|
||||
|
||||
def _refresh_doc_edit_help(self) -> None:
|
||||
marker = "*" if self._doc_dirty else "clean"
|
||||
self.query_one("#doc-edit-help", Static).update(
|
||||
@@ -1535,8 +1559,15 @@ def launch_tui2(
|
||||
"SeedPass ◈ TUI v2",
|
||||
f"Fingerprint: {current_fp}",
|
||||
f"Path: {self._session_breadcrumb_text()}",
|
||||
f"FP: {current_fp} | Profiles: {profiles_loaded} | Session: {session_state}",
|
||||
f"Scope: filter={self.filter_kind} arch={self.archive_scope} links={self.link_relation_filter} managed={managed_state}",
|
||||
(
|
||||
"Session: "
|
||||
f"{session_state} | Profiles: {profiles_loaded} | Managed: {managed_state}"
|
||||
),
|
||||
(
|
||||
"Scope: "
|
||||
f"filter={self.filter_kind} arch={self.archive_scope} "
|
||||
f"links={self.link_relation_filter}"
|
||||
),
|
||||
self._selected_summary(),
|
||||
(
|
||||
f"Results: {len(self._all_results)} | "
|
||||
@@ -1742,6 +1773,7 @@ def launch_tui2(
|
||||
else:
|
||||
self._set_status("No entries match current filter/search")
|
||||
self._update_filters_panel()
|
||||
self._refresh_layout_balance()
|
||||
return
|
||||
|
||||
self._result_page, start, end, _total_pages = pagination_window(
|
||||
@@ -1791,6 +1823,7 @@ def launch_tui2(
|
||||
pass
|
||||
self._set_status("Entries loaded. Select an entry to inspect.")
|
||||
self._update_filters_panel()
|
||||
self._refresh_layout_balance()
|
||||
|
||||
def _entry_detail_text(self, entry: dict[str, Any]) -> str:
|
||||
payload = truncate_entry_for_display(
|
||||
@@ -1865,6 +1898,14 @@ def launch_tui2(
|
||||
f"Notes: {notes_preview}",
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def _board_card(title: str, rows: list[str]) -> list[str]:
|
||||
width = max([len(title), *[len(row) for row in rows], 12])
|
||||
top = f"+- {title} " + "-" * max(1, width - len(title) - 2) + "+"
|
||||
body = [f"| {row.ljust(width)} |" for row in rows]
|
||||
bottom = "+" + "-" * (width + 2) + "+"
|
||||
return [top, *body, bottom]
|
||||
|
||||
def _entry_board_header(self, entry: dict[str, Any]) -> list[str]:
|
||||
entry_id = (
|
||||
self._selected_entry_id
|
||||
@@ -1919,6 +1960,13 @@ def launch_tui2(
|
||||
archived = "Yes" if bool(entry.get("archived", False)) else "No"
|
||||
index_num = str(entry.get("index", "(auto)"))
|
||||
label = str(entry.get("label") or "(untitled)")
|
||||
credential_rows = [
|
||||
"Password*: hidden (v reveal, g qr)",
|
||||
f"Username*: {username}",
|
||||
f"URL : {url}",
|
||||
f"Length : {length} chars",
|
||||
]
|
||||
operation_rows = [action_row]
|
||||
lines = [
|
||||
f"Entry #{entry_id} {label}",
|
||||
"-" * max(24, len(label) + 10),
|
||||
@@ -1926,12 +1974,9 @@ def launch_tui2(
|
||||
f"Kind: {kind} | Modified: {modified} | Archived: {archived}",
|
||||
f"Index Num*: {index_num}",
|
||||
"Login Fields",
|
||||
"Password*: hidden (v reveal, g qr)",
|
||||
f"Username*: {username}",
|
||||
f"URL : {url}",
|
||||
f"Length : {length} chars",
|
||||
*self._board_card("Credentials", credential_rows),
|
||||
"Operations",
|
||||
action_row,
|
||||
*self._board_card("Quick Actions", operation_rows),
|
||||
*self._notes_tags_panel_hint(
|
||||
tags_text=tags_text, notes_text=notes_text
|
||||
),
|
||||
@@ -1948,7 +1993,7 @@ def launch_tui2(
|
||||
content = str(entry.get("content") or "")
|
||||
preview = content.strip()
|
||||
if preview:
|
||||
preview = preview.replace("\n", " ")[:120]
|
||||
preview = preview.replace("\n", " ")[:180]
|
||||
else:
|
||||
preview = "(empty)"
|
||||
entry_id = (
|
||||
@@ -1964,6 +2009,10 @@ def launch_tui2(
|
||||
)
|
||||
archived = "Yes" if bool(entry.get("archived", False)) else "No"
|
||||
index_num = str(entry.get("index", "(auto)"))
|
||||
document_rows = [
|
||||
f"Content Length: {len(content)} chars",
|
||||
f"Preview : {preview}",
|
||||
]
|
||||
lines = [
|
||||
f"Entry #{entry_id} {str(entry.get('label') or '(untitled)')}",
|
||||
"-" * 28,
|
||||
@@ -1971,10 +2020,9 @@ def launch_tui2(
|
||||
f"Kind: {kind} | Modified: {modified} | Archived: {archived}",
|
||||
f"Index Num*: {index_num} | File Type: {file_type}",
|
||||
"Document Fields",
|
||||
f"Content Length: {len(content)} chars",
|
||||
f"Preview : {preview}",
|
||||
*self._board_card("Content", document_rows),
|
||||
"Operations",
|
||||
"▣ Edit ▣ Export",
|
||||
*self._board_card("Quick Actions", ["▣ Edit ▣ Export"]),
|
||||
*self._notes_tags_panel_hint(
|
||||
tags_text=tags_text, notes_text=notes_text
|
||||
),
|
||||
@@ -2397,6 +2445,7 @@ def launch_tui2(
|
||||
self._update_links_panel()
|
||||
self._update_filters_panel()
|
||||
self._set_status(f"Selected entry {entry_index}")
|
||||
self._refresh_layout_balance()
|
||||
except Exception as exc:
|
||||
self.query_one("#entry-detail", Static).update(
|
||||
f"Failed to load entry {entry_index}: {exc}"
|
||||
@@ -2415,6 +2464,7 @@ def launch_tui2(
|
||||
retry=lambda: self._show_entry(entry_index),
|
||||
hint="Press 'x' to retry.",
|
||||
)
|
||||
self._refresh_layout_balance()
|
||||
|
||||
def _update_links_panel(self) -> None:
|
||||
if self._service is None or self._selected_entry_id is None:
|
||||
@@ -2926,6 +2976,7 @@ def launch_tui2(
|
||||
self.totp_board_open = False
|
||||
self.editing_document = True
|
||||
self._update_inspector_heading()
|
||||
self._refresh_layout_balance()
|
||||
return
|
||||
if mode_token == "totp":
|
||||
editor.add_class("hidden")
|
||||
@@ -2937,6 +2988,7 @@ def launch_tui2(
|
||||
self.editing_document = False
|
||||
self._refresh_totp_board(force_reload=True)
|
||||
self._update_inspector_heading()
|
||||
self._refresh_layout_balance()
|
||||
return
|
||||
# Default "view": entry board + side panels, no editor/2FA board.
|
||||
if mode_token != "view":
|
||||
@@ -2950,6 +3002,7 @@ def launch_tui2(
|
||||
self.totp_board_open = False
|
||||
self.editing_document = False
|
||||
self._update_inspector_heading()
|
||||
self._refresh_layout_balance()
|
||||
|
||||
def _set_palette_visible(self, visible: bool) -> None:
|
||||
palette = self.query_one("#command-palette", Input)
|
||||
|
||||
@@ -1317,7 +1317,7 @@ async def test_tui2_textual_hires_density_compacts_vertical_chrome() -> None:
|
||||
assert str(ribbon.styles.height) == "2"
|
||||
assert str(status.styles.height) == "2"
|
||||
assert str(action_strip.styles.height) == "2"
|
||||
assert str(grid_heading.styles.height) == "3"
|
||||
assert str(grid_heading.styles.height) == "4"
|
||||
assert "Dense" in _widget_text(app, "#action-strip")
|
||||
assert "S Set" in _widget_text(app, "#action-strip")
|
||||
assert "Keys:" in _widget_text(app, "#filters")
|
||||
@@ -1338,6 +1338,37 @@ async def test_tui2_textual_hires_density_compacts_vertical_chrome() -> None:
|
||||
assert "Dense" not in _widget_text(app, "#action-strip")
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_hires_density_rebalances_idle_vs_selected() -> None:
|
||||
service = FakeEntryService(
|
||||
[
|
||||
{"id": 1, "kind": "password", "label": "Login 1", "length": 16},
|
||||
{"id": 2, "kind": "document", "label": "Doc 2", "content": "hello"},
|
||||
]
|
||||
)
|
||||
app = _build_app(service)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause()
|
||||
top_work = app.query_one("#top-work")
|
||||
right = app.query_one("#right")
|
||||
|
||||
app._update_responsive_layout(width=220, height=55)
|
||||
await pilot.pause()
|
||||
assert str(top_work.styles.height) == "9fr"
|
||||
assert str(right.styles.height) == "3fr"
|
||||
|
||||
app._run_palette_command("open 1")
|
||||
await pilot.pause()
|
||||
assert str(top_work.styles.height) == "8fr"
|
||||
assert str(right.styles.height) == "4fr"
|
||||
|
||||
app._set_totp_board_visible(True)
|
||||
await pilot.pause()
|
||||
assert str(top_work.styles.height) == "8fr"
|
||||
assert str(right.styles.height) == "4fr"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_dense_mode_reduces_inspector_helper_noise() -> None:
|
||||
service = FakeEntryService(
|
||||
@@ -1501,6 +1532,8 @@ async def test_tui2_textual_note_and_totp_boards_include_common_metadata() -> No
|
||||
assert "Kind: document | Modified:" in board
|
||||
assert "Index Num*:" in board
|
||||
assert "Document Fields" in board
|
||||
assert "+- Content " in board
|
||||
assert "+- Quick Actions " in board
|
||||
|
||||
app._run_palette_command("open 2")
|
||||
await pilot.pause()
|
||||
@@ -1511,6 +1544,34 @@ async def test_tui2_textual_note_and_totp_boards_include_common_metadata() -> No
|
||||
assert "2FA Fields" in board
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_password_board_uses_card_sections() -> None:
|
||||
service = FakeEntryService(
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"kind": "password",
|
||||
"label": "Login 1",
|
||||
"username": "alice",
|
||||
"url": "https://example.com",
|
||||
"length": 18,
|
||||
}
|
||||
]
|
||||
)
|
||||
app = _build_app(service)
|
||||
|
||||
async with app.run_test() as pilot:
|
||||
await pilot.pause()
|
||||
app._run_palette_command("open 1")
|
||||
await pilot.pause()
|
||||
board = _widget_text(app, "#entry-detail")
|
||||
assert "Password Board" in board
|
||||
assert "Login Fields" in board
|
||||
assert "+- Credentials " in board
|
||||
assert "Password*: hidden (v reveal, g qr)" in board
|
||||
assert "+- Quick Actions " in board
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_tui2_textual_filters_panel_tracks_selected_entry() -> None:
|
||||
service = FakeEntryService(
|
||||
@@ -1682,9 +1743,9 @@ async def test_tui2_textual_action_strip_click_routes_to_shortcuts() -> None:
|
||||
app._run_palette_command("open 1")
|
||||
await pilot.pause()
|
||||
action_line = _widget_text(app, "#action-strip").splitlines()[1]
|
||||
reveal_col = action_line.find("v Reveal")
|
||||
reveal_col = action_line.find("Reveal (v")
|
||||
if reveal_col < 0:
|
||||
reveal_col = action_line.find("v Rev")
|
||||
reveal_col = action_line.find("Rev (v")
|
||||
assert reveal_col >= 0
|
||||
handled = app._handle_action_strip_click(reveal_col + 1, 1)
|
||||
await pilot.pause()
|
||||
@@ -1707,13 +1768,13 @@ async def test_tui2_textual_action_strip_context_updates_for_selected_kind() ->
|
||||
app._run_palette_command("open 1")
|
||||
await pilot.pause()
|
||||
action_line = _widget_text(app, "#action-strip").splitlines()[1]
|
||||
assert ("v Reveal" in action_line) or ("v Rev" in action_line)
|
||||
assert ("Reveal (v)" in action_line) or ("Rev (v)" in action_line)
|
||||
assert "(password)" in action_line
|
||||
|
||||
app._run_palette_command("open 2")
|
||||
await pilot.pause()
|
||||
action_line = _widget_text(app, "#action-strip").splitlines()[1]
|
||||
assert "e Edit Doc" in action_line
|
||||
assert "Edit Doc (e)" in action_line
|
||||
assert "(document)" in action_line
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user