Enhance timeline and movement flow UI; add fetched painting images.

Timeline: era and historical-event click-to-zoom, event markers from historical-events.ts. Movement flow: shared zoom/pan, viewport-fit layout, curved lineage streams, hover-to-reveal artist lifespans on stacked lanes. Docs updated. Includes batch-fetched paintings and regenerated thumbs.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila KhodjaefandCursor committed 2026-06-20 00:21:31 +03:00
1 parent 35c337253d
commit 0ece1195fa
1301 files changed
+1412 -140

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
/** Major world events shown as markers on the timeline (frontend curation). */
export interface HistoricalEvent {
id: string;
name: string;
startYear: number;
/** Omit for a single-year marker. */
endYear?: number;
/** Shorter label when the view is crowded. */
shortLabel?: string;
}
export const HISTORICAL_EVENTS: readonly HistoricalEvent[] = [
{ id: 'fall-rome', name: 'Fall of Rome', startYear: 476 },
{ id: 'black-death', name: 'Black Death', startYear: 1347 },
{ id: 'printing-press', name: 'Printing press', startYear: 1450 },
{ id: 'reformation', name: 'Protestant Reformation', startYear: 1517 },
{ id: 'columbus', name: 'Columbus reaches the Americas', startYear: 1492 },
{ id: 'american-revolution', name: 'American Revolution', startYear: 1776 },
{ id: 'french-revolution', name: 'French Revolution', startYear: 1789 },
{ id: 'waterloo', name: 'Battle of Waterloo', startYear: 1815 },
{ id: 'revolutions-1848', name: 'Revolutions of 1848', startYear: 1848 },
{ id: 'german-unification', name: 'Unification of Germany', startYear: 1871 },
{ id: 'ww1', name: 'World War I', startYear: 1914, endYear: 1918, shortLabel: 'WWI' },
{ id: 'russian-revolution', name: 'Russian Revolution', startYear: 1917 },
{ id: 'ww2', name: 'World War II', startYear: 1939, endYear: 1945, shortLabel: 'WWII' },
{ id: 'berlin-wall', name: 'Fall of Berlin Wall', startYear: 1989 },
];
export function eventEndYear(event: HistoricalEvent): number {
return event.endYear ?? event.startYear;
}
export function eventInView(event: HistoricalEvent, viewStart: number, viewEnd: number): boolean {
return eventEndYear(event) >= viewStart && event.startYear <= viewEnd;
}
+52
View File
@@ -0,0 +1,52 @@
/**
* Documented art-historical movement lineage (predecessor → successor).
* Sources: Met Museum essays, TheArtStory, Sotheby's movement guides.
*
* Multiple parents are allowed (e.g. Post-Impressionism feeding several modern paths).
*/
export const MOVEMENT_LINEAGE: ReadonlyArray<readonly [parent: string, child: string]> = [
// Classical & medieval
['Ancient Greek & Roman', 'Byzantine'],
['Byzantine', 'Gothic'],
// Renaissance chain
['Gothic', 'Early Renaissance'],
['Early Renaissance', 'High Renaissance'],
['Northern Renaissance', 'Early Renaissance'],
['High Renaissance', 'Mannerism'],
['Mannerism', 'Baroque'],
// 17th–19th century
['Baroque', 'Rococo'],
['Rococo', 'Neoclassicism'],
['Neoclassicism', 'Romanticism'],
['Romanticism', 'Realism'],
['Romanticism', 'Impressionism'], // Turner, Delacroix → light and atmosphere
['Realism', 'Impressionism'],
// Modern origins (Met / ArtStory: Impressionism → Post-Impressionism → …)
['Impressionism', 'Post-Impressionism'],
['Post-Impressionism', 'Symbolism'],
['Post-Impressionism', 'Fauvism'],
['Post-Impressionism', 'Expressionism'],
['Post-Impressionism', 'Cubism'], // Cézanne's structure → Picasso/Braque
['Symbolism', 'Art Nouveau'],
['Fauvism', 'Cubism'], // Braque, Matisse → geometric turn
['Cubism', 'Futurism'],
['Cubism', 'Suprematism'],
['Cubism', 'Constructivism'],
['Expressionism', 'Abstract Expressionism'],
// Avant-garde 20th century
['Dada', 'Surrealism'],
['Surrealism', 'Abstract Expressionism'],
['Abstract Expressionism', 'Pop Art'],
];
export function lineageParentsByName(childName: string): string[] {
return MOVEMENT_LINEAGE.filter(([, child]) => child === childName).map(([parent]) => parent);
}
export function lineageChildrenByName(parentName: string): string[] {
return MOVEMENT_LINEAGE.filter(([parent]) => parent === parentName).map(([, child]) => child);
}