Add curator Movement links config with proof URLs and admin-gated delete votes.

Persist lineage in PostgreSQL, seed documented edges, and keep timeline/tree consumers API-backed with a static fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila KhodjaefandCursor committed 2026-09-08 14:37:20 +03:00
1 parent 2b4cdc5a9f
commit 1b760faf0d
36 files changed
+2051 -43

No files matched your search

+37 -10
View File
@@ -1,10 +1,12 @@
/**
* Documented art-historical movement lineage (predecessor → successor).
* Sources: Met Museum essays, TheArtStory, Sotheby's movement guides.
*
* Art-historical movement lineage (predecessor → successor).
* Defaults match museum essays; live data comes from GET /api/movement-links.
* Multiple parents are allowed (e.g. Post-Impressionism feeding several modern paths).
*/
export const MOVEMENT_LINEAGE: ReadonlyArray<readonly [parent: string, child: string]> = [
export type MovementLineageEdge = readonly [parent: string, child: string];
export const FALLBACK_MOVEMENT_LINEAGE: ReadonlyArray<MovementLineageEdge> = [
// Classical & medieval
['Ancient Greek & Roman', 'Byzantine'],
['Byzantine', 'Gothic'],
@@ -21,17 +23,17 @@ export const MOVEMENT_LINEAGE: ReadonlyArray<readonly [parent: string, child: st
['Rococo', 'Neoclassicism'],
['Neoclassicism', 'Romanticism'],
['Romanticism', 'Realism'],
['Romanticism', 'Impressionism'], // Turner, Delacroix → light and atmosphere
['Romanticism', 'Impressionism'],
['Realism', 'Impressionism'],
// Modern origins (Met / ArtStory: Impressionism → Post-Impressionism → …)
// Modern origins
['Impressionism', 'Post-Impressionism'],
['Post-Impressionism', 'Symbolism'],
['Post-Impressionism', 'Fauvism'],
['Post-Impressionism', 'Expressionism'],
['Post-Impressionism', 'Cubism'], // Cézanne's structure → Picasso/Braque
['Post-Impressionism', 'Cubism'],
['Symbolism', 'Art Nouveau'],
['Fauvism', 'Cubism'], // Braque, Matisse → geometric turn
['Fauvism', 'Cubism'],
['Cubism', 'Futurism'],
['Cubism', 'Suprematism'],
['Cubism', 'Constructivism'],
@@ -43,10 +45,35 @@ export const MOVEMENT_LINEAGE: ReadonlyArray<readonly [parent: string, child: st
['Abstract Expressionism', 'Pop Art'],
];
let activeLineage: ReadonlyArray<MovementLineageEdge> = FALLBACK_MOVEMENT_LINEAGE;
/** Active edges for timeline / tree layout (API-backed when loaded). */
export function getMovementLineage(): ReadonlyArray<MovementLineageEdge> {
return activeLineage;
}
/** Apply curator DB rows; empty list keeps the static fallback. */
export function setMovementLineageFromApi(
items: ReadonlyArray<{ source_name: string; target_name: string }>
): void {
if (!items.length) {
activeLineage = FALLBACK_MOVEMENT_LINEAGE;
return;
}
activeLineage = items.map((row) => [row.source_name, row.target_name] as const);
}
/** @deprecated Use getMovementLineage() — alias for older imports. */
export const MOVEMENT_LINEAGE = FALLBACK_MOVEMENT_LINEAGE;
export function lineageParentsByName(childName: string): string[] {
return MOVEMENT_LINEAGE.filter(([, child]) => child === childName).map(([parent]) => parent);
return getMovementLineage()
.filter(([, child]) => child === childName)
.map(([parent]) => parent);
}
export function lineageChildrenByName(parentName: string): string[] {
return MOVEMENT_LINEAGE.filter(([parent]) => parent === parentName).map(([, child]) => child);
return getMovementLineage()
.filter(([parent]) => parent === parentName)
.map(([, child]) => child);
}