web: 8-color label palette + VIDEO badge in carousel
Expand color labels from the 4-swatch Lightroom culling palette to 8
neutral colors (red/orange/yellow/green/teal/blue/purple/pink) with no
attached semantics, rendered as outlines that fill in when picked.
Carousel tiles now show a VIDEO badge, and the folder row in the right
sidebar always renders ("/" for root) instead of disappearing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
setFocused,
|
||||
toggle
|
||||
} from '$lib/stores/selection.svelte';
|
||||
import { primaryFile, type PpPhoto } from '$lib/types/photoprism';
|
||||
import { isVideo, primaryFile, type PpPhoto } from '$lib/types/photoprism';
|
||||
|
||||
const qc = useQueryClient();
|
||||
|
||||
@@ -30,22 +30,22 @@
|
||||
* any time, plenty to cover normal arrow-skim without bloating. */
|
||||
const WINDOW = 50;
|
||||
|
||||
function lookup(uid: string): string | null {
|
||||
function lookup(uid: string): PpPhoto | null {
|
||||
const direct = qc.getQueryData<PpPhoto>(['photo', uid]);
|
||||
if (direct) return primaryFile(direct).Hash ?? null;
|
||||
if (direct) return direct;
|
||||
const lists = qc.getQueriesData({ queryKey: ['photos'] });
|
||||
for (const [, data] of lists) {
|
||||
if (!data) continue;
|
||||
if (Array.isArray(data)) {
|
||||
const hit = (data as PpPhoto[]).find((p) => p.UID === uid);
|
||||
if (hit) return primaryFile(hit).Hash ?? null;
|
||||
if (hit) return hit;
|
||||
continue;
|
||||
}
|
||||
const pages = (data as { pages?: PpPhoto[][] }).pages;
|
||||
if (!Array.isArray(pages)) continue;
|
||||
for (const page of pages) {
|
||||
const hit = page?.find?.((p) => p.UID === uid);
|
||||
if (hit) return primaryFile(hit).Hash ?? null;
|
||||
if (hit) return hit;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -58,6 +58,7 @@
|
||||
interface Tile {
|
||||
uid: string;
|
||||
hash: string | null;
|
||||
video: boolean;
|
||||
idx: number;
|
||||
}
|
||||
const slice = $derived.by<Tile[]>(() => {
|
||||
@@ -66,7 +67,13 @@
|
||||
const hi = Math.min(order.length, focusedIdx + WINDOW + 1);
|
||||
const out: Tile[] = [];
|
||||
for (let i = lo; i < hi; i++) {
|
||||
out.push({ uid: order[i], hash: lookup(order[i]), idx: i });
|
||||
const photo = lookup(order[i]);
|
||||
out.push({
|
||||
uid: order[i],
|
||||
hash: photo ? (primaryFile(photo).Hash ?? null) : null,
|
||||
video: photo ? isVideo(photo) : false,
|
||||
idx: i
|
||||
});
|
||||
}
|
||||
return out;
|
||||
});
|
||||
@@ -154,6 +161,12 @@
|
||||
{#if isSelected}
|
||||
<div class="pointer-events-none absolute inset-0 bg-blue-500/40"></div>
|
||||
{/if}
|
||||
{#if tile.video}
|
||||
<span
|
||||
class="absolute left-1.5 top-1.5 rounded bg-background/80 px-1 text-[10px] font-medium text-foreground"
|
||||
>VIDEO</span
|
||||
>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user