Merge branch 'overstory/builder-sortfilter/SeedPass-de11' into beta

This commit is contained in:
thePR0M3TH3AN committed 2026-03-10 15:14:13 -04:00
commit 842f0ca9c7
3 files changed
+201 -1

No files matched your search

+24
View File
@@ -930,6 +930,10 @@ class SeedPassTuiV3(App[None]):
Binding("g", "show_qr", "QR", show=False),
Binding("a", "toggle_archive", "Archive", show=False),
Binding("c", "copy_selected", "Copy", show=False),
Binding("n", "sort_label", "Sort:Name", show=False),
Binding("r", "sort_recent", "Sort:Recent", show=False),
Binding("k", "sort_kind", "Sort:Kind", show=False),
Binding("f", "clear_filter", "Clear Filter", show=False),
]
# Shared Reactive State
@@ -1299,6 +1303,26 @@ class SeedPassTuiV3(App[None]):
self.notify(f"Applied filter: {self.filter_kind}")
self.action_refresh()
def action_sort_label(self) -> None:
"""Keyboard shortcut: sort grid by label (A→Z)."""
self.action_set_search_sort("label_asc")
def action_sort_recent(self) -> None:
"""Keyboard shortcut: sort grid by most-recently modified."""
self.action_set_search_sort("modified_desc")
def action_sort_kind(self) -> None:
"""Keyboard shortcut: sort grid by entry kind."""
self.action_set_search_sort("kind")
def action_clear_filter(self) -> None:
"""Clear active filter, search query, and reset sort to relevance."""
self.filter_kind = "all"
self.search_query = ""
self.search_sort = "relevance"
self.notify("Filter and sort cleared")
self.action_refresh()
def action_open_palette(self) -> None:
"""Toggle the command palette."""
try:
+52 -1
View File
@@ -79,6 +79,11 @@ class GridToolbar(Horizontal):
margin-right: 1;
height: 1;
}
GridToolbar Button.active {
background: #007700;
color: #ffffff;
text-style: bold;
}
"""
def compose(self) -> ComposeResult:
@@ -93,11 +98,57 @@ class GridToolbar(Horizontal):
yield Button("Semantic", id="grid-mode-semantic")
yield Button("Relevance", id="grid-sort-relevance")
yield Button("Recent", id="grid-sort-modified_desc")
yield Button("Label", id="grid-sort-label_asc")
yield Button("Label ↑", id="grid-sort-label_asc")
yield Button("Kind", id="grid-sort-kind")
yield Button("Linked", id="grid-sort-most_linked")
yield Button("[X] Clear", id="grid-clear-filter")
def on_mount(self) -> None:
self.watch(self.app, "filter_kind", self._sync_active_states)
self.watch(self.app, "search_mode", self._sync_active_states)
self.watch(self.app, "search_sort", self._sync_active_states)
self._sync_active_states()
def _sync_active_states(self, *_args: Any) -> None:
"""Update button active CSS classes to reflect current app state."""
try:
app = self.app
filter_kind = app.filter_kind
search_mode = app.search_mode
search_sort = app.search_sort
filter_map = {
"grid-filter-all": filter_kind == "all",
"grid-filter-secrets": filter_kind == "secrets",
"grid-filter-docs": filter_kind == "docs",
"grid-filter-keys": filter_kind == "keys",
"grid-filter-2fa": filter_kind == "2fa",
"grid-mode-keyword": search_mode == "keyword",
"grid-mode-hybrid": search_mode == "hybrid",
"grid-mode-semantic": search_mode == "semantic",
"grid-sort-relevance": search_sort == "relevance",
"grid-sort-modified_desc": search_sort == "modified_desc",
"grid-sort-label_asc": search_sort == "label_asc",
"grid-sort-kind": search_sort == "kind",
"grid-sort-most_linked": search_sort == "most_linked",
}
for btn_id, is_active in filter_map.items():
try:
btn = self.query_one(f"#{btn_id}", Button)
if is_active:
btn.add_class("active")
else:
btn.remove_class("active")
except Exception:
pass
except Exception:
pass
def on_button_pressed(self, event: Button.Pressed) -> None:
button_id = event.button.id or ""
if button_id == "grid-clear-filter":
self.app.action_clear_filter()
return
if button_id.startswith("grid-filter-"):
self.app.action_set_kind_filter(button_id.removeprefix("grid-filter-"))
return
+125
View File
@@ -1095,3 +1095,128 @@ async def test_tui3_inspector_utility_hints_update_by_entry_kind() -> None:
await pilot.pause()
hints_text = str(hints_bar.render())
assert "Actions" not in hints_text
@pytest.mark.anyio
async def test_tui3_keyboard_sort_shortcuts_change_sort() -> None:
app, _entry, _vault, search = _build_app()
async with app.run_test() as pilot:
await pilot.pause()
# Default sort is relevance
assert app.search_sort == "relevance"
# n → sort by label_asc
await pilot.press("n")
await pilot.pause()
assert app.search_sort == "label_asc"
assert search.calls and search.calls[-1]["sort"] == "label_asc"
# r → sort by modified_desc
await pilot.press("r")
await pilot.pause()
assert app.search_sort == "modified_desc"
assert search.calls[-1]["sort"] == "modified_desc"
# k → sort by kind
await pilot.press("k")
await pilot.pause()
assert app.search_sort == "kind"
assert search.calls[-1]["sort"] == "kind"
@pytest.mark.anyio
async def test_tui3_clear_filter_button_resets_state() -> None:
app, _entry, _vault, search = _build_app()
async with app.run_test() as pilot:
await pilot.pause()
# Set non-default filter/sort/query state
app.action_set_kind_filter("docs")
app.action_set_search_sort("modified_desc")
app.action_search("Email")
await pilot.pause()
assert app.filter_kind == "docs"
assert app.search_sort == "modified_desc"
assert app.search_query == "Email"
# Press Clear button
app.screen.query_one("#grid-clear-filter", Button).press()
await pilot.pause()
assert app.filter_kind == "all"
assert app.search_sort == "relevance"
assert app.search_query == ""
@pytest.mark.anyio
async def test_tui3_clear_filter_keyboard_shortcut_resets_state() -> None:
app, _entry, _vault, _search = _build_app()
async with app.run_test() as pilot:
await pilot.pause()
app.action_set_kind_filter("keys")
app.action_set_search_sort("label_asc")
await pilot.pause()
assert app.filter_kind == "keys"
await pilot.press("f")
await pilot.pause()
assert app.filter_kind == "all"
assert app.search_sort == "relevance"
@pytest.mark.anyio
async def test_tui3_filter_sort_state_persists_across_screen_transitions() -> None:
"""Filter and sort state must survive pushing/popping overlay screens."""
app, _entry, _vault, search = _build_app()
async with app.run_test() as pilot:
await pilot.pause()
# Establish non-default state
app.action_set_kind_filter("secrets")
app.action_set_search_sort("modified_desc")
await pilot.pause()
assert app.filter_kind == "secrets"
assert app.search_sort == "modified_desc"
# Push a screen (atlas wayfinder)
app.action_open_atlas_wayfinder()
await pilot.pause()
assert isinstance(app.screen, AtlasWayfinderScreen)
# Pop back to main
app.pop_screen()
await pilot.pause()
# State must be unchanged
assert app.filter_kind == "secrets"
assert app.search_sort == "modified_desc"
# Grid refresh triggered after pop should use persisted sort
table = app.screen.query_one("#entry-data-table", DataTable)
table._refresh_data()
await pilot.pause()
assert search.calls and search.calls[-1]["sort"] == "modified_desc"
@pytest.mark.anyio
async def test_tui3_toolbar_active_class_reflects_app_state() -> None:
"""GridToolbar buttons must gain/lose the 'active' CSS class as state changes."""
app, _entry, _vault, _search = _build_app()
async with app.run_test() as pilot:
await pilot.pause()
# Default: filter=all, mode=keyword, sort=relevance
assert app.screen.query_one("#grid-filter-all", Button).has_class("active")
assert app.screen.query_one("#grid-mode-keyword", Button).has_class("active")
assert app.screen.query_one("#grid-sort-relevance", Button).has_class("active")
assert not app.screen.query_one("#grid-filter-docs", Button).has_class("active")
# Change filter to docs
app.action_set_kind_filter("docs")
await pilot.pause()
assert app.screen.query_one("#grid-filter-docs", Button).has_class("active")
assert not app.screen.query_one("#grid-filter-all", Button).has_class("active")
# Change sort to label_asc
app.action_set_search_sort("label_asc")
await pilot.pause()
assert app.screen.query_one("#grid-sort-label_asc", Button).has_class("active")
assert not app.screen.query_one("#grid-sort-relevance", Button).has_class("active")