Twenty-nine commits since v0.12.0. See CHANGELOG.md for the full list and the upgrade notes. Ten additions — the SEO package (site URL, robots.txt, sitemap.xml, JSON-LD), slideshow mode, opt-in original downloads, copy-link, previous/next album navigation, alt text from the Immich description, per-album location precision, the update notice, a Help tab, and a subpage cover grid separate from its photo grid. Two fixes: CJK album slugs, and the lightbox control bar. Photo of the Day (#476) was built on `dev` and reverted as not planned; it never appears in a release. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01DmsHVRL9yQvckUB1ZpVWre --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: ralksta <ralksta@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
61 lines
2.0 KiB
TypeScript
61 lines
2.0 KiB
TypeScript
/**
|
|
* The JSON-LD payload for an album page (#472).
|
|
*
|
|
* `ImageGallery` with an `ImageObject` for the cover is the annotation that
|
|
* pays off for a photography portfolio: it is what a search result can show,
|
|
* and it is where `creator` and `license` belong.
|
|
*
|
|
* Built as a pure function so the shape can be asserted without rendering, and
|
|
* so the "no site URL, no structured data" rule is stated once.
|
|
*/
|
|
|
|
export interface AlbumStructuredDataInput {
|
|
siteUrl: string | null;
|
|
/** Absolute URL of the album page. */
|
|
pageUrl: string | null;
|
|
albumName: string;
|
|
description?: string;
|
|
/** Absolute URL of the cover image, if there is one. */
|
|
coverUrl?: string | null;
|
|
/** The photographer, for `creator`. */
|
|
creator?: string;
|
|
/** A licence URL or short identifier, e.g. a Creative Commons link. */
|
|
license?: string;
|
|
photoCount?: number;
|
|
}
|
|
|
|
export function albumStructuredData(
|
|
input: AlbumStructuredDataInput,
|
|
): Record<string, unknown> | null {
|
|
// Structured data is a set of claims about URLs. Without an absolute site URL
|
|
// there is nothing truthful to claim, so nothing is emitted.
|
|
if (!input.siteUrl || !input.pageUrl) return null;
|
|
if (!input.albumName.trim()) return null;
|
|
|
|
const creator = input.creator?.trim()
|
|
? { '@type': 'Person', name: input.creator.trim() }
|
|
: undefined;
|
|
const license = input.license?.trim() || undefined;
|
|
|
|
return {
|
|
'@context': 'https://schema.org',
|
|
'@type': 'ImageGallery',
|
|
name: input.albumName.trim(),
|
|
url: input.pageUrl,
|
|
...(input.description?.trim() ? { description: input.description.trim() } : {}),
|
|
...(typeof input.photoCount === 'number' ? { numberOfItems: input.photoCount } : {}),
|
|
...(creator ? { creator } : {}),
|
|
...(license ? { license } : {}),
|
|
...(input.coverUrl
|
|
? {
|
|
image: {
|
|
'@type': 'ImageObject',
|
|
contentUrl: input.coverUrl,
|
|
...(creator ? { creator } : {}),
|
|
...(license ? { license } : {}),
|
|
},
|
|
}
|
|
: {}),
|
|
};
|
|
}
|