CI-green dev batch (HEAD 4fd6d4c, run #494 green). Adds alembic 0036 (HNSW index — builds over existing embeddings on deploy, ~30–60s table lock).
Gallery — visual "more like this" (Phase 3)
GalleryService.similar() ranks images by pgvector cosine distance to a source image's precomputed SigLIP embedding (no query-time ML). Composes with the Phase-1/2 scope filters; bounded top-N; 404 on missing source, empty for un-embedded.
GET /api/gallery/similar, has_embedding on image-detail, alembic 0036 HNSW vector_cosine_ops index.
Modal Related strip (non-blocking: deferred, single-flighted, collapses on empty/slow/error, hidden when no embedding) + "See all similar" gallery takeover with a clearable "Similar to #id" chip.
UI polish
Showcase reveal cadence doubled (80→160ms) for a slower, more deliberate cascade.
Gallery filter bar restyled to the exact TopNav gradiated-obsidian frost so the two read as one continuous piece of chrome.
Mobile responsiveness
TopNav: nav links fold into a hamburger menu <768px; brand text hides <480px (was a colliding 7-link flex row).
CI-green dev batch (HEAD `4fd6d4c`, run #494 green). Adds alembic **0036** (HNSW index — builds over existing embeddings on deploy, ~30–60s table lock).
## Gallery — visual "more like this" (Phase 3)
- `GalleryService.similar()` ranks images by pgvector cosine distance to a source image's precomputed SigLIP embedding (no query-time ML). Composes with the Phase-1/2 scope filters; bounded top-N; 404 on missing source, empty for un-embedded.
- `GET /api/gallery/similar`, `has_embedding` on image-detail, alembic 0036 HNSW `vector_cosine_ops` index.
- Modal **Related strip** (non-blocking: deferred, single-flighted, collapses on empty/slow/error, hidden when no embedding) + "See all similar" gallery takeover with a clearable "Similar to #id" chip.
## UI polish
- Showcase reveal cadence doubled (80→160ms) for a slower, more deliberate cascade.
- Gallery filter bar restyled to the **exact** TopNav gradiated-obsidian frost so the two read as one continuous piece of chrome.
## Mobile responsiveness
- **TopNav**: nav links fold into a hamburger menu <768px; brand text hides <480px (was a colliding 7-link flex row).
- Primary path: BulkEditorPanel `min(320px,90vw)`; GalleryFilterBar search full-width row <600px; GalleryFacetPanel date inputs stack <480px; ArtistsView grid `minmax(min(440px,100%),1fr)`; GalleryView hides the timeline strip <600px.
- Posts & Subscriptions filter bars: wrap + full-width fields on phones.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
GalleryService.similar() ranks images by pgvector cosine distance to a source
image's precomputed SigLIP embedding — no query-time ML inference. Composes
with the Phase-1/2 scope filters (AND) but replaces the date sort (always
nearest-first, bounded top-N, no cursor). Returns None for a missing source
(→404), [] for a source with no embedding (video / pending ML); excludes self
and NULL-embedding rows. New GET /api/gallery/similar?similar_to=<id>&limit=N.
Image-detail payload gains has_embedding so the UI can hide the surface.
Alembic 0036 adds an HNSW vector_cosine_ops index on siglip_embedding (1152<2000
dims) so the search is sub-50ms ANN instead of a full scan; one-time ~30-60s
build over existing embeddings on deploy. Shared _gallery_images/_image_json
helpers de-dup the scroll/similar builders.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Modal 'Related' strip (RelatedStrip.vue) — top-12 similar thumbs, fetched on
its own DEFERRED, single-flighted path (200ms after the modal is up) so it
never blocks or slows the modal; collapses silently on empty/slow/error and is
hidden when the image has no embedding (has_embedding flag). 'See all similar'
closes the modal and navigates the gallery to ?similar_to=<id>.
Gallery store: similar_to filter field + loadSimilar() (ranked, hasMore=false,
no timeline); applyFilterFromQuery routes similar-mode to /similar with the
scope filters composed; cloneFilter/filterToQuery carry similar_to. Filter bar:
clearable 'Similar to #id' chip, sort hidden in similar-mode; timeline sidebar
hidden too.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Showcase reveal cadence 80ms -> 160ms (slower, more deliberate one-at-a-time
cascade per operator). Bump showcase.spec timer advances to cover 60x160ms.
- Gallery filter bar now uses the EXACT gradiated-obsidian frost + blur as
TopNav (was a flat rgba(...,0.55) block), so the two read as one continuous
piece of chrome with images visibly scrolling under both; the nav's
transparent bottom edge against the bar's opaque top leaves a faint seam that
separates them at the very top of scroll.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The top nav packed brand + health + pipeline chip + ~7 inline links + an
action slot into one flex row, colliding/overflowing on phones (operator:
'almost unusable'). Below 768px the links now fold into a hamburger v-menu;
below 480px the brand text hides (glyph still brands). Plus the primary
browsing path:
- BulkEditorPanel: fixed 320px -> min(320px, 90vw) so it can't swallow the screen.
- GalleryFilterBar: <600px gives search its own full-width row (its 200px
min-width was jamming the wrapping bar); sort grows.
- GalleryFacetPanel: <480px wraps groups + lets the side-by-side date inputs
grow full-width.
- ArtistsView grid: minmax(min(440px,100%),1fr) so a card never overflows
(single column on phones).
- GalleryView: hide the year/month timeline strip <600px.
ImageViewer already stacks its side panel below the image <900px (left as-is).
Secondary surfaces (Posts/Subscriptions filter bars, SubscriptionsTab table,
SeriesReader, PostCard) still need a mobile pass — follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PostsFilterBar didn't wrap and its artist/platform fields had inline
min-widths (240/180px) a media query can't override → horizontal overflow on
phones. Moved widths to classes, added flex-wrap, and <600px each field takes
a full-width row. SubscriptionsTab's status/search inline max-widths likewise
moved to classes; <600px they go full-width and the v-spacer is dropped so the
search isn't shoved around.
Verified as already-fine (sweep false positives, no change): PostCard (default
body is a stacked column; only goes row at container >=800px), SeriesReaderView
(already has a <=768px block: nav drawer 150px, quick-nav stacks). The
subscriptions v-data-table scrolls horizontally within its own wrapper, so it
doesn't widen the page — a true mobile card layout is a larger follow-up if
wanted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
CI-green dev batch (HEAD
4fd6d4c, run #494 green). Adds alembic 0036 (HNSW index — builds over existing embeddings on deploy, ~30–60s table lock).Gallery — visual "more like this" (Phase 3)
GalleryService.similar()ranks images by pgvector cosine distance to a source image's precomputed SigLIP embedding (no query-time ML). Composes with the Phase-1/2 scope filters; bounded top-N; 404 on missing source, empty for un-embedded.GET /api/gallery/similar,has_embeddingon image-detail, alembic 0036 HNSWvector_cosine_opsindex.UI polish
Mobile responsiveness
min(320px,90vw); GalleryFilterBar search full-width row <600px; GalleryFacetPanel date inputs stack <480px; ArtistsView gridminmax(min(440px,100%),1fr); GalleryView hides the timeline strip <600px.🤖 Generated with Claude Code