From a5397f37f8dc47ec5790f41e8a3c0681ae9b7d27 Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Fri, 3 Apr 2026 08:51:19 -0700 Subject: [PATCH] Limit group view to direct entries --- appstate/state.go | 2 +- appstate/state_test.go | 11 ++++++----- main.go | 33 +++++++++------------------------ main_test.go | 18 +++++++++++------- 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/appstate/state.go b/appstate/state.go index 4e1e1b2..a02c693 100644 --- a/appstate/state.go +++ b/appstate/state.go @@ -302,7 +302,7 @@ func (s *State) VisibleEntries() ([]vault.Entry, error) { } if s.Section == SectionEntries { - return model.EntriesUnderPath(s.CurrentPath), nil + return entriesInPath(model.Entries, s.CurrentPath), nil } if s.Section == SectionRecycleBin || len(s.CurrentPath) == 0 { return entries, nil diff --git a/appstate/state_test.go b/appstate/state_test.go index 7e6a044..8bd5cc2 100644 --- a/appstate/state_test.go +++ b/appstate/state_test.go @@ -44,13 +44,14 @@ func TestVisibleEntriesFollowsCurrentPathWithoutSearch(t *testing.T) { } } -func TestVisibleEntriesIncludesDescendantEntriesAtParentGroup(t *testing.T) { +func TestVisibleEntriesAtParentGroupOnlyShowsDirectEntries(t *testing.T) { t.Parallel() state := State{ Session: stubSession{ model: vault.Model{ Entries: []vault.Entry{ + {ID: "joe-note", Title: "Crew Note", Path: []string{"Crew"}}, {ID: "bellagio", Title: "Bellagio", Path: []string{"Crew", "Internet"}}, {ID: "vault-console", Title: "Vault Console", Path: []string{"Crew", "Internet"}}, {ID: "surveillance-console", Title: "Surveillance Console", Path: []string{"Crew", "Home Assistant"}}, @@ -69,8 +70,8 @@ func TestVisibleEntriesIncludesDescendantEntriesAtParentGroup(t *testing.T) { for _, entry := range got { titles = append(titles, entry.Title) } - if !slices.Equal(titles, []string{"Bellagio", "Vault Console", "Surveillance Console"}) { - t.Fatalf("visible titles = %v, want descendant entries from Crew", titles) + if !slices.Equal(titles, []string{"Crew Note"}) { + t.Fatalf("visible titles = %v, want only direct entries from Crew", titles) } } @@ -220,8 +221,8 @@ func TestVisibleEntriesReturnsDescendantsAfterClearingSearch(t *testing.T) { if err != nil { t.Fatalf("VisibleEntries() after clearing search error = %v", err) } - if len(got) != 3 { - t.Fatalf("len(VisibleEntries()) after clearing search = %d, want 3 descendant entries", len(got)) + if len(got) != 0 { + t.Fatalf("len(VisibleEntries()) after clearing search = %d, want 0 direct entries at Crew", len(got)) } } diff --git a/main.go b/main.go index 2c28316..e3aaf67 100644 --- a/main.go +++ b/main.go @@ -650,10 +650,12 @@ func (u *ui) filter() { } } -func (u *ui) visibleEntrySnapshot() ([]entry, []widget.Clickable) { +func (u *ui) visibleEntrySnapshot() ([]entry, []*widget.Clickable) { visible := append([]entry(nil), u.visible...) - clicks := make([]widget.Clickable, len(visible)) - copy(clicks, u.entryClicks) + clicks := make([]*widget.Clickable, len(visible)) + for i := range visible { + clicks[i] = &u.entryClicks[i] + } return visible, clicks } @@ -4225,7 +4227,7 @@ func (u *ui) listPanel(gtx layout.Context) layout.Dimensions { for i := range visibleEntries { idx := i rows = append(rows, func(gtx layout.Context) layout.Dimensions { - return u.entryRow(gtx, &entryClicks[idx], idx, visibleEntries[idx]) + return u.entryRow(gtx, entryClicks[idx], idx, visibleEntries[idx]) }) } } @@ -5386,24 +5388,14 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions { displayPath := u.displayPath() atRoot := len(displayPath) == 0 return compactCard(gtx, func(gtx layout.Context) layout.Dimensions { - if u.mode == "phone" { + if u.mode == "phone" { if atRoot { u.phoneGroupBrowserExpanded = true } return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(u.theme, unit.Sp(12), "GROUPS") - lbl.Color = mutedColor - return lbl.Layout(gtx) - }), - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return layout.Spacer{Height: unit.Dp(8)}.Layout(gtx) - }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { if len(groups) == 0 { - lbl := material.Label(u.theme, unit.Sp(12), "No subgroups here.") - lbl.Color = mutedColor - return lbl.Layout(gtx) + return layout.Dimensions{} } maxY := gtx.Dp(unit.Dp(168)) if gtx.Constraints.Max.Y > maxY { @@ -5428,11 +5420,6 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions { ) } return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(u.theme, unit.Sp(12), "GROUPS") - lbl.Color = mutedColor - return lbl.Layout(gtx) - }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { if atRoot { return layout.Dimensions{} @@ -5468,9 +5455,7 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions { layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { if len(groups) == 0 { - lbl := material.Label(u.theme, unit.Sp(12), "No groups here.") - lbl.Color = mutedColor - return lbl.Layout(gtx) + return layout.Dimensions{} } maxGroupListHeight := 200 if u.mode == "phone" { diff --git a/main_test.go b/main_test.go index 6d2b025..fa920d1 100644 --- a/main_test.go +++ b/main_test.go @@ -844,6 +844,9 @@ func TestUIVisibleEntrySnapshotIsStableAfterVisibleMutation(t *testing.T) { if got := visible[1].Title; got != "Beta" { t.Fatalf("visible snapshot second title = %q, want Beta", got) } + if clicks[1] == nil { + t.Fatal("snapshot click pointer = nil, want stable clickable pointer") + } } func TestUIPhoneBackReturnsFromSubscreenToEntries(t *testing.T) { @@ -1561,8 +1564,8 @@ func TestUIStartOpenRemoteActionAppliesResultOnMainThread(t *testing.T) { if !manager.HasVault() { t.Fatal("manager.HasVault() = false after remote result applied, want true") } - if got := u.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) { - t.Fatalf("filteredTitles() = %v, want [Vault Console]", got) + if got := u.filteredTitles(); len(got) != 0 { + t.Fatalf("filteredTitles() = %v, want empty at root when entries only appear in child groups", got) } } @@ -1757,8 +1760,8 @@ func TestUIStartOpenVaultActionAppliesResultOnMainThread(t *testing.T) { if !manager.HasVault() { t.Fatal("manager.HasVault() = false after background result applied, want true") } - if got := u.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) { - t.Fatalf("filteredTitles() = %v, want [Vault Console]", got) + if got := u.filteredTitles(); len(got) != 0 { + t.Fatalf("filteredTitles() = %v, want empty at root when entries only appear in child groups", got) } } @@ -2043,11 +2046,12 @@ func TestUIGroupNavigationLabelsDistinguishRootCurrentAndParent(t *testing.T) { } } -func TestUIParentGroupShowsDescendantEntries(t *testing.T) { +func TestUIParentGroupDoesNotShowDescendantEntries(t *testing.T) { t.Parallel() u := newUIWithModel("desktop", vault.Model{ Entries: []vault.Entry{ + {ID: "joe-note", Title: "Crew Note", Path: []string{"Crew"}}, {ID: "bellagio", Title: "Bellagio", Path: []string{"Crew", "Internet"}}, {ID: "vault-console", Title: "Vault Console", Path: []string{"Crew", "Internet"}}, {ID: "surveillance-console", Title: "Surveillance Console", Path: []string{"Crew", "Home Assistant"}}, @@ -2057,8 +2061,8 @@ func TestUIParentGroupShowsDescendantEntries(t *testing.T) { u.state.NavigateToPath([]string{"Crew"}) u.filter() - if got := u.filteredTitles(); !slices.Equal(got, []string{"Bellagio", "Vault Console", "Surveillance Console"}) { - t.Fatalf("filteredTitles() = %v, want descendant entries under Crew", got) + if got := u.filteredTitles(); !slices.Equal(got, []string{"Crew Note"}) { + t.Fatalf("filteredTitles() = %v, want only direct entries under Crew", got) } }