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>
80 lines
2.5 KiB
TypeScript
80 lines
2.5 KiB
TypeScript
/**
|
||
* 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 type MovementLineageEdge = readonly [parent: string, child: string];
|
||
|
||
export const FALLBACK_MOVEMENT_LINEAGE: ReadonlyArray<MovementLineageEdge> = [
|
||
// 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'],
|
||
['Realism', 'Impressionism'],
|
||
|
||
// Modern origins
|
||
['Impressionism', 'Post-Impressionism'],
|
||
['Post-Impressionism', 'Symbolism'],
|
||
['Post-Impressionism', 'Fauvism'],
|
||
['Post-Impressionism', 'Expressionism'],
|
||
['Post-Impressionism', 'Cubism'],
|
||
['Symbolism', 'Art Nouveau'],
|
||
['Fauvism', 'Cubism'],
|
||
['Cubism', 'Futurism'],
|
||
['Cubism', 'Suprematism'],
|
||
['Cubism', 'Constructivism'],
|
||
['Expressionism', 'Abstract Expressionism'],
|
||
|
||
// Avant-garde 20th century
|
||
['Dada', 'Surrealism'],
|
||
['Surrealism', 'Abstract Expressionism'],
|
||
['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 getMovementLineage()
|
||
.filter(([, child]) => child === childName)
|
||
.map(([parent]) => parent);
|
||
}
|
||
|
||
export function lineageChildrenByName(parentName: string): string[] {
|
||
return getMovementLineage()
|
||
.filter(([parent]) => parent === parentName)
|
||
.map(([, child]) => child);
|
||
}
|