feat(tui-v2): implement true profile tree hierarchy with expand/collapse

Added _profile_tree_expanded state to track node visibility. Implemented
action_profile_tree_toggle and profile-tree-toggle command. Bound 'space'
and 'right' keys to toggle tree nodes. Added [+] / [-] visual indicators
to profile nodes. Updated interactive agent test to verify hierarchy
logic across collapse/expand transitions.
This commit is contained in:
thePR0M3TH3AN committed 2026-03-02 22:29:47 -05:00
1 parent 4eeb936e6d
commit 43f735fe3c
2 files changed
+77 -1

No files matched your search

+25
View File
@@ -158,6 +158,10 @@ async def run_full_walkthrough():
app._run_palette_command('add-password "Gmail" 20')
app._run_palette_command('add-totp "Bank"')
app._run_palette_command('add-document "Readme" md "Hello World"')
# Add managed/agent for hierarchy
service.add_entry("SubUser", 10, kind="managed_account")
service.add_entry("MyAgent", 10, kind="nostr")
await pilot.pause()
print(f" [OK] Created {len(service.entries)} entries.")
@@ -197,6 +201,27 @@ async def run_full_walkthrough():
print(f" [DEBUG] QR Panel Content:\n{qr_text}")
print("\n--- PHASE 3: System Operations ---")
# Tree Hierarchy
print(" Testing tree hierarchy...")
# Initial state: current profile expanded (auto-expand logic)
nodes_before = app._profile_tree_visible_nodes()
has_child = any(n.get("kind") in {"managed", "agent"} for n in nodes_before)
print(f" [OK] Child nodes visible initially: {has_child}")
# Collapse via palette
app._run_palette_command("profile-tree-toggle")
await pilot.pause()
nodes_after = app._profile_tree_visible_nodes()
has_child_after = any(n.get("kind") in {"managed", "agent"} for n in nodes_after)
print(f" [OK] Child nodes hidden after collapse: {not has_child_after}")
# Expand via palette
app._run_palette_command("profile-tree-toggle")
await pilot.pause()
nodes_final = app._profile_tree_visible_nodes()
has_child_final = any(n.get("kind") in {"managed", "agent"} for n in nodes_final)
print(f" [OK] Child nodes restored after expand: {has_child_final}")
# Managed Account
service.add_entry("Sub", 10, kind="managed_account")
app._load_entries("")
+52 -1
View File
@@ -345,6 +345,8 @@ def launch_tui2(
("down", "profile_tree_next", "Profile Next"),
("ctrl+o", "profile_tree_open", "Profile Open"),
("u", "profile_tree_preview", "Profile Preview"),
("space", "profile_tree_toggle", "Toggle Node"),
("right", "profile_tree_toggle", "Expand"),
("p", "prev_page", "Prev Page"),
("n", "next_page", "Next Page"),
("l", "cycle_link_filter", "Link Filter"),
@@ -505,6 +507,7 @@ def launch_tui2(
self._density_mode = "compact"
self._profile_tree_items: list[str] = []
self._profile_tree_cursor = 0
self._profile_tree_expanded: dict[str, bool] = {}
self._compact_layout = False
self._dense_hires_layout = False
self._viewport_width = 0
@@ -789,11 +792,21 @@ def launch_tui2(
profiles = [current_fp or "(default)"]
for fp in profiles:
nodes.append({"kind": "profile", "fingerprint": fp})
# Auto-expand the active profile if not explicitly tracked
if fp not in self._profile_tree_expanded:
self._profile_tree_expanded[fp] = (fp == current_fp)
if not self._profile_tree_expanded.get(fp, False):
continue
# Only show child nodes for the ACTIVE profile branch for now
# (since searching across all profiles is expensive)
is_active_branch = fp == current_fp or (
not current_fp and fp == "(default)"
)
if not is_active_branch:
continue
for entry_id, label in managed_nodes[:3]:
nodes.append(
{
@@ -1487,6 +1500,11 @@ def launch_tui2(
label = item if item != "(default)" else "default"
fp_short = item if len(item) <= 12 else f"{item[:9]}..."
active_branch = item == current_fp
# Add expand/collapse indicator
expanded = self._profile_tree_expanded.get(item, False)
exp_ind = "[-] " if expanded else "[+] "
branch_suffix = ""
if active_branch:
managed_count = len(
@@ -1496,8 +1514,9 @@ def launch_tui2(
[n for n in nodes if str(n.get("kind")) == "agent"]
)
branch_suffix = f" M:{managed_count} A:{agent_count}"
tree_lines.append(
f"{cursor} {marker} {label[:16]:<16} | Seed: {fp_short}{branch_suffix}"
f"{cursor} {marker} {exp_ind}{label[:12]:<12} | Seed: {fp_short}{branch_suffix}"
)
elif kind == "managed":
if not rendered_managed_header:
@@ -4463,6 +4482,13 @@ def launch_tui2(
self.action_profile_tree_open()
return
if cmd == "profile-tree-toggle":
if args:
self._set_status("Usage: profile-tree-toggle")
return
self.action_profile_tree_toggle()
return
if cmd == "setting-secret":
if self._config_service is None:
self._set_status("Config service unavailable")
@@ -5751,6 +5777,31 @@ def launch_tui2(
self._update_filters_panel()
self._set_status(self._profile_tree_selection_text(selected))
def action_profile_tree_toggle(self) -> None:
if self._sidebar_collapsed:
self._set_status("Sidebar is collapsed. Press Ctrl+B to expand.")
return
if self._focus_pane != "left":
self._set_status("Focus left pane first (press 1)")
return
self._refresh_profile_tree()
nodes = self._profile_tree_visible_nodes()
if not nodes:
return
self._profile_tree_cursor = min(
max(0, self._profile_tree_cursor), len(nodes) - 1
)
selected = nodes[self._profile_tree_cursor]
if selected.get("kind") == "profile":
fp = str(selected.get("fingerprint", ""))
is_expanded = self._profile_tree_expanded.get(fp, False)
self._profile_tree_expanded[fp] = not is_expanded
state = "Expanded" if not is_expanded else "Collapsed"
self._update_filters_panel()
self._set_status(f"{state} profile branch: {fp}")
else:
self._set_status("Only profile nodes can be expanded/collapsed")
def action_focus_center(self) -> None:
self._focus_pane = "center"
self._apply_focus_style()