From 9c006a9a2f983eee6801c7b1ddc4e2c181752cea Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Tue, 20 Oct 2026 16:01:10 -0400 Subject: [PATCH] fix(filters): browse with no rules works, popup under the input, no sticky headers (user report) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Blank typed queries rode the STATIC autocomplete path for the classic six fields, where a single space similarity-matches into nothing — clicking an empty value box with no other filters said 'No matching values' in a library full of values. Blank = browse now always rides the composed engine (which trims and answers the top values); static keeps its pinned contract for real typed queries. Regression-pinned: browse with no rules lists the field's values. - The value popup now drops BELOW the input (top: 100%), not over it. - Field-menu group headers lost position:sticky — the search box alone stays pinned. --- cmd/server/tests/filter_builder_test.go | 10 ++++++++++ internal/handlers/media.go | 11 ++++++++--- web/static/input.css | 5 ++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cmd/server/tests/filter_builder_test.go b/cmd/server/tests/filter_builder_test.go index f08943a1..24a641de 100644 --- a/cmd/server/tests/filter_builder_test.go +++ b/cmd/server/tests/filter_builder_test.go @@ -526,6 +526,16 @@ func TestFilterSuggestionsUnderRules(t *testing.T) { assert.NotContains(t, valuesOf(results), "spacewestern", "zero-match values never offered") assert.NotContains(t, valuesOf(results), "history", "values outside the rules never offered") + // Browse with NO other rules in place: the blank query still lists + // the field's values (the static similarity path would answer a + // single space with nothing). Regression for the empty-browse bug. + status, results = suggest("genre", " ", "") + require.Equal(t, http.StatusOK, status) + assert.Contains(t, valuesOf(results), "fantasy", "browse with no rules lists the field's values") + status, results = suggest("author", " ", "") + require.Equal(t, http.StatusOK, status) + assert.Contains(t, valuesOf(results), "Amara Voss", "author browse with no rules") + // Library field menu: visibility-scoped, id values + name display // (this test's fixture libraries are named "Test Builder Sug …"). status, results = suggest("library", "Test Builder", "") diff --git a/internal/handlers/media.go b/internal/handlers/media.go index 42b78347..6a1631d7 100644 --- a/internal/handlers/media.go +++ b/internal/handlers/media.go @@ -2648,9 +2648,14 @@ func (mh *MediaHandler) handleFieldValuesSearch(c *echo.Context, userID pgtype.U tree = t } - // Static path: the classic six with no rule tree (unchanged - // contract — the tag-dropdown island and deep links still ride it). - staticSix := tree == nil + // Static path: the classic six with a REAL typed query and no rule + // tree (unchanged contract — the tag-dropdown island and deep links + // still ride it). A BLANK typed query is BROWSE (the builder's + // click-the-box top-values list): the static queries would + // similarity-match a single space into nothing, so browse rides the + // composed engine, which trims the query and answers the top values + // under the rules. + staticSix := tree == nil && strings.TrimSpace(searchQuery) != "" if staticSix { switch fieldType { case "author", "genre", "tags", "narrator", "series", "language": diff --git a/web/static/input.css b/web/static/input.css index 8180b732..71c44f46 100644 --- a/web/static/input.css +++ b/web/static/input.css @@ -1962,6 +1962,8 @@ .fb-menu-value { width: 16rem; left: 0; + top: 100%; + margin-top: 0.25rem; } .fb-menu-group { font-size: 0.65rem; @@ -1974,9 +1976,6 @@ background-color: color-mix(in srgb, var(--text-primary) 5%, transparent); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); - position: sticky; - top: 0; - z-index: 1; } .fb-menu-filter-wrap { padding: 0.4rem 0.5rem;