Improve 3D gallery and debug checkup workflow for image curation.
Add parquet floor, museum-style exit, movement-tinted walls, and gold/black frames with gallery sync after Fix it. Debug panel gets Checked and Fix it buttons; documentation and image-fetch reliability updates included. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
4c6acd5a3a
commit
019ce4e136
+22
-6
@@ -96,11 +96,17 @@ Full artist profile for the bio page and 3D gallery entry.
|
||||
"wikipedia_title": "Claude Monet"
|
||||
},
|
||||
"periods": [ { "id": 1, "name": "Milan Period", "start_year": 1482, "end_year": 1499, ... } ],
|
||||
"paintings": [ { "id": 10, "title": "...", "year": 1498, "image_path": "...", "thumbnail_path": "...", "wikipedia_title": "...", "has_influence_links": true, ... } ]
|
||||
"paintings": [ { "id": 10, "title": "...", "year": 1498, "image_path": "...", "thumbnail_path": "...", "wikipedia_title": "...", "has_influence_links": true, "checkup_checked": false, "checkup_fixed": false, ... } ]
|
||||
}
|
||||
```
|
||||
|
||||
Each painting includes `has_influence_links` (boolean) — `true` when the work appears in any `painting_influences` row as source or target. The 3D gallery uses this to show a golden lamp above the frame.
|
||||
Each painting includes:
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `has_influence_links` | `true` when the work appears in any influence row — 3D gallery shows a golden lamp above the frame |
|
||||
| `checkup_checked` | Reviewed in checkup / debug workflow (gold frame in 3D when true) |
|
||||
| `checkup_fixed` | Image replaced via **Fix it** |
|
||||
|
||||
Populate biographies with `npm run fetch-artist-bios` (see [data-and-images.md](data-and-images.md)).
|
||||
|
||||
@@ -163,7 +169,7 @@ Painting detail with influence graph neighbours.
|
||||
|
||||
```json
|
||||
{
|
||||
"painting": { "id": 10, "title": "...", "artist_name": "...", "image_path": "...", "has_influence_links": true, ... },
|
||||
"painting": { "id": 10, "title": "...", "artist_name": "...", "image_path": "...", "checkup_checked": false, "checkup_fixed": false, "has_influence_links": true, ... },
|
||||
"influencedBy": [
|
||||
{
|
||||
"source_type": "painting",
|
||||
@@ -281,12 +287,21 @@ Google-family image search for debug / checkup (Custom Search → Google Arts &
|
||||
|
||||
Download a remote URL and replace the painting’s local full image + thumbnail. Sets `checkup_fixed = true` and `checkup_checked = true`.
|
||||
|
||||
Uses `downloadImageForFix` in `scripts/image-fetcher.js` (browser User-Agent, referer fallbacks, Wikimedia URL upgrades) for reliable downloads from Google Arts, Commons, etc.
|
||||
|
||||
**Body**
|
||||
|
||||
```json
|
||||
{ "imageUrl": "https://…" }
|
||||
{
|
||||
"imageUrl": "https://…",
|
||||
"searchUrl": "https://…",
|
||||
"source": "google-arts",
|
||||
"thumbUrl": "https://…"
|
||||
}
|
||||
```
|
||||
|
||||
Only `imageUrl` is required; optional fields improve fetch success for hotlinked URLs.
|
||||
|
||||
**Response**
|
||||
|
||||
```json
|
||||
@@ -325,9 +340,10 @@ The React client wraps these endpoints in `client/src/api/client.ts`:
|
||||
| `preloadArtistImages(id)` | `POST /api/artists/:id/preload-images` |
|
||||
| `imageUrl(path)` | `/images/<path>` or placeholder |
|
||||
| `galleryImageUrl(painting)` | Local thumb/full only (3D) |
|
||||
| `galleryImageUrlWithRevision(painting, revision)` | Local URL with `?v=` cache buster after fix |
|
||||
| `paintingImageUrl(painting)` | Local file or on-demand API |
|
||||
| `api.getPaintingCheckup()` | `GET /api/paintings/checkup` |
|
||||
| `api.updatePaintingCheckupFlags(id, flags)` | `PATCH /api/paintings/:id/checkup-flags` |
|
||||
| `api.getPaintingDebugImageSearch(id)` | `GET /api/paintings/:id/debug-image-search` |
|
||||
| `api.fixPaintingImage(id, imageUrl)` | `POST /api/paintings/:id/fix-image` |
|
||||
| `debugImageProxyUrl(imageUrl)` | `GET /api/debug/image-proxy?url=…` |
|
||||
| `api.fixPaintingImage(id, imageUrl, context?)` | `POST /api/paintings/:id/fix-image` |
|
||||
| `debugImageProxyUrl(imageUrl, context?)` | `GET /api/debug/image-proxy?url=…` |
|
||||
|
||||
+31
-4
@@ -31,9 +31,14 @@ Gallery/
|
||||
├── server/ # Express API, DB pool, image service
|
||||
├── client/ # React/Vite frontend
|
||||
│ ├── src/ # Source (components, pages, 3D scene)
|
||||
│ │ ├── components/VirtualGallery.tsx # 3D hall (parquet, frames, museum exit)
|
||||
│ │ ├── components/PaintingDetail.tsx # Detail view + debug panel
|
||||
│ │ ├── components/MovementBands.tsx # Movement flow (SVG streams + branches)
|
||||
│ │ ├── pages/CheckupPage.tsx # Image audit table
|
||||
│ │ ├── data/historical-events.ts # Timeline event markers (UI)
|
||||
│ │ ├── data/movement-lineage.ts # Curated movement predecessor links (UI)
|
||||
│ │ ├── utils/parquetFloorTexture.ts # Procedural parquet floor
|
||||
│ │ ├── utils/debugMode.ts # Debug mode localStorage toggle
|
||||
│ │ └── utils/timelineView.ts # Shared zoom/pan math for timeline + movements
|
||||
│ └── dist/ # Production build (served by API when present)
|
||||
├── scripts/ # Seed, bios, catalog expansion, image fetch, checkup tools
|
||||
@@ -160,15 +165,20 @@ Each artist has **exactly one hall**. The hall is a rectangular room sized to fi
|
||||
|------|----------------|
|
||||
| One hall per artist | `VirtualGallery.tsx` builds a single room from that artist’s paintings |
|
||||
| Catalog depth | Most artists target **≥ 6** notable works via `npm run expand-catalog` and `famous-paintings-data.js`; some masters have larger museum dumps |
|
||||
| Paintings on walls | Works hang on the **back, left, and right** walls; the room **grows and uses multiple rows** when the catalog is large (e.g. 75+ works) |
|
||||
| Paintings on walls | Works hang on the **back, left, and right** walls in **one row per wall**; room **depth grows** when the catalog is large |
|
||||
| Corridor layout | **15+ paintings:** short back wall (up to 8 works), remaining works on extended **left/right** side walls — a long gallery corridor |
|
||||
| Wall order | On each wall, left → right: **later works on the left**, **earlier works on the right**; undated works sort toward the left |
|
||||
| Floor | Herringbone **parquet** procedural texture (`parquetFloorTexture.ts`) |
|
||||
| Wall tint | Gallery walls blend the artist’s **movement colour** into cream plaster tones |
|
||||
| Frame finish | **Unchecked** works: black moulding; **Reviewed** (`checkup_checked`): bright gold moulding at **double width** |
|
||||
| Influence lamps | A golden picture light appears **above frames** whose work has any influence-graph edge (`has_influence_links` from the API) |
|
||||
| Eye-level viewing | Frame centres sit at **eye height (~1.65 m)**; the camera stays **level with the floor** (no pitch up/down) |
|
||||
| Open centre | Floor and ceiling only — no columns, pedestals, or other centre objects |
|
||||
| Single exit | One doorway on the **front wall**; walk to it or click it |
|
||||
| Museum exit | Front-wall **double doors** with transom, brass hardware, sconces, marble threshold, and warm vestibule glow |
|
||||
| Hall-to-hall travel | Exit opens a panel: **Predecessors** (left) and **Successors** (right), each grouped by art movement |
|
||||
| Missing images | Works without a local file show a **draped canvas cover** in the frame (not a blank white rectangle) |
|
||||
| Detail view return | Opening a painting close-up **keeps the 3D hall mounted** in the background so position and view direction are preserved when you go back |
|
||||
| After image fix | Debug **Fix it** updates the gallery session, busts texture cache (`?v=N`), and returns to the hall with the new image and gold frame |
|
||||
|
||||
**Controls:**
|
||||
|
||||
@@ -213,6 +223,10 @@ Opened from the 3D hall (click a frame) or from influence thumbnails on another
|
||||
- **Influence links** push a new detail layer; **Back** from an influenced work returns to the painting you came from (and from there back to the gallery if applicable).
|
||||
- The 3D hall stays mounted in the background while detail is open so nothing is lost on return.
|
||||
|
||||
### Debug mode (developer)
|
||||
|
||||
When **Debug mode** is enabled from the home header, painting detail shows a bottom-left panel with image search preview and **Checked** / **Fix it** buttons. See [Developer tools (image audit)](#developer-tools-image-audit).
|
||||
|
||||
## Key design decisions
|
||||
|
||||
- **Timeline bounds** derive from the earliest art movement start year, not ancient-era metadata alone, so the default view opens where catalogued content begins.
|
||||
@@ -231,9 +245,22 @@ Optional workflow for curating local image files — not part of the public visi
|
||||
|---------|--------|---------|
|
||||
| **Debug mode** | Home header toggle (`client/src/utils/debugMode.ts`) | Persists in `localStorage`; enables debug panel on painting detail |
|
||||
| **Checkup page** | Home header → **Checkup** (`CheckupPage.tsx`) | Full-catalog table: gallery vs detail thumbnails, search, fix, review flags |
|
||||
| **Debug panel** | Painting detail (bottom-left, when debug mode on) | Google-family image search preview + **Fix it** for the current work |
|
||||
| **Debug panel** | Painting detail (bottom-left, when debug mode on) | Search preview + two action buttons |
|
||||
|
||||
**Checkup columns:** Gallery and Detail thumbnails, Search (reference image), Fix (replace local file), **Reviewed** (`checkup_checked`), **Fixed** (`checkup_fixed`).
|
||||
### Debug panel (painting detail)
|
||||
|
||||
When debug mode is on, a panel at the bottom-left shows the image search query, a preview when a result is found, and **two buttons**:
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **Checked** | Sets `checkup_checked` via `PATCH /api/paintings/:id/checkup-flags` (disabled once already reviewed) |
|
||||
| **Fix it** | Replaces local full + thumbnail from the search result via `POST /api/paintings/:id/fix-image`; sets **Fixed** and **Reviewed** |
|
||||
|
||||
After **Fix it**, the detail image, gallery textures, and frame colour (gold if reviewed) update without a full page reload. **Back to Gallery** returns to the live hall session, not a stale snapshot.
|
||||
|
||||
### Checkup page
|
||||
|
||||
**Columns:** Gallery and Detail thumbnails, Search (reference image), Fix (replace local file), **Reviewed** (`checkup_checked`), **Fixed** (`checkup_fixed`).
|
||||
|
||||
**Search visible** runs image search only for rows currently shown after text/filter — not automatically on page load. Fixing an image sets **Fixed** and **Reviewed**.
|
||||
|
||||
|
||||
@@ -337,7 +337,18 @@ As of a recent audit (~1200 paintings): **52 exact duplicate pairs** (52 removab
|
||||
When **Debug mode** is on (home header) or from the **Checkup** page:
|
||||
|
||||
1. **Search** — `GET /api/paintings/:id/debug-image-search` tries Google Custom Search (if `GOOGLE_CSE_API_KEY` + `GOOGLE_CSE_CX` are set in `.env`), Google Arts & Culture, Google Images scrape, then DuckDuckGo (`searchGoogleImagesFirst` in `scripts/image-fetcher.js`).
|
||||
2. **Fix** — `POST /api/paintings/:id/fix-image` downloads the chosen URL via `replacePaintingImageFromUrl` in `server/image-service.js`, regenerates the thumbnail with `sharp`, and sets checkup flags.
|
||||
2. **Fix** — `POST /api/paintings/:id/fix-image` downloads the chosen URL via `downloadImageForFix` → `replacePaintingImageFromUrl` in `server/image-service.js`, regenerates the thumbnail with `sharp`, and sets `checkup_fixed` + `checkup_checked`.
|
||||
|
||||
### Painting detail debug panel
|
||||
|
||||
With debug mode on, `PaintingDetail.tsx` shows a bottom-left panel with search preview and two buttons:
|
||||
|
||||
| Button | API | Effect |
|
||||
|--------|-----|--------|
|
||||
| **Checked** | `PATCH …/checkup-flags` `{ "checked": true }` | Marks reviewed; 3D frame turns gold |
|
||||
| **Fix it** | `POST …/fix-image` | Saves image to disk, sets both flags, refreshes detail + gallery |
|
||||
|
||||
The client passes `searchUrl`, `source`, and `thumbUrl` from the search result to improve download reliability. After a fix, `HomePage` updates the gallery session and appends a revision query on 3D texture URLs so replaced files reload even when the path is unchanged.
|
||||
|
||||
Checkup **Search visible** queues search for filtered rows only (3 concurrent); it does not search the full catalog on load.
|
||||
|
||||
|
||||
@@ -168,4 +168,7 @@ After clone: copy `.env.example` → `.env`, install dependencies, run `npm run
|
||||
| Permission denied creating tables | `gallery` user lacks CREATE | Run admin grants, then migrate |
|
||||
| Wikipedia API rate limit during fetch | Too many requests in a row | Wait and re-run; scripts retry with backoff |
|
||||
| Checkup **Reviewed** toggle returns 404 | Stale server process missing new routes | Restart `npm run dev` after pulling API changes |
|
||||
| **Fix it** fails with `read ECONNRESET` | Remote host dropped connection | Restart server; client sends `searchUrl` / `source`; retry or use Commons URL in overrides |
|
||||
| Fixed image not shown in 3D gallery | Stale gallery session or cached texture | Rebuild client; fix updates session + `?v=` revision — use **Back to Gallery** (not browser back) |
|
||||
| Frame still black after **Checked** | Gallery session not synced | Re-enter hall or toggle debug **Checked** from detail with gallery open behind overlay |
|
||||
| Duplicate works in gallery / timeline | Double import or variant Wikipedia titles | `npm run find-duplicates`; merge or delete spare rows manually |
|
||||
|
||||
Reference in New Issue
Block a user