From 4fb1c8f43d37ddb7bf05838377ff2b6a54a846eb Mon Sep 17 00:00:00 2001 From: retoor Date: Sat, 13 Jun 2026 15:24:48 +0200 Subject: [PATCH] Update --- AGENTS.md | 9 +++ CLAUDE.md | 3 +- README.md | 5 +- devplacepy/database.py | 10 +++ devplacepy/docs_api.py | 16 ++++ devplacepy/routers/feed.py | 24 +++++- devplacepy/routers/gists.py | 19 +++-- devplacepy/routers/projects.py | 9 +-- devplacepy/schemas.py | 2 + devplacepy/services/devii/actions/catalog.py | 2 + devplacepy/templates/feed.html | 2 + devplacepy/templates/gists.html | 2 + devplacepy/templates/projects.html | 7 +- tests/test_feed.py | 78 ++++++++++++++++++++ tests/test_gists.py | 75 +++++++++++++++++++ 15 files changed, 240 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 792d7b46..d1ca7094 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1483,6 +1483,15 @@ Both the home page Latest Posts section and the main `/feed` cap how many posts When building any new "recent items" list, reuse these instead of raw `find(order_by=...)` / `paginate`. +## Listing search (feed, gists, projects) + +The three public listings - `/feed`, `/gists`, `/projects` - share one free-text search box at the top of the left filter panel. Two reuse points keep it DRY and consistent: + +- **Data layer:** `database.text_search_clause(table, search, fields=("title", "description"))` returns a single SQLAlchemy `or_(col.ilike("%term%") ...)` clause over the given columns (skipping any not present on the table), or `None` when `search` is blank or the table does not exist yet. Append it as a positional clause to `paginate` / `paginate_diverse` / `table.count(...)` **only when not None** (no search leaves the queries unchanged). Field mapping: projects/gists use `("title", "description")`, posts use `("title", "content")`. `projects.py`, `gists.py`, and `feed.py` all call it; never re-inline an `ilike` search clause. +- **View layer:** `templates/_sidebar_search.html` is the single search-box partial (the `Filter` heading + the GET form). Include it at the very top of `