Add movement gallery wings with period interiors and expand timeline features.
Movement galleries split large catalogs into chronological wings (~55 works), use era-themed 3D interiors with side-wall windows, wing navigator on the back exit, and front archways between wings. Also adds painting annotations, timeline event guides, portrait hover highlights, and documentation/API updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
0972b5df99
commit
df29848d89
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState, type ChangeEvent, type SyntheticEvent } fr
|
||||
import type { InfluenceLink, Painting, PaintingDetail } from '../types';
|
||||
import { api, debugImageProxyUrl, imageUrl, paintingImageUrl, type DebugImageSearchResult, type DebugImageSearchResultItem, type FixPaintingImageResult } from '../api/client';
|
||||
import DebugSearchResultsModal from './DebugSearchResultsModal';
|
||||
import PaintingAnnotationsPanel, { PaintingAnnotationMarkers } from './PaintingAnnotations';
|
||||
import PaintingLightbox from './PaintingLightbox';
|
||||
import './PaintingDetail.css';
|
||||
|
||||
@@ -190,7 +191,7 @@ export default function PaintingDetailView({
|
||||
onPaintingImageFixed,
|
||||
onPaintingCheckupFlagsUpdated,
|
||||
}: Props) {
|
||||
const { painting, influencedBy, influenced } = data;
|
||||
const { painting, influencedBy, influenced, annotations = [] } = data;
|
||||
const [fullscreen, setFullscreen] = useState(false);
|
||||
const [imageVersion, setImageVersion] = useState(0);
|
||||
const [debugSearch, setDebugSearch] = useState<DebugImageSearchResult | null>(null);
|
||||
@@ -205,6 +206,7 @@ export default function PaintingDetailView({
|
||||
const [applyingUrl, setApplyingUrl] = useState<string | null>(null);
|
||||
const [clearing, setClearing] = useState(false);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [activeAnnotationId, setActiveAnnotationId] = useState<number | null>(null);
|
||||
const uploadInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const baseImageUrl = paintingImageUrl(painting);
|
||||
@@ -227,6 +229,7 @@ export default function PaintingDetailView({
|
||||
setMoreOpen(false);
|
||||
setMoreResults(null);
|
||||
setMoreError(null);
|
||||
setActiveAnnotationId(null);
|
||||
}, [painting.id]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -475,11 +478,28 @@ export default function PaintingDetailView({
|
||||
title={imageSrc ? 'View full screen' : undefined}
|
||||
aria-label={imageSrc ? `View ${painting.title} full screen` : `${painting.title} (no image)`}
|
||||
>
|
||||
{imageSrc ? (
|
||||
<img src={imageSrc} alt={painting.title} onError={handleImageError} draggable={false} />
|
||||
) : null}
|
||||
<div className="painting-frame-image-wrap">
|
||||
{imageSrc ? (
|
||||
<img src={imageSrc} alt={painting.title} onError={handleImageError} draggable={false} />
|
||||
) : null}
|
||||
{imageSrc && annotations.length > 0 && (
|
||||
<PaintingAnnotationMarkers
|
||||
annotations={annotations}
|
||||
activeId={activeAnnotationId}
|
||||
onSelect={setActiveAnnotationId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{annotations.length > 0 && (
|
||||
<PaintingAnnotationsPanel
|
||||
annotations={annotations}
|
||||
activeId={activeAnnotationId}
|
||||
onSelect={setActiveAnnotationId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showCatalogNav && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user