From 6e6672f22545106f2c61740b0bd0c9329ead94db Mon Sep 17 00:00:00 2001 From: dtoro Date: Tue, 7 Apr 2026 22:15:19 +0200 Subject: [PATCH] fix: use square cells in timeline grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PhotoThumbnail computed cell height as size * min(aspectRatio, 1.5), so portrait photos overflowed their row. The TanStack Virtual row estimate is a single fixed value (thumbnailSize + gap), so any cell taller than that pushed into the row below — visible as overlapping thumbnails whenever a portrait shared a row with landscapes. Switching to square cells (Lightroom Library default) means every row is exactly the estimated height. The image still fills via object-cover, just cropped on the long axis. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/components/timeline/PhotoThumbnail.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/timeline/PhotoThumbnail.tsx b/frontend/src/components/timeline/PhotoThumbnail.tsx index 908c7f4..21ac608 100644 --- a/frontend/src/components/timeline/PhotoThumbnail.tsx +++ b/frontend/src/components/timeline/PhotoThumbnail.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useCallback, useRef } from 'react' -import { Star, Check, X, RefreshCw } from 'lucide-react' +import { Star, Check, Trash2, RefreshCw } from 'lucide-react' import clsx from 'clsx' import { photos as photosApi } from '../../services/api' import type { Photo } from '../../types/photo' @@ -28,9 +28,11 @@ export function PhotoThumbnail({ photo, size, isSelected, onClick, onDoubleClick const baseUrl = photosApi.getThumbnailUrl(photo.id, 'medium') const thumbnailUrl = retryCount > 0 ? `${baseUrl}?retry=${retryCount}` : baseUrl - // Calculate aspect ratio for proper sizing (default to 1:1 if dimensions unknown) - const aspectRatio = (photo.height && photo.width) ? photo.height / photo.width : 1 - const displayHeight = size * Math.min(aspectRatio, 1.5) // Cap height at 1.5x width + // Square cells (Lightroom-style grid). Variable-aspect cells previously + // overflowed their row because TanStack Virtual estimates row height as a + // single fixed value — portraits in a landscape row would overlap the row + // below. With object-cover the image still fills the cell, just cropped. + const displayHeight = size const clearRetryTimer = () => { if (retryTimerRef.current !== null) { @@ -169,8 +171,8 @@ export function PhotoThumbnail({ photo, size, isSelected, onClick, onDoubleClick {photo.is_picked && ( )} - {photo.is_rejected && ( - + {photo.is_trashed && ( + )}