feat(web): /tags tabs + keyword surfacing + dup tab restyle
- /tags hosts four tabs (Labels (auto) / Keywords / Ratings / Colors), URL-driven with pagination on the label + keyword grids; ratings and colors stay as fixed buckets. - /duplicates tabs (Stacks / Cross-folder) restyled to pill row in the Toolbar to match /tags; tab state moved into the route and bound to ?tab=... - New aggregateKeywords() service fans out per-photo getPhoto calls so user-typed Details.Keywords surface on /tags (PhotoPrism's /labels only returns classifier output). - RightSidebar renders photo.Labels[] as dashed-border chips after the Keywords section, each linking to /?q=label:slug. - /colors and /ratings routes redirect to /tags?tab=colors|ratings so old bookmarks still land somewhere useful; LeftSidebar drops their entries and the Tags badge now sums labels + ratings + colors. - listFolderCounts dedupes by UID (merged=false returns one row per FILE, so HEIC+JPG / Live Photo / RAW+JPG pairs were inflating folder counts ~2x). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -486,6 +486,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auto-labels (PhotoPrism's TensorFlow classifier output). Read-only:
|
||||
editing labels requires re-indexing on PhotoPrism's side. The
|
||||
dashed border + lower contrast distinguishes them from the user-
|
||||
editable Keywords chips above. -->
|
||||
{#if (photo.Labels ?? []).length > 0}
|
||||
<div class="space-y-1">
|
||||
<div
|
||||
class="flex items-center gap-1 text-[10px] uppercase tracking-wide text-muted-foreground"
|
||||
>
|
||||
<Tag class="h-3 w-3" /> Labels (auto)
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each photo.Labels ?? [] as lbl (lbl.UID ?? lbl.Label?.Slug)}
|
||||
{@const slug = lbl.Label?.Slug}
|
||||
{@const name = lbl.Label?.Name ?? slug ?? '(unknown)'}
|
||||
<a
|
||||
href={slug ? `/?q=${encodeURIComponent(`label:${slug}`)}` : '#'}
|
||||
class="inline-flex items-center rounded-full border border-dashed border-border bg-muted/40 px-1.5 py-0.5 text-[10px] text-muted-foreground hover:text-foreground"
|
||||
title={`Source: ${lbl.Source ?? 'classifier'}`}
|
||||
>
|
||||
{name}
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- GPS detail (collapsed by default) -->
|
||||
<details class="rounded border border-border" open={Boolean(photo.Lat || photo.Lng)}>
|
||||
<summary
|
||||
|
||||
Reference in New Issue
Block a user