Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
"type": "git",
"url": "https://github.com/SimplePhotoGallery/core"
},
"engines": {
"node": ">=20.0.0"
},
"type": "module",
"main": "./dist/gallery.js",
"types": "./dist/gallery.d.ts",
Expand Down
30 changes: 15 additions & 15 deletions common/src/gallery/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { z } from 'zod';

/** Zod schema for thumbnail metadata including path and dimensions */
export const ThumbnailSchema = z.object({
export const ThumbnailSchema = z.looseObject({
baseUrl: z.string().optional(),
path: z.string(),
pathRetina: z.string(),
Expand All @@ -11,7 +11,7 @@ export const ThumbnailSchema = z.object({
});

/** Zod schema for media file metadata including type, dimensions, and thumbnail info */
export const MediaFileSchema = z.object({
export const MediaFileSchema = z.looseObject({
type: z.enum(['image', 'video']),
filename: z.string(),
url: z.string().optional(),
Expand All @@ -26,7 +26,7 @@ export const MediaFileSchema = z.object({
* Zod schema for media file with path.
* @deprecated Use MediaFileSchema instead which uses 'filename' instead of 'path'.
*/
export const MediaFileDeprecatedSchema = z.object({
export const MediaFileDeprecatedSchema = z.looseObject({
type: z.enum(['image', 'video']),
path: z.string(),
alt: z.string().optional(),
Expand All @@ -37,7 +37,7 @@ export const MediaFileDeprecatedSchema = z.object({
});

/** Zod schema for a gallery section containing title, description, and media files */
export const GallerySectionSchema = z.object({
export const GallerySectionSchema = z.looseObject({
title: z.string().optional(),
description: z.string().optional(),
images: z.array(MediaFileSchema),
Expand All @@ -47,14 +47,14 @@ export const GallerySectionSchema = z.object({
* Zod schema for a gallery section containing title, description, and media files.
* @deprecated Use GallerySectionSchema instead which uses MediaFileSchema.
*/
export const GallerySectionDeprecatedSchema = z.object({
export const GallerySectionDeprecatedSchema = z.looseObject({
title: z.string().optional(),
description: z.string().optional(),
images: z.array(MediaFileDeprecatedSchema),
});

/** Zod schema for sub-gallery metadata including title, header image, and path */
export const SubGallerySchema = z.object({
export const SubGallerySchema = z.looseObject({
title: z.string(),
headerImage: z.string(),
path: z.string(),
Expand All @@ -79,23 +79,23 @@ const LandscapeSizesSchema = z.object({
});

/** Zod schema for header image variants allowing explicit specification of responsive hero images */
export const HeaderImageVariantsSchema = z.object({
export const HeaderImageVariantsSchema = z.looseObject({
portrait: z
.object({
.looseObject({
avif: PortraitSizesSchema.optional(),
jpg: PortraitSizesSchema.optional(),
})
.optional(),
landscape: z
.object({
.looseObject({
avif: LandscapeSizesSchema.optional(),
jpg: LandscapeSizesSchema.optional(),
})
.optional(),
});

/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */
export const GalleryMetadataSchema = z.object({
export const GalleryMetadataSchema = z.looseObject({
image: z.string().optional(),
imageWidth: z.number().optional(),
imageHeight: z.number().optional(),
Expand All @@ -112,7 +112,7 @@ export const GalleryMetadataSchema = z.object({
});

/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */
export const GalleryDataSchema = z.object({
export const GalleryDataSchema = z.looseObject({
title: z.string(),
description: z.string(),
mediaBasePath: z.string().optional(),
Expand All @@ -122,7 +122,7 @@ export const GalleryDataSchema = z.object({
headerImageVariants: HeaderImageVariantsSchema.optional(),
theme: z.string().optional(),
thumbnails: z
.object({
.looseObject({
size: z.number().optional(),
edge: z.enum(['auto', 'width', 'height']).optional(),
format: z.enum(['avif', 'webp', 'jpeg']).optional(),
Expand All @@ -137,14 +137,14 @@ export const GalleryDataSchema = z.object({
ctaBanner: z.boolean().optional(),
customStyles: z.record(z.string(), z.string()).optional(),
sections: z.array(GallerySectionSchema),
subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),
subGalleries: z.looseObject({ title: z.string(), galleries: z.array(SubGallerySchema) }),
});

/**
* Zod schema for complete gallery data without mediaBasePath.
* @deprecated Use GalleryDataSchema instead which includes mediaBasePath and headerImageVariants.
*/
export const GalleryDataDeprecatedSchema = z.object({
export const GalleryDataDeprecatedSchema = z.looseObject({
title: z.string(),
description: z.string(),
url: z.string().optional(),
Expand All @@ -154,5 +154,5 @@ export const GalleryDataDeprecatedSchema = z.object({
mediaBaseUrl: z.string().optional(),
analyticsScript: z.string().optional(),
sections: z.array(GallerySectionDeprecatedSchema),
subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),
subGalleries: z.looseObject({ title: z.string(), galleries: z.array(SubGallerySchema) }),
});
12 changes: 8 additions & 4 deletions common/src/theme/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ function resolveSubGallery(
};
}

function getHeroVariantSources(variants: unknown): Record<number, string | undefined> | undefined {
return variants && typeof variants === 'object' ? (variants as Record<number, string | undefined>) : undefined;
}

/**
* Resolve hero data with all paths computed and srcsets built.
*/
Expand All @@ -97,7 +101,7 @@ async function resolveHero(gallery: GalleryData): Promise<ResolvedHero> {

const srcsets = {
portraitAvif: buildHeroSrcset(
headerImageVariants?.portrait?.avif,
getHeroVariantSources(headerImageVariants?.portrait?.avif),
PORTRAIT_SIZES,
thumbnailBasePath,
imgBasename,
Expand All @@ -106,7 +110,7 @@ async function resolveHero(gallery: GalleryData): Promise<ResolvedHero> {
useDefaultPaths,
),
portraitJpg: buildHeroSrcset(
headerImageVariants?.portrait?.jpg,
getHeroVariantSources(headerImageVariants?.portrait?.jpg),
PORTRAIT_SIZES,
thumbnailBasePath,
imgBasename,
Expand All @@ -115,7 +119,7 @@ async function resolveHero(gallery: GalleryData): Promise<ResolvedHero> {
useDefaultPaths,
),
landscapeAvif: buildHeroSrcset(
headerImageVariants?.landscape?.avif,
getHeroVariantSources(headerImageVariants?.landscape?.avif),
LANDSCAPE_SIZES,
thumbnailBasePath,
imgBasename,
Expand All @@ -124,7 +128,7 @@ async function resolveHero(gallery: GalleryData): Promise<ResolvedHero> {
useDefaultPaths,
),
landscapeJpg: buildHeroSrcset(
headerImageVariants?.landscape?.jpg,
getHeroVariantSources(headerImageVariants?.landscape?.jpg),
LANDSCAPE_SIZES,
thumbnailBasePath,
imgBasename,
Expand Down
36 changes: 22 additions & 14 deletions docs/commands/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@ If you have created the gallery in a different folder from the photos folder, th

## Options

| Option | Description | Default |
| ----------------------------- | -------------------------------------------------- | --------------------------------------- |
| `-g, --gallery <path>` | Path to gallery directory | Current directory |
| `-r, --recursive` | Build all galleries | `false` |
| `-b, --base-url <url>` | Base URL for external hosting | None |
| `-t, --thumbs-base-url <url>` | Base URL for external hosting of thumbnails | None |
| `--theme <package\|path>` | Theme package name or local path | gallery.json theme or `theme-modern` |
| `--thumbnail-size <pixels>` | Override thumbnail size in pixels | From config hierarchy |
| `--thumbnail-edge <mode>` | Override size mode: auto, width, or height | From config hierarchy |
| `--no-scan` | Do not scan for new photos | `true` |
| `--no-thumbnails` | Skip creating thumbnails | `true` |
| `-v, --verbose` | Show detailed output | |
| `-q, --quiet` | Only show warnings/errors | |
| `-h, --help` | Show command help | |
| Option | Description | Default |
| ----------------------------- | ---------------------------------------------- | ------------------------------------ |
| `-g, --gallery <path>` | Path to gallery directory | Current directory |
| `-r, --recursive` | Build all galleries | `false` |
| `-b, --base-url <url>` | Base URL for external hosting | None |
| `-t, --thumbs-base-url <url>` | Base URL for external hosting of thumbnails | None |
| `--theme <package\|path>` | Theme package name or local path | gallery.json theme or `theme-modern` |
| `--thumbnail-size <pixels>` | Override thumbnail size in pixels | From config hierarchy |
| `--thumbnail-edge <mode>` | Override size mode: auto, width, or height | From config hierarchy |
| `--no-scan` | Do not scan for new photos | `true` |
| `--prune` | Remove missing source files from gallery.json | `false` |
| `--no-thumbnails` | Skip creating thumbnails | `true` |
| `-y, --yes` | Confirm build prompts for non-interactive runs | `false` |
| `-v, --verbose` | Show detailed output | |
| `-q, --quiet` | Only show warnings/errors | |
| `-h, --help` | Show command help | |

## Examples

Expand All @@ -50,6 +52,12 @@ spg build -b https://photos.example.com/ -t https://photos.example.com/thumbnail
# Build without scanning for new photos
spg build --no-scan

# Remove deleted source files while scanning
spg build --prune

# Build non-interactively when photos must be copied into the gallery output
spg build --yes

# Build without creating thumbnails
spg build --no-thumbnails

Expand Down
3 changes: 3 additions & 0 deletions gallery/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"url": "https://github.com/SimplePhotoGallery/core"
},
"homepage": "https://simple.photo",
"engines": {
"node": ">=20.0.0"
},
"files": [
"dist"
],
Expand Down
4 changes: 3 additions & 1 deletion gallery/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const program = new Command();
const telemetryService = new TelemetryService(packageJson.name, packageJson.version, createConsolaUI(program.opts()));

program
.name('gallery')
.name('spg')
.description('Simple Photo Gallery CLI')
.version(packageJson.version)
.option('-v, --verbose', 'Verbose output (debug level)', false)
Expand Down Expand Up @@ -188,6 +188,8 @@ program
.option('-t, --thumbs-base-url <url>', 'Base URL where the thumbnails are hosted')
.option('--no-thumbnails', 'Skip creating thumbnails when building the gallery', true)
.option('--no-scan', 'Do not scan for new photos when building the gallery', true)
.option('--prune', 'Remove gallery entries whose source files are missing when scanning', false)
.option('-y, --yes', 'Answer yes to build confirmations, including copying photos from mediaBasePath', false)
.option(
'--theme <package|path>',
'Theme package name (e.g., @simple-photo-gallery/theme-modern) or local path (e.g., ./themes/my-theme)',
Expand Down
Loading
Loading