web: collapsible Tags section + file size on its own row
Groups score, color label, keywords, and auto-labels under one collapsible "Tags" section on the metadata sidebar (open by default, choice persists). Moves file size onto its own row with a HardDrive icon so dimensions and weight read as independent facts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
ExternalLink,
|
||||
File,
|
||||
Folder,
|
||||
HardDrive,
|
||||
ImageIcon,
|
||||
Loader2,
|
||||
MapPin,
|
||||
@@ -345,11 +346,19 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Dimensions + file size -->
|
||||
<!-- Dimensions -->
|
||||
<div class="flex items-center gap-2">
|
||||
<ImageIcon class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span class="min-w-0 flex-1 truncate px-1 py-0.5 text-muted-foreground">
|
||||
{dims} · {sizeStr}
|
||||
{dims}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- File size -->
|
||||
<div class="flex items-center gap-2">
|
||||
<HardDrive class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
||||
<span class="min-w-0 flex-1 truncate px-1 py-0.5 text-muted-foreground">
|
||||
{sizeStr}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -386,11 +395,25 @@
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Score + color label — two separate sections. Click a star/swatch to
|
||||
set, click the active one to clear. Sits next to Keywords because
|
||||
these are the per-photo culling marks the user reaches for in the
|
||||
same workflow. Stored on the mule-sidecar; PhotoPrism's PUT can't
|
||||
persist them. -->
|
||||
<!-- Tags — score, color label, keywords, and auto-labels grouped under
|
||||
one collapsible section. Score + color are stored on the mule-
|
||||
sidecar (PhotoPrism's PUT can't persist them); keywords live on
|
||||
Details; auto-labels come from PhotoPrism's TF classifier and are
|
||||
read-only. Open by default since these are the culling marks the
|
||||
user reaches for first. -->
|
||||
<details
|
||||
class="rounded border border-border"
|
||||
open={getMetadataSectionOpen('tags', true)}
|
||||
ontoggle={(e) => setMetadataSection('tags', e.currentTarget.open)}
|
||||
>
|
||||
<summary
|
||||
class="cursor-pointer px-2 py-1 text-[10px] uppercase tracking-wide text-muted-foreground"
|
||||
>
|
||||
<span class="inline-flex items-center gap-1">
|
||||
<Tag class="h-3 w-3" /> Tags
|
||||
</span>
|
||||
</summary>
|
||||
<div class="space-y-2 p-2 pt-1">
|
||||
<div class="space-y-1">
|
||||
<div class="text-[10px] uppercase tracking-wide text-muted-foreground">Score</div>
|
||||
<div class="flex items-center gap-0.5" role="group" aria-label="Rating">
|
||||
@@ -429,9 +452,7 @@
|
||||
|
||||
<!-- Keywords as chips -->
|
||||
<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" /> Keywords
|
||||
</div>
|
||||
<div class="text-[10px] uppercase tracking-wide text-muted-foreground">Keywords</div>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each keywords as kw (kw)}
|
||||
<span
|
||||
@@ -463,6 +484,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="text-[10px] uppercase tracking-wide text-muted-foreground">
|
||||
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}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Related strips (only the /review route opts in). The three
|
||||
scopes match the three decisions the user usually makes here:
|
||||
"did all these come from the same shoot?" (folder), "same
|
||||
@@ -492,33 +540,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- 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. Static default (closed); user's expand/collapse
|
||||
choice persists across photo switches via the view store.
|
||||
Avoid data-driven defaults here — they make the `open` attr
|
||||
|
||||
Reference in New Issue
Block a user