feat: highlight matched metadata on thumbnails during search

Renders a thin top-of-cell banner on each photo while a text search
is active, labelling which metadata field matched (filename, title,
note, tag, or EXIF key name) and showing a short excerpt with the
exact matched substring highlighted in amber. Extends the list
endpoint's search to also match photos whose tag names contain the
query so tags show up alongside filename/title/notes/EXIF hits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-11 21:38:27 +02:00
parent 64e4ea8083
commit 38d975e354
3 changed files with 236 additions and 2 deletions

View File

@@ -58,15 +58,26 @@ async def list_photos(
# Apply filters
filters = []
# Text search (would use FTS5 in production)
# Text search. Walks every metadata field a user might reasonably
# remember a photo by: basename, title, notes, raw EXIF blob, and
# tag names (via a subquery so photos with any matching tag come
# back even when the tag filter isn't set). Case-insensitive ILIKE
# across all fields — the frontend re-runs the same match logic to
# render a "matched on …" chip on each thumbnail.
if q:
search_pattern = f"%{q}%"
tag_subq = (
select(photo_tags.c.photo_id)
.select_from(photo_tags.join(Tag, photo_tags.c.tag_id == Tag.id))
.where(Tag.name.ilike(search_pattern))
)
filters.append(
or_(
Photo.filename.ilike(search_pattern),
Photo.user_title.ilike(search_pattern),
Photo.user_notes.ilike(search_pattern),
Photo.exif_json.ilike(search_pattern)
Photo.exif_json.ilike(search_pattern),
Photo.id.in_(tag_subq),
)
)