#362 · A timeline's own image: settings, cards, previews and oEmbed (#339)
- timeline-app
- pull request
- sharing
Closes #339 ## What changed - **Storage**: the image is one of the document's settings (`image: {url, alt, width?, height?}`, validated by `validateTimelineImage` in `src/timeline-image.ts`: HTTPS without credentials, alt text up to 1000 characters). Stored in `oc_timelines.image` and in the history settings JSON, so saved history, `.ochx` exports, forks (shared-index column list), duplicates, merges, pull requests and retention's saves keep it. Like every other image it is a **link**, never a copy on the server (docs/images.md); the editor's **Export** embeds a copy with the other images. - **API**: `PATCH /api/timelines/{id}/settings` takes `image` (object, or `null` to remove), owner only, saved as a revision of the settings alone (via `applyMerge`) in the same locked transaction; 409 for comparisons and copies still being prepared. Writers can also change it through the document (`PUT …/changes` settings or a whole-document save); editors' saves merge it like other settings. OpenAPI updated (`TimelineImage` schema, settings, metadata, documents). - **Web UI**: an **Image** section in Sharing & settings (preview cropped as on cards, link, alt text, Set/Replace/Remove; the preview's natural size is sent for oEmbed). - **Cards** (`platform/components/timeline-image.jsx`, one line in `timeline-list.jsx`): image at the top, 16:9 crop (capped at 220px tall), `loading="lazy"`, `referrerpolicy="no-referrer"`, placeholder when none or when the timeline is labelled. Search results come from `searchTimelines`, which now selects `t.image`. - **Editor / reader header**: the image above the title (cropped 16:9, up to 420px wide), from the embedded copy when offline, blurred on labelled timelines like their other images. - **Link previews**: the timeline page's `og:image` / `twitter:image` (with alt and size) is the image when the timeline is public and unlabelled; moment and duration pages keep the drawn preview. oEmbed's `thumbnail_url` is the image when its size is known (oEmbed requires width/height), otherwise the drawn preview. - **CSP**: unchanged. `img-src https:` already allows linked images. - Docs: images.md (new "A timeline's own image" section), sharing.md, collaboration.md, architecture.md. ## Tests - `test/timeline-image.test.mjs` (new): validation, documents, the editor's index and export, merges, retention and sparse-save settings. - `test/timeline-image-postgres.mjs` (new, added to CI): set/replace/remove through settings, validation 400s, owner-only, history versions, fork and duplicate, search cards, changes through `/changes` and whole-document saves, merge with an editor's base, comparison 409. - `test/sharing-postgres.mjs`: the settings UI in a browser (set, then remove), og/twitter meta, oEmbed thumbnail (with and without size), the card's lazy ` `, the editor heading, and the fallbacks for labelled timelines. - Passing locally: `npm run typecheck`, `npm test` (337 pass), prettier, the Postgres suites (timeline-image, image-links, history, shared-history, save-merge, merge, incremental, retention, openapi, labels, optional-history, fork-read-through, document-stream, pull-history, next-postgres), and in chromium `sharing-postgres`, `test:platform` and `test:browser`. ## Notes - "Uploaded image": the server does not store image files (docs/images.md, #109), so the image is set as a link. Exports carry an embedded copy. - Another PR for #337/#338 is reworking `timeline-list.jsx`. The card change here is one import plus one ` ` line, and `searchTimelines` gains `t.image`. Both should be easy to carry over.
Sources
Open #362 · A timeline's own image: settings, cards, previews and oEmbed (#339) in the timeline