Improve timeline UX and add catalog restore scripts for paintings and portraits.
Load the home-page catalog once with a lightweight artists API, batch pan/zoom updates per frame, use dynamic year labels, and speed up movement-flow zoom. Add sync-image-paths and fetch-artist-images plus docs for the post-seed pipeline.
This commit is contained in:
@@ -65,9 +65,12 @@ Artists for timeline portraits and the movement flow diagram.
|
||||
| `start` | int | Only artists alive after this year |
|
||||
| `end` | int | Only artists born before this year |
|
||||
| `movement_id` | int | Filter by movement |
|
||||
| `timeline` | bool | When `1` or `true`, return a lightweight row set for the home-page timeline (omits `bio_full` and other heavy fields) |
|
||||
|
||||
**Response** — array of artist objects with joined `movement_name` and `movement_color`.
|
||||
|
||||
The React home page loads the timeline catalog **once** on mount via `GET /api/bounds`, `GET /api/timeline?start=…&end=…` (full range), and `GET /api/artists?timeline=1`. Pan and zoom filter movements and portraits **client-side** — no refetch per view change. Rapid pan/zoom is batched with `createViewChangeScheduler()` (one React update per animation frame).
|
||||
|
||||
---
|
||||
|
||||
## `GET /api/movements/:id/artists`
|
||||
@@ -581,6 +584,7 @@ The React client wraps these endpoints in `client/src/api/client.ts`:
|
||||
| `api.getBounds()` | `GET /api/bounds` |
|
||||
| `api.getTimeline(start, end)` | `GET /api/timeline` |
|
||||
| `api.getArtists(...)` | `GET /api/artists` |
|
||||
| `api.getTimelineArtists()` | `GET /api/artists?timeline=1` |
|
||||
| `api.getArtist(id)` | `GET /api/artists/:id` |
|
||||
| `api.getArtistNavigation(id)` | `GET /api/artists/:id/navigation` |
|
||||
| `api.getPainting(id)` | `GET /api/paintings/:id` |
|
||||
|
||||
+27
-5
@@ -53,6 +53,10 @@ Gallery/
|
||||
│ │ └── 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
|
||||
│ ├── seed-wikipedia.js
|
||||
│ ├── seed-catalog-data.js
|
||||
│ ├── sync-image-paths.js
|
||||
│ ├── fetch-artist-images.js
|
||||
│ ├── fetch-artist-bios.js
|
||||
│ ├── expand-paintings.js
|
||||
│ ├── famous-paintings-data.js
|
||||
@@ -92,7 +96,7 @@ npm run dev:server # API on PORT from .env (3520 production, 3001 typical dev)
|
||||
npm run dev:client # Vite on :5173, proxies /api and /images to PORT
|
||||
```
|
||||
|
||||
Use the Vite URL during frontend work for HMR.
|
||||
Use the Vite URL during frontend work for HMR. When the public domain is proxied to Vite (see [`deploy/nginx-gallery.conf`](../deploy/nginx-gallery.conf)), both `dev:server` and `dev:client` must stay running or visitors see **503**.
|
||||
|
||||
## User navigation flow
|
||||
|
||||
@@ -132,12 +136,26 @@ The home page shows two linked views over the **same year window** (`viewStart`
|
||||
| Era bar | `Timeline.tsx` | Historical eras, major event markers, click-to-zoom |
|
||||
| Movement flow | `MovementBands.tsx` | Curved streams per movement, lineage branches, artist portraits |
|
||||
|
||||
Both views share zoom/pan behaviour via `client/src/utils/timelineView.ts`. The home page uses a **fixed viewport** (`100vh`): timeline + movement flow sit in a shared `home-timeline-stack` so event guide lines can extend from the era bar down through the movement canvas. The movement flow compresses vertically so all movements in the visible year range fit without page scrolling.
|
||||
Both views share zoom/pan behaviour via `client/src/utils/timelineView.ts` (`zoomTimelineView`, `panTimelineView`, `chooseTimelineTickInterval`, `createViewChangeScheduler`). The home page uses a **fixed viewport** (`100vh`): timeline + movement flow sit in a shared `home-timeline-stack` so event guide lines can extend from the era bar down through the movement canvas. The movement flow compresses vertically so all movements in the visible year range fit without page scrolling.
|
||||
|
||||
### Timeline data loading
|
||||
|
||||
On first visit, `HomePage.tsx` fetches the full catalog once:
|
||||
|
||||
1. `GET /api/bounds` — initialise the year range.
|
||||
2. `GET /api/timeline?start=…&end=…` — all eras and movements for that range.
|
||||
3. `GET /api/artists?timeline=1` — lightweight artist rows (portraits, lifespan, movement colour; no full biography text).
|
||||
|
||||
Pan, zoom, and era/event click-to-zoom only update **local** `viewStart` / `viewEnd` state. `MovementBands.tsx` and `Timeline.tsx` filter what is visible for the current window — they do not trigger new API calls. The “Loading art history…” message appears only until the first successful load completes.
|
||||
|
||||
View updates are **batched to one commit per animation frame** via `createViewChangeScheduler()` in `timelineView.ts` (`HomePage.tsx` → `handleViewChange`), so rapid scroll-wheel events do not flood React with separate renders.
|
||||
|
||||
### Timeline year labels
|
||||
|
||||
Year ticks along the bottom of the era bar use **large, high-contrast** labels (bold cream text with shadow). The active range in the control row (e.g. `1400 CE — 1900 CE`) uses the same stronger styling.
|
||||
|
||||
Label density is **dynamic**: `chooseTimelineTickInterval()` in `timelineView.ts` picks a “nice” step (1, 2, 5, 10, … 5000 years) from the visible span and measured bar width so labels stay ~76 px apart. Zoomed-out overviews show fewer dates; zooming in reveals finer steps automatically.
|
||||
|
||||
### Timeline controls
|
||||
|
||||
| Input | Action |
|
||||
@@ -165,7 +183,7 @@ Each visible movement is drawn as a **portrait-width curved stream** (~54 px str
|
||||
| Vertical depth | Successor movements sit on rows below their deepest parent; sibling movements at the same depth are spread into lanes to limit overlap |
|
||||
| Branch connectors | Smooth curves from the **centre** of a parent stream to the **centre** of each child stream (siblings fan out along the parent’s length) |
|
||||
| Visual blending | Path-aligned SVG gradients with transparent fades at stream ends and branch junctions; streams draw on top of branches so overlap brightness stays uniform |
|
||||
| Filtering | Same rule as the API: only movements with at least one artist active in the visible year range |
|
||||
| Filtering | Same rule as the API: only movements with at least one artist active in the visible year range (filtered client-side after initial load) |
|
||||
| Viewport layout | Row height and stream width scale from measured canvas size so every visible movement row fits in the remaining screen space |
|
||||
|
||||
### Artists on movement streams
|
||||
@@ -184,12 +202,14 @@ Each artist appears as a **portrait circle** on their movement’s stream row:
|
||||
|
||||
| Input | Action |
|
||||
|-------|--------|
|
||||
| Scroll wheel on flow canvas | Zoom (same range as timeline) |
|
||||
| Scroll wheel anywhere on flow canvas | Zoom (same year range as timeline; works over portraits and labels too) |
|
||||
| Drag on flow canvas | Pan |
|
||||
| Click portrait | Open artist biography |
|
||||
| Click **movement name** (label on stream) | Open **movement gallery** for that movement |
|
||||
|
||||
Artist portraits stop wheel/drag propagation so zooming over a face does not fight portrait clicks. Hovering a portrait highlights the artist’s lifespan on the era bar and brightens their segment on the movement stream.
|
||||
**Zoom/pan performance:** The movement canvas uses a **capture-phase** wheel listener so scroll zoom works even when the cursor is over a portrait. While scrolling or dragging, a short **interaction mode** (~120 ms after the last input) draws simplified solid SVG strokes and temporarily hides portrait/label DOM so zoom stays responsive; full gradients and portraits return when you stop.
|
||||
|
||||
Only **mousedown** on portraits and movement labels stops propagation (so drag-to-pan does not start when clicking them). Hovering a portrait highlights the artist’s lifespan on the era bar and brightens their segment on the movement stream.
|
||||
|
||||
**Note:** Movement lineage is **frontend curation** for layout and labels — it is not stored in PostgreSQL. Painting influence links live in **`painting_influence_sources`** (paintings, artists, or movements as sources). The API reads that table for detail panels, hall navigation, and `has_influence_links`. The legacy **`painting_influences`** table is still written in parallel when curators add painting-to-painting edges but is not queried for display.
|
||||
|
||||
@@ -309,6 +329,8 @@ Next to the toggle, **Show more** (checkbox, persisted in `localStorage`) opens
|
||||
## 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.
|
||||
- **Timeline catalog** loads once from the API; pan/zoom is client-side only, with per-frame batching via `createViewChangeScheduler()`.
|
||||
- **Movement flow interaction** uses simplified SVG and hides portrait DOM during active scroll/drag so zoom stays responsive over dense portrait fields.
|
||||
- **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.
|
||||
|
||||
@@ -26,11 +26,14 @@ File names are sanitised `{Artist}_{Title}.{ext}`. The image service can redisco
|
||||
|
||||
| Script | npm command | Role |
|
||||
|--------|-------------|------|
|
||||
| `seed-wikipedia.js` | `npm run seed` | Initial eras, movements, artists, paintings, influences |
|
||||
| `seed-wikipedia.js` | `npm run seed` | Initial eras, movements, artists, one flagship painting per artist |
|
||||
| `seed-catalog-data.js` | *(data only)* | Eras, movements, artist metadata consumed by seed |
|
||||
| `sync-image-paths.js` | `npm run sync-image-paths` | Import painting rows from disk; set `image_path` / `thumbnail_path` |
|
||||
| `fetch-artist-images.js` | `npm run fetch-artist-images` | Download or link artist portraits under `data/images/portraits/` |
|
||||
| `fetch-artist-bios.js` | `npm run fetch-artist-bios` | Wikipedia intros → `bio_short` / `bio_full` |
|
||||
| `famous-paintings-data.js` | *(data only)* | Curated list of notable works per artist |
|
||||
| `expand-paintings.js` | `npm run expand-catalog` | Inserts works from data file for thin catalogs |
|
||||
| `art-influences-data.js` | *(data only)* | Curated painting-to-painting influence edges |
|
||||
| `art-influences-data.js` | *(data only)* | Curated influence edges (painting / artist / movement) |
|
||||
| `update-influences.js` | `npm run update-influences` | Applies influence graph; creates missing artists/works |
|
||||
| `fetch-missing-images.js` | `npm run fetch-images` | Downloads files for paintings missing on disk |
|
||||
| `image-fetcher.js` | *(library)* | Wikimedia / museum resolution used by fetch scripts and API |
|
||||
@@ -42,23 +45,26 @@ File names are sanitised `{Artist}_{Title}.{ext}`. The image service can redisco
|
||||
## Typical workflow
|
||||
|
||||
```text
|
||||
migrate → seed → fetch-artist-bios → expand-catalog → update-influences → fetch-images (per artist or batch) → build client
|
||||
migrate → seed → sync-image-paths → fetch-artist-images → fetch-artist-bios → expand-catalog → update-influences → fetch-images (per artist or batch) → build client
|
||||
```
|
||||
|
||||
1. **Seed** creates the base catalog (often one flagship painting per modern artist).
|
||||
2. **fetch-artist-bios** fills biography fields for every artist with a `wikipedia_title`.
|
||||
3. **expand-catalog** brings each artist up to at least **6** notable works (configurable via `MIN_PAINTINGS`).
|
||||
4. **fetch-images** downloads artwork files; the 3D gallery needs local files for reliable textures.
|
||||
1. **Seed** creates the base catalog (one flagship painting per artist; ~100 artists).
|
||||
2. **sync-image-paths** imports additional paintings when `data/images/paintings/` already contains files from a full clone (filename pattern `{Artist}_{Title}.jpg`).
|
||||
3. **fetch-artist-images** sets `portrait_path` from local files or Wikipedia.
|
||||
4. **fetch-artist-bios** fills biography fields for every artist with a `wikipedia_title`.
|
||||
5. **expand-catalog** brings each artist up to at least **6** notable works (configurable via `MIN_PAINTINGS`).
|
||||
6. **update-influences** loads the influence graph (*Influenced By* / *Influenced* panels, 3D hall lamps, exit navigation).
|
||||
7. **fetch-images** downloads artwork files still missing on disk; the 3D gallery needs local files for reliable textures.
|
||||
|
||||
## Seeding pipeline
|
||||
|
||||
`npm run seed` runs `scripts/seed-wikipedia.js`, which:
|
||||
|
||||
1. Inserts **historical eras** and **art movements** (curated date ranges and colours).
|
||||
1. Inserts **historical eras** and **art movements** (curated date ranges and colours from `seed-catalog-data.js`).
|
||||
2. For each curated **artist**:
|
||||
- Creates **artist periods** and **paintings**.
|
||||
- May download portraits and painting images (depending on seed script version).
|
||||
3. Writes **painting_influences** edges from curated scholarship references (mirrored into `painting_influence_sources` when you run `npm run migrate:influence-sources` and `npm run update-influences`).
|
||||
- Creates **artist periods** and one **flagship painting**.
|
||||
- May download portraits and painting images when run with `--fetch-images`.
|
||||
3. Does **not** insert influence edges — run `npm run update-influences` after seed (see [Painting influence graph](#painting-influence-graph)).
|
||||
|
||||
Those influence edges power **3D hall navigation** and painting detail panels via **`painting_influence_sources`** (see `GET /api/artists/:id/navigation` and `GET /api/paintings/:id` in [API.md](API.md)).
|
||||
|
||||
@@ -111,7 +117,44 @@ To add more works, append entries to `famous-paintings-data.js`:
|
||||
|
||||
`wikipedia_title` is optional; it defaults to `title`. Use it when the Wikipedia article name differs from the display title.
|
||||
|
||||
Renaissance and medieval masters with large museum catalog dumps (e.g. Raphael, Dürer) are usually above the minimum already; expansion targets Impressionists, modernists, and other artists who had only a single seed painting.
|
||||
Renaissance and medieval masters with large museum catalog dumps (e.g. Raphael, Dürer) are usually above the minimum already when **`sync-image-paths`** has imported files from disk; expansion targets Impressionists, modernists, and other artists who had only a single seed painting.
|
||||
|
||||
## Importing paintings from disk
|
||||
|
||||
When the repository includes a full `data/images/paintings/` tree but the database was seeded fresh (one row per artist), run:
|
||||
|
||||
```bash
|
||||
npm run sync-image-paths
|
||||
```
|
||||
|
||||
`scripts/sync-image-paths.js`:
|
||||
|
||||
1. Scans `data/images/paintings/` for full-size files (not `thumbs/`).
|
||||
2. Matches filenames to artists using the same `{Artist}_{Title}` sanitisation as `server/image-service.js`.
|
||||
3. **Updates** `image_path` / `thumbnail_path` on existing rows when files are found.
|
||||
4. **Inserts** missing painting rows for files not yet in the catalog.
|
||||
|
||||
Flags:
|
||||
|
||||
- `--dry-run` — report counts only, no DB writes.
|
||||
|
||||
Safe to re-run; already-imported works are skipped by normalized title matching.
|
||||
|
||||
Typical result on a full clone: ~1,000+ paintings linked from ~1,000 on-disk files.
|
||||
|
||||
## Artist portraits
|
||||
|
||||
`npm run fetch-artist-images` runs `scripts/fetch-artist-images.js`:
|
||||
|
||||
1. For each artist, checks `data/images/portraits/{Artist}.jpg` (or other extensions) and sets `portrait_path` when a local file exists.
|
||||
2. Otherwise downloads from Wikipedia / search fallbacks via `image-fetcher.js`.
|
||||
|
||||
Flags:
|
||||
|
||||
- `--force` — re-fetch even when `portrait_path` is already set.
|
||||
- `--limit=N` — process only the first N artists needing portraits.
|
||||
|
||||
Run after seed when portrait files exist on disk but the DB still has null `portrait_path` values.
|
||||
|
||||
## Painting influence graph
|
||||
|
||||
@@ -216,6 +259,8 @@ Wing layout (up to 55 works per wing, side-wall-only hang, window gap placement)
|
||||
| Storage | TypeScript module in the client — **not** a database table |
|
||||
| Format | `{ id, name, startYear, endYear?, shortLabel? }` — omit `endYear` for a single-year pin |
|
||||
| Interaction | Click a marker to zoom the shared timeline/movement view to that period |
|
||||
| Year axis labels | Dynamic density in `Timeline.tsx` via `chooseTimelineTickInterval()` — fewer labels when zoomed out |
|
||||
| Pan/zoom batching | `createViewChangeScheduler()` in `timelineView.ts` — one React update per animation frame |
|
||||
| Vertical guides | `TimelineEventGuides.tsx` draws faint gold lines (or shaded spans) from the marker row down through the movement flow, aligned to the same year scale |
|
||||
|
||||
Edit `HISTORICAL_EVENTS` and rebuild the client to extend the set.
|
||||
|
||||
+21
-8
@@ -55,7 +55,7 @@ Or step by step:
|
||||
|
||||
```bash
|
||||
npm run migrate # db/schema.sql + db/migrate-*.sql via server/migrate.js
|
||||
npm run seed # eras, movements, artists, paintings, influences
|
||||
npm run seed # eras, movements, artists, flagship paintings (one per artist)
|
||||
```
|
||||
|
||||
`npm run migrate` is safe to re-run on existing databases (uses `IF NOT EXISTS` / `ADD COLUMN IF NOT EXISTS`).
|
||||
@@ -67,6 +67,8 @@ If migration fails with permission errors, grant schema rights to the app user f
|
||||
After a fresh seed, run these to match a fully populated local install:
|
||||
|
||||
```bash
|
||||
npm run sync-image-paths # import paintings from data/images/paintings/ (clone with image files)
|
||||
npm run fetch-artist-images # link local portraits or download from Wikipedia
|
||||
npm run fetch-artist-bios # bio_short / bio_full from Wikipedia
|
||||
npm run expand-catalog # famous works for artists below MIN_PAINTINGS
|
||||
npm run update-influences # painting influence graph for detail view + hall exits
|
||||
@@ -79,6 +81,8 @@ npm run fetch-images -- --limit=50 --max-wait=120 # same batch size, longer look
|
||||
cd client && npm run build && cd ..
|
||||
```
|
||||
|
||||
**Order matters:** `sync-image-paths` should run when the repo already contains painting files under `data/images/paintings/` but the database only has one flagship work per artist (typical after `npm run setup` on a clone). `update-influences` is required for *Influenced By* / *Influenced* panels and golden lamps in the 3D hall — seed does not insert influence edges.
|
||||
|
||||
Image fetch can take hours if you run it for the entire catalog. The first line of each run reports **`Missing local files: N`**. Use **`npm run fetch-images -- --limit=N`** for random batches (10s per painting by default), **`--artist="…"`** for one artist in catalog order, or on-demand resolution when viewing a painting in the detail view.
|
||||
|
||||
## Run
|
||||
@@ -139,9 +143,9 @@ Or use the systemd unit in [`deploy/gallery.service`](../deploy/gallery.service)
|
||||
|
||||
Point `gallery.mysuperlab.netcraze.pro` at the host running the app. Example nginx config: [`deploy/nginx-gallery.conf`](../deploy/nginx-gallery.conf).
|
||||
|
||||
**Development (default in repo):** nginx forwards to **Vite on `127.0.0.1:5173`**. Run both `npm run dev:server` (API on `3520`) and `npm run dev:client` (`5173`). Vite proxies `/api` and `/images` to the API.
|
||||
**Development (default in repo):** nginx forwards to **Vite on `127.0.0.1:5173`**. Run both `npm run dev:server` (API on `3520`) and `npm run dev:client` (`5173`). Vite proxies `/api` and `/images` to the API. If either process stops, the public hostname may return **503** (reverse proxy cannot reach upstream).
|
||||
|
||||
**Production (built SPA):** change nginx `proxy_pass` to `http://127.0.0.1:3520` after `npm run build` and `npm run start` — Node serves `client/dist` and the API on one port.
|
||||
**Production (built SPA):** change nginx `proxy_pass` to `http://127.0.0.1:3520` after `npm run build` and `npm run start` — Node serves `client/dist` and the API on one port. Prefer the systemd unit in [`deploy/gallery.service`](../deploy/gallery.service) so the process restarts automatically.
|
||||
|
||||
Keep `TRUST_PROXY=true` in `.env` so Express sees the correct client IP and scheme.
|
||||
|
||||
@@ -168,8 +172,10 @@ Allow inbound **TCP 3520** on the gallery host if clients reach it directly on t
|
||||
| `npm run regenerate-thumbnails` | `scripts/regenerate-thumbnails.js` | Rebuild all thumbs from full local files |
|
||||
| `npm run audit-painting-images` | `scripts/audit-painting-images.js` | List thumb/full aspect-ratio mismatches |
|
||||
| `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 fetch-artist-images` | `scripts/fetch-artist-images.js` | Download portraits or link existing files under `data/images/portraits/` |
|
||||
| `npm run fetch-artist-images -- --force` | ↑ | Re-fetch even when `portrait_path` is set |
|
||||
| `npm run sync-image-paths` | `scripts/sync-image-paths.js` | Link `image_path` / `thumbnail_path` and import missing painting rows from disk |
|
||||
| `npm run sync-image-paths -- --dry-run` | ↑ | Report only, no DB writes |
|
||||
| `npm run migrate:influence-sources` | `scripts/migrate-influence-sources.js` | Create `painting_influence_sources` + backfill legacy edges |
|
||||
| `npm run migrate:checkup-flags` | `scripts/migrate-checkup-flags.js` | Add `checkup_checked` / `checkup_fixed` on `paintings` |
|
||||
| `npm run migrate:artist-checkup-flags` | `scripts/migrate-artist-checkup-flags.js` | Add `checkup_checked` / `checkup_fixed` on `artists` (bio debug) |
|
||||
@@ -192,7 +198,10 @@ Allow inbound **TCP 3520** on the gallery host if clients reach it directly on t
|
||||
|
||||
These are checked in and maintained:
|
||||
|
||||
- `seed-wikipedia.js`, `seed-catalog-data.js` — initial catalog (`npm run seed`)
|
||||
- `image-fetcher.js` — Wikimedia / museum image resolution
|
||||
- `fetch-artist-images.js` — artist portrait download / disk linking
|
||||
- `sync-image-paths.js` — import paintings and align paths from `data/images/paintings/`
|
||||
- `fetch-artist-bios.js` — artist biographies
|
||||
- `expand-paintings.js` + `famous-paintings-data.js` — catalog expansion
|
||||
- `update-influences.js` + `art-influences-data.js` — influence graph (paintings, artists, movements)
|
||||
@@ -207,8 +216,6 @@ These are checked in and maintained:
|
||||
- `migrate-painting-annotations.js`, `update-painting-annotations.js`, `painting-annotations-data.js` — art-history notes on painting detail
|
||||
- `regenerate-thumbnails.js`, `audit-painting-images.js`
|
||||
|
||||
These are referenced in `package.json` but may need to be restored from git history if missing locally: `fetch-artist-images.js`, `sync-image-paths.js`.
|
||||
|
||||
See [data-and-images.md](data-and-images.md) for pipeline details and override maps.
|
||||
|
||||
## Remote repository
|
||||
@@ -226,9 +233,15 @@ After clone: copy `.env.example` → `.env`, install dependencies, run `npm run
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---------|--------------|-----|
|
||||
| Empty timeline | DB not seeded | `npm run seed` |
|
||||
| 503 on public URL | Vite or API not running behind nginx | Start `npm run dev:server` + `npm run dev:client`, or switch nginx to production `:3520` |
|
||||
| 500 on all `/api/*` | Wrong `.env` or Postgres down | Check connection, logs |
|
||||
| “Biographical information not yet available” | Bios not fetched | `npm run fetch-artist-bios` |
|
||||
| Artist hall has only 1–2 paintings | Catalog not expanded | `npm run expand-catalog`; extend `famous-paintings-data.js` |
|
||||
| Placeholder portraits on timeline | `portrait_path` not set | `npm run fetch-artist-images` |
|
||||
| Artist hall has only 1–2 paintings | DB not expanded / disk not imported | `npm run sync-image-paths` then `npm run expand-catalog`; extend `famous-paintings-data.js` |
|
||||
| Movement flow shows “Loading art history…” repeatedly | Stale client refetching on every pan/zoom | Pull latest client; timeline loads catalog once — hard-refresh |
|
||||
| Movement flow slow on first load | Large artist payload | Client uses `GET /api/artists?timeline=1` (no full bios); rebuild client |
|
||||
| Movement flow zoom sluggish / dead over portraits | Stale client build | Rebuild client — wheel uses capture listener + interaction-mode rendering; hard-refresh |
|
||||
| Timeline year labels overlap when zoomed out | Stale client | Rebuild client — `chooseTimelineTickInterval()` adapts step to span and bar width |
|
||||
| Black frames / canvas covers in 3D gallery | No local image for painting | `npm run fetch-images -- --limit=50` or `--artist="…"`; then `POST …/preload-images` |
|
||||
| Many `⏱ timeout` lines in fetch batch | Default 10s cap too short for hard works | `--max-wait=120` or raise `FETCH_MAX_WAIT_SEC` |
|
||||
| White/grey flicker on frames | Texture loading or z-fighting with wall | Rebuild client (`cd client && npm run build`); ensure latest `VirtualGallery.tsx` |
|
||||
|
||||
Reference in New Issue
Block a user