Add influence rework, image checkup, debug mode, and fetched paintings.
Support artist and movement influence links with web discovery, a developer checkup table with gallery/detail thumbnails, and debug image search with fix-it workflow. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
0ece1195fa
commit
bf7db9b25e
+19
-2
@@ -164,8 +164,25 @@ Painting detail with influence graph neighbours.
|
||||
```json
|
||||
{
|
||||
"painting": { "id": 10, "title": "...", "artist_name": "...", "image_path": "...", "has_influence_links": true, ... },
|
||||
"influencedBy": [ { "id": 3, "title": "...", "artist_name": "...", "notes": "...", "aspects": "...", "quote": "...", "source_author": "...", ... } ],
|
||||
"influenced": [ { "id": 20, "title": "...", ... } ]
|
||||
"influencedBy": [
|
||||
{
|
||||
"source_type": "painting",
|
||||
"id": 3, "title": "...", "artist_name": "...", "year": 1885,
|
||||
"notes": "...", "aspects": "...", "quote": "...", "source_author": "...", "source_url": "..."
|
||||
},
|
||||
{
|
||||
"source_type": "artist",
|
||||
"source_artist_id": 12, "source_artist_name": "Paul Cézanne",
|
||||
"artist_portrait": "...", "period_note": "during creation (1907)",
|
||||
"period_start_year": 1907, "period_end_year": 1907
|
||||
},
|
||||
{
|
||||
"source_type": "movement",
|
||||
"movement_id": 8, "movement_name": "Fauvism", "movement_color": "#c45c26",
|
||||
"period_start_year": 1905, "period_end_year": 1907
|
||||
}
|
||||
],
|
||||
"influenced": [ { "source_type": "painting", "id": 20, "title": "...", ... } ]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ erDiagram
|
||||
artist_periods ||--o{ paintings : groups
|
||||
paintings ||--o{ painting_influences : influenced_by
|
||||
paintings ||--o{ painting_influences : influences
|
||||
paintings ||--o{ painting_influence_sources : influenced_by
|
||||
artists ||--o{ painting_influence_sources : artist_source
|
||||
art_movements ||--o{ painting_influence_sources : movement_source
|
||||
```
|
||||
|
||||
## Tables
|
||||
@@ -114,7 +117,35 @@ Directed edges: *this painting* was influenced by *that painting*.
|
||||
|
||||
Unique constraint on `(painting_id, influenced_by_painting_id)`.
|
||||
|
||||
Used by the 3D gallery exit panel: predecessors are artists of `influenced_by_painting_id` works; successors are artists of paintings influenced by this artist’s works (see [API.md](API.md#get-apiartistsidnavigation)).
|
||||
Used by the 3D gallery exit panel: predecessors are artists of `influenced_by_painting_id` works; successors are artists of paintings influenced by this artist’s works (see [API.md](API.md#get-apiartistsidnavigation)). Legacy table; new polymorphic links live in `painting_influence_sources`.
|
||||
|
||||
### `painting_influence_sources`
|
||||
|
||||
Polymorphic influence links: *this painting* was influenced by a **painting**, **artist**, or **movement**.
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | SERIAL PK | |
|
||||
| `painting_id` | FK → `paintings` | The work being explained |
|
||||
| `source_type` | VARCHAR(20) | `painting`, `artist`, or `movement` |
|
||||
| `source_painting_id` | FK → `paintings` | When `source_type = painting` |
|
||||
| `source_artist_id` | FK → `artists` | When `source_type = artist` |
|
||||
| `source_movement_id` | FK → `art_movements` | When `source_type = movement` |
|
||||
| `period_note` | VARCHAR(240) | Human-readable period label |
|
||||
| `period_start_year` | INTEGER | Optional span start |
|
||||
| `period_end_year` | INTEGER | Optional span end |
|
||||
| `notes` | TEXT | Curator summary |
|
||||
| `source` | VARCHAR(500) | General attribution |
|
||||
| `aspects` | TEXT | What was borrowed |
|
||||
| `quote` | TEXT | Short citation |
|
||||
| `source_author` | VARCHAR(200) | e.g. Gombrich, Janson |
|
||||
| `source_url` | VARCHAR(500) | Reference link |
|
||||
| `discovered_via` | VARCHAR(120) | e.g. `wikipedia`, `wikidata`, `web:metmuseum.org` |
|
||||
| `confidence` | VARCHAR(20) | `curated` (default) or `discovered` |
|
||||
|
||||
Unique index on `(painting_id, source_type, source_painting_id, source_artist_id, source_movement_id)` with COALESCE for null FKs.
|
||||
|
||||
Used by painting detail API (`influencedBy`). Painting-type rows also feed `has_influence_links` and hall navigation (with legacy `painting_influences`).
|
||||
|
||||
## Indexes
|
||||
|
||||
@@ -122,6 +153,7 @@ Used by the 3D gallery exit panel: predecessors are artists of `influenced_by_pa
|
||||
- `paintings(artist_id)`, `paintings(period_id)`
|
||||
- `art_movements(era_id)`, `art_movements(start_year, end_year)`
|
||||
- `painting_influences(painting_id)`, `painting_influences(influenced_by_painting_id)`
|
||||
- `painting_influence_sources(painting_id)`, `painting_influence_sources(source_artist_id)`, `painting_influence_sources(source_movement_id)`
|
||||
|
||||
## First-time setup
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ The app is organised as a **drill-down hierarchy**:
|
||||
1. **Timeline** — historical eras (Ancient → Contemporary) with definite or fuzzy date boundaries.
|
||||
2. **Movement flow** — art movements as curved SVG streams on the same year axis; documented predecessor→successor branches; portrait thumbnails placed along each stream.
|
||||
3. **3D gallery** — one personal hall per artist; paintings on the walls, open centre, single exit for influence-based navigation.
|
||||
4. **Painting detail** — full work in the centre, *Influenced By* on the left, *Influenced* on the right, prev/next catalog browsing, optional fullscreen, link to artist biography.
|
||||
4. **Painting detail** — full work in the centre, *Influenced By* on the left, *Influenced* on the right (paintings, artists, or movements), prev/next catalog browsing, optional fullscreen, link to artist biography.
|
||||
5. **Artist biography** — portrait, lifespan, movement, and Wikipedia-sourced intro text (`bio_short` / `bio_full`).
|
||||
|
||||
All artwork images are stored locally under `data/images/` — the UI never hot-links to Wikipedia or Commons at runtime (except optional on-demand fetch when a file is missing).
|
||||
@@ -149,7 +149,7 @@ Each artist appears as a **portrait circle** on their movement’s stream row:
|
||||
|
||||
Artist portraits stop wheel/drag propagation so zooming over a face does not fight portrait clicks.
|
||||
|
||||
**Note:** Movement lineage is **frontend curation** for layout and labels — it is not stored in PostgreSQL. Painting-to-painting influence edges (`painting_influences`) are separate and drive the 3D exit picker and detail panels.
|
||||
**Note:** Movement lineage is **frontend curation** for layout and labels — it is not stored in PostgreSQL. Painting influence links (`painting_influence_sources`, plus legacy `painting_influences` for hall navigation) are separate and drive the 3D exit picker and detail panels.
|
||||
|
||||
## Virtual gallery (3D halls)
|
||||
|
||||
@@ -191,7 +191,7 @@ Opened from the 3D hall (click a frame) or from influence thumbnails on another
|
||||
|
||||
| Layer | What you see |
|
||||
|-------|----------------|
|
||||
| **Detail** | Centre image, *Influenced By* (left) and *Influenced* (right), position in catalog (e.g. `3 of 12`) |
|
||||
| **Detail** | Centre image, *Influenced By* (left) and *Influenced* (right) — painting thumbnails, artist portraits, or movement swatches — position in catalog (e.g. `3 of 12`) |
|
||||
| **Fullscreen** | Click the centre image; `Escape` or click anywhere to return to detail only |
|
||||
|
||||
**Controls:**
|
||||
@@ -202,6 +202,7 @@ Opened from the 3D hall (click a frame) or from influence thumbnails on another
|
||||
| `←` / `→` | Same as prev / next (disabled while fullscreen is open) |
|
||||
| Click centre image | Open fullscreen lightbox |
|
||||
| Click influence thumbnail | Open that work’s detail (different artist allowed) |
|
||||
| Click influence artist portrait | Open that artist’s 3D gallery hall |
|
||||
| **← Back to Gallery** | Return to the hall you entered from — **3D camera position is preserved** |
|
||||
| **About {artist}** | Open artist biography |
|
||||
|
||||
@@ -217,9 +218,9 @@ Opened from the 3D hall (click a frame) or from influence thumbnails on another
|
||||
- **Movement filtering** on zoom only shows movements that have at least one artist active in the visible year range.
|
||||
- **Movement lineage** (`movement-lineage.ts`) documents art-historical predecessor→successor links for the flow diagram; extend that file to add or correct branches.
|
||||
- **One hall per artist** keeps navigation predictable: enter from the timeline or bio, leave via the single exit or back button.
|
||||
- **Influence-based hall links** connect artists through documented painting-to-painting relationships, grouped by movement at the exit.
|
||||
- **Influence-based hall links** connect artists through documented painting relationships, grouped by movement at the exit.
|
||||
- **3D gallery images** use locally cached files only; slow remote fetches would break realtime rendering.
|
||||
- **Influence data** is stored as directed edges between paintings, with optional citation fields (source author, quote, URL) for art-historical references.
|
||||
- **Influence data** is stored as directed links from paintings to sources (another painting, an artist, or a movement), with optional period fields and citation metadata (source author, quote, URL).
|
||||
|
||||
## Related docs
|
||||
|
||||
|
||||
@@ -113,22 +113,68 @@ Renaissance and medieval masters with large museum catalog dumps (e.g. Raphael,
|
||||
|
||||
## Painting influence graph
|
||||
|
||||
Directed edges in `painting_influences` drive:
|
||||
Directed influence links are stored in **`painting_influence_sources`**. Each row connects a painting to a **source** of type `painting`, `artist`, or `movement`, with optional period context (e.g. influence during the work’s creation year).
|
||||
|
||||
- **Painting detail** — *Influenced By* (left) and *Influenced* (right) panels with notes, aspects, and citations
|
||||
- **3D hall exit** — predecessor and successor artists grouped by movement
|
||||
- **3D gallery lamps** — a golden picture light above any frame whose work has an influence edge (`has_influence_links` on painting API responses)
|
||||
The legacy **`painting_influences`** table (painting-to-painting only) is still written for hall navigation compatibility and is backfilled into `painting_influence_sources` on migration.
|
||||
|
||||
Influence data drives:
|
||||
|
||||
- **Painting detail** — *Influenced By* (left) and *Influenced* (right) panels: painting thumbnails, artist portraits, or movement colour swatches, plus notes, aspects, period labels, and citations
|
||||
- **3D hall exit** — predecessor and successor artists grouped by movement (painting edges only)
|
||||
- **3D gallery lamps** — golden picture light above frames with any influence edge (`has_influence_links` on painting API responses)
|
||||
|
||||
### One-time migration
|
||||
|
||||
```bash
|
||||
npm run migrate:influence-sources # create table + backfill legacy painting edges
|
||||
```
|
||||
|
||||
### Curated updates
|
||||
|
||||
`npm run update-influences` runs `scripts/update-influences.js` against `scripts/art-influences-data.js`:
|
||||
|
||||
```bash
|
||||
npm run update-influences # insert edges; create missing artists/paintings
|
||||
npm run update-influences -- --fetch-images # also download images for newly created works
|
||||
npm run update-influences # insert curated edges
|
||||
npm run update-influences -- --fetch-images # also download images for newly created works
|
||||
npm run update-influences -- --discover # curated + web discovery pass
|
||||
npm run discover-influences # discovery only (no curated file pass)
|
||||
npm run update-influences -- --discover --limit=20 # cap discovery to N works
|
||||
```
|
||||
|
||||
Each entry defines a later `work` influenced by an earlier `influencedBy` painting, plus optional curator fields (`notes`, `aspects`, `source_author`, `source`). When `artistMeta` is included, missing artists are created with movement and lifespan. Missing paintings are inserted with `wikipedia_title` for image fetch.
|
||||
Each entry defines a later `work` and one or more `influencedBy` sources. Legacy single-object form is still supported:
|
||||
|
||||
Extend the data file to add lineage chains (e.g. Giotto → Masaccio → Michelangelo → Manet → Picasso → Warhol). New bridge artists may include Géricault, Friedrich, Constable, Giorgione, Poussin, de Chirico, and Böcklin.
|
||||
```javascript
|
||||
{ work: { artist: '...', title: '...', year: 1907 },
|
||||
influencedBy: { artist: 'Giotto', title: 'Lamentation' } }
|
||||
```
|
||||
|
||||
Multi-source form (painting, artist, movement):
|
||||
|
||||
```javascript
|
||||
{
|
||||
work: { artist: 'Pablo Picasso', title: "Les Demoiselles d'Avignon", year: 1907 },
|
||||
influencedBy: [
|
||||
{ type: 'painting', artist: 'Paul Cézanne', title: 'The Bathers' },
|
||||
{ type: 'artist', artist: 'Paul Cézanne', period: { duringCreation: true } },
|
||||
{ type: 'movement', movement: 'Fauvism', period: { start: 1905, end: 1907, note: '...' } },
|
||||
],
|
||||
notes, aspects, source_author, source, source_url,
|
||||
}
|
||||
```
|
||||
|
||||
When `artistMeta` is included, missing artists are created with movement and lifespan. Missing paintings are inserted with `wikipedia_title` for image fetch.
|
||||
|
||||
### Web discovery
|
||||
|
||||
`scripts/influence-discovery.js` searches art-history sources when `--discover` or `--discover-only` is passed:
|
||||
|
||||
- Wikipedia summaries and Wikidata **P737** (influenced by)
|
||||
- Met Museum collection API
|
||||
- DuckDuckGo site-restricted search across TheArtStory, Met, Google Arts & Culture, NGA, Art Institute of Chicago, MoMA, Britannica, JSTOR, Oxford Art Online, WikiArt, and Wikipedia
|
||||
|
||||
Discovered rows are stored with `confidence: discovered` and `discovered_via` (e.g. `wikipedia`, `wikidata`, `met`, `web:theartstory.org`). Period hints are inferred when the source text mentions influence during creation or a date range overlapping the work’s year.
|
||||
|
||||
Extend `art-influences-data.js` for high-quality curated chains; use discovery to suggest additional artist and movement links for manual review.
|
||||
|
||||
## Movement lineage (frontend flow diagram)
|
||||
|
||||
|
||||
@@ -111,8 +111,11 @@ Open http://localhost:3001 (or your configured `PORT`).
|
||||
| `npm run migrate:thumbnails` | `db/migrate-thumbnails.sql` | Add thumbnail columns |
|
||||
| `npm run fetch-artist-images` | `scripts/fetch-artist-images.js` | Backfill portrait files *(if present)* |
|
||||
| `npm run sync-image-paths` | `scripts/sync-image-paths.js` | Align DB paths with disk *(if present)* |
|
||||
| `npm run update-influences` | `scripts/update-influences.js` | Insert influence edges from `art-influences-data.js` |
|
||||
| `npm run migrate:influence-sources` | `scripts/migrate-influence-sources.js` | Create `painting_influence_sources` + backfill legacy edges |
|
||||
| `npm run update-influences` | `scripts/update-influences.js` | Insert influence links (painting / artist / movement) from `art-influences-data.js` |
|
||||
| `npm run update-influences -- --fetch-images` | ↑ | Also download images for newly created works |
|
||||
| `npm run update-influences -- --discover` | ↑ | Curated pass + web discovery (Wikipedia, Wikidata, Met, art-history sites) |
|
||||
| `npm run discover-influences` | `update-influences.js --discover-only` | Discovery pass only |
|
||||
|
||||
### Scripts in the repository
|
||||
|
||||
@@ -121,7 +124,8 @@ These are checked in and maintained:
|
||||
- `image-fetcher.js` — Wikimedia / museum image resolution
|
||||
- `fetch-artist-bios.js` — artist biographies
|
||||
- `expand-paintings.js` + `famous-paintings-data.js` — catalog expansion
|
||||
- `update-influences.js` + `art-influences-data.js` — painting influence graph
|
||||
- `update-influences.js` + `art-influences-data.js` — influence graph (paintings, artists, movements)
|
||||
- `influence-discovery.js` + `influence-resolver.js` — web discovery and polymorphic source resolution
|
||||
- `fetch-missing-images.js` — batch image backfill
|
||||
- `regenerate-thumbnails.js`, `audit-painting-images.js`
|
||||
|
||||
@@ -153,7 +157,7 @@ After clone: copy `.env.example` → `.env`, install dependencies, run `npm run
|
||||
| Wrong painting in 3D gallery frame | Stale or mismatched thumbnail file | `npm run regenerate-thumbnails`; gallery prefers full `image_path` |
|
||||
| Wrong painting image (fetch) | Bad museum / search match on first download | Add entry to `DIRECT_IMAGE_OVERRIDES` in `scripts/image-fetcher.js`, re-fetch file |
|
||||
| Empty exit navigation lists | No `painting_influences` edges for artist | `npm run update-influences` |
|
||||
| Empty *Influenced By* / *Influenced* on painting detail | No edges for that work | `npm run update-influences`; extend `art-influences-data.js` |
|
||||
| Empty *Influenced By* / *Influenced* on painting detail | No edges for that work | `npm run migrate:influence-sources` then `npm run update-influences`; extend `art-influences-data.js` or run `--discover` |
|
||||
| No golden lamps above frames in 3D hall | Stale API process or no influence edges | Restart server after API changes; run `npm run update-influences` |
|
||||
| Default Vite page instead of gallery | `client/dist` missing or stale | `cd client && npm run build` |
|
||||
| Permission denied creating tables | `gallery` user lacks CREATE | Run admin grants, then migrate |
|
||||
|
||||
Reference in New Issue
Block a user