simple-photo-gallery 2.0.18 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +367 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +364 -48
- package/dist/index.js.map +1 -1
- package/dist/lib/index.cjs +29 -5
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +8 -4
- package/dist/lib/index.d.ts +8 -4
- package/dist/lib/index.js +29 -5
- package/dist/lib/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var process4 = require('process');
|
|
5
5
|
var commander = require('commander');
|
|
6
6
|
var consola = require('consola');
|
|
7
7
|
var child_process = require('child_process');
|
|
@@ -11,16 +11,19 @@ var buffer = require('buffer');
|
|
|
11
11
|
var sharp2 = require('sharp');
|
|
12
12
|
var blurhash = require('blurhash');
|
|
13
13
|
var common = require('@simple-photo-gallery/common');
|
|
14
|
+
var theme = require('@simple-photo-gallery/common/theme');
|
|
14
15
|
var ExifReader = require('exifreader');
|
|
15
16
|
var ffprobe = require('node-ffprobe');
|
|
17
|
+
var url = require('url');
|
|
16
18
|
var os = require('os');
|
|
17
19
|
var Conf = require('conf');
|
|
18
20
|
var axios = require('axios');
|
|
19
21
|
var semverParser = require('semver-parser');
|
|
20
22
|
|
|
23
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
21
24
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
25
|
|
|
23
|
-
var
|
|
26
|
+
var process4__default = /*#__PURE__*/_interopDefault(process4);
|
|
24
27
|
var fs8__default = /*#__PURE__*/_interopDefault(fs8);
|
|
25
28
|
var path7__default = /*#__PURE__*/_interopDefault(path7);
|
|
26
29
|
var sharp2__default = /*#__PURE__*/_interopDefault(sharp2);
|
|
@@ -31,7 +34,6 @@ var Conf__default = /*#__PURE__*/_interopDefault(Conf);
|
|
|
31
34
|
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
32
35
|
|
|
33
36
|
// src/config/index.ts
|
|
34
|
-
var DEFAULT_THUMBNAIL_SIZE = 300;
|
|
35
37
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".tif", ".svg", ".avif"]);
|
|
36
38
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm", ".mkv", ".m4v", ".3gp"]);
|
|
37
39
|
var HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];
|
|
@@ -60,7 +62,7 @@ async function cropAndResizeImage(image, outputPath, width, height, format = "av
|
|
|
60
62
|
withoutEnlargement: true
|
|
61
63
|
}).toFormat(format).toFile(outputPath);
|
|
62
64
|
}
|
|
63
|
-
async function createImageThumbnails(image, metadata, outputPath, outputPathRetina, size) {
|
|
65
|
+
async function createImageThumbnails(image, metadata, outputPath, outputPathRetina, size, sizeDimension = "auto") {
|
|
64
66
|
const originalWidth = metadata.width || 0;
|
|
65
67
|
const originalHeight = metadata.height || 0;
|
|
66
68
|
if (originalWidth === 0 || originalHeight === 0) {
|
|
@@ -69,12 +71,20 @@ async function createImageThumbnails(image, metadata, outputPath, outputPathReti
|
|
|
69
71
|
const aspectRatio = originalWidth / originalHeight;
|
|
70
72
|
let width;
|
|
71
73
|
let height;
|
|
72
|
-
if (
|
|
74
|
+
if (sizeDimension === "width") {
|
|
73
75
|
width = size;
|
|
74
76
|
height = Math.round(size / aspectRatio);
|
|
75
|
-
} else {
|
|
77
|
+
} else if (sizeDimension === "height") {
|
|
76
78
|
width = Math.round(size * aspectRatio);
|
|
77
79
|
height = size;
|
|
80
|
+
} else {
|
|
81
|
+
if (originalWidth > originalHeight) {
|
|
82
|
+
width = size;
|
|
83
|
+
height = Math.round(size / aspectRatio);
|
|
84
|
+
} else {
|
|
85
|
+
width = Math.round(size * aspectRatio);
|
|
86
|
+
height = size;
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
89
|
await resizeImage(image, outputPath, width, height);
|
|
80
90
|
await resizeImage(image, outputPathRetina, width * 2, height * 2);
|
|
@@ -340,11 +350,15 @@ function migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui) {
|
|
|
340
350
|
filename: path7__default.default.basename(image.path)
|
|
341
351
|
}))
|
|
342
352
|
}));
|
|
353
|
+
const thumbnails2 = deprecatedGalleryData.thumbnailSize === void 0 ? void 0 : { size: deprecatedGalleryData.thumbnailSize };
|
|
343
354
|
const galleryData = {
|
|
344
355
|
...deprecatedGalleryData,
|
|
356
|
+
thumbnailSize: void 0,
|
|
357
|
+
// Remove old field
|
|
345
358
|
headerImage: path7__default.default.basename(deprecatedGalleryData.headerImage),
|
|
346
359
|
sections,
|
|
347
|
-
mediaBasePath
|
|
360
|
+
mediaBasePath,
|
|
361
|
+
thumbnails: thumbnails2
|
|
348
362
|
};
|
|
349
363
|
ui.debug("Backing up old gallery.json file");
|
|
350
364
|
fs8__default.default.copyFileSync(galleryJsonPath, `${galleryJsonPath}.old`);
|
|
@@ -415,9 +429,18 @@ async function getGallerySettingsFromUser(galleryName, defaultImage, ui) {
|
|
|
415
429
|
default: defaultImage,
|
|
416
430
|
placeholder: defaultImage
|
|
417
431
|
});
|
|
418
|
-
return {
|
|
432
|
+
return {
|
|
433
|
+
title,
|
|
434
|
+
description,
|
|
435
|
+
url,
|
|
436
|
+
headerImage,
|
|
437
|
+
thumbnails: {
|
|
438
|
+
size: "",
|
|
439
|
+
edge: void 0
|
|
440
|
+
}
|
|
441
|
+
};
|
|
419
442
|
}
|
|
420
|
-
async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ctaBanner, ui) {
|
|
443
|
+
async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ctaBanner, configOptions, ui) {
|
|
421
444
|
const galleryDir = path7__default.default.dirname(galleryJsonPath);
|
|
422
445
|
const isSameLocation = path7__default.default.relative(scanPath, path7__default.default.join(galleryDir, "..")) === "";
|
|
423
446
|
const mediaBasePath = isSameLocation ? void 0 : scanPath;
|
|
@@ -425,12 +448,23 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
|
|
|
425
448
|
...subGallery,
|
|
426
449
|
headerImage: subGallery.headerImage ? path7__default.default.relative(galleryDir, subGallery.headerImage) : ""
|
|
427
450
|
}));
|
|
451
|
+
const thumbnailsConfig = {};
|
|
452
|
+
if (configOptions.thumbnailSize !== void 0) {
|
|
453
|
+
thumbnailsConfig.size = configOptions.thumbnailSize;
|
|
454
|
+
}
|
|
455
|
+
if (configOptions.thumbnailEdge !== void 0) {
|
|
456
|
+
thumbnailsConfig.edge = configOptions.thumbnailEdge;
|
|
457
|
+
}
|
|
428
458
|
let galleryData = {
|
|
429
459
|
title: "My Gallery",
|
|
430
460
|
description: "My gallery with fantastic photos.",
|
|
431
461
|
headerImage: mediaFiles[0]?.filename || "",
|
|
432
462
|
mediaBasePath,
|
|
433
463
|
metadata: {},
|
|
464
|
+
// Include theme if provided via CLI
|
|
465
|
+
...configOptions.theme && { theme: configOptions.theme },
|
|
466
|
+
// Include thumbnails if any values were set via CLI
|
|
467
|
+
...Object.keys(thumbnailsConfig).length > 0 && { thumbnails: thumbnailsConfig },
|
|
434
468
|
sections: [
|
|
435
469
|
{
|
|
436
470
|
images: mediaFiles
|
|
@@ -443,13 +477,15 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
|
|
|
443
477
|
...ctaBanner !== void 0 && { ctaBanner }
|
|
444
478
|
};
|
|
445
479
|
if (!useDefaultSettings) {
|
|
480
|
+
const userSettings = await getGallerySettingsFromUser(
|
|
481
|
+
path7__default.default.basename(path7__default.default.join(galleryDir, "..")),
|
|
482
|
+
path7__default.default.basename(mediaFiles[0]?.filename || ""),
|
|
483
|
+
ui
|
|
484
|
+
);
|
|
485
|
+
const { thumbnails: thumbnails2, ...otherSettings } = userSettings;
|
|
446
486
|
galleryData = {
|
|
447
487
|
...galleryData,
|
|
448
|
-
...
|
|
449
|
-
path7__default.default.basename(path7__default.default.join(galleryDir, "..")),
|
|
450
|
-
path7__default.default.basename(mediaFiles[0]?.filename || ""),
|
|
451
|
-
ui
|
|
452
|
-
)
|
|
488
|
+
...otherSettings
|
|
453
489
|
};
|
|
454
490
|
}
|
|
455
491
|
await fs8.promises.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
@@ -464,7 +500,7 @@ async function galleryExists(outputPath) {
|
|
|
464
500
|
return false;
|
|
465
501
|
}
|
|
466
502
|
}
|
|
467
|
-
async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ctaBanner, ui) {
|
|
503
|
+
async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ctaBanner, configOptions, ui) {
|
|
468
504
|
ui.start(`Scanning ${scanPath}`);
|
|
469
505
|
let totalFiles = 0;
|
|
470
506
|
let totalGalleries = 1;
|
|
@@ -480,6 +516,7 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
|
|
|
480
516
|
useDefaultSettings,
|
|
481
517
|
force,
|
|
482
518
|
ctaBanner,
|
|
519
|
+
configOptions,
|
|
483
520
|
ui
|
|
484
521
|
);
|
|
485
522
|
totalFiles += result2.totalFiles;
|
|
@@ -505,7 +542,16 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
|
|
|
505
542
|
}
|
|
506
543
|
try {
|
|
507
544
|
await fs8.promises.mkdir(galleryPath, { recursive: true });
|
|
508
|
-
await createGalleryJson(
|
|
545
|
+
await createGalleryJson(
|
|
546
|
+
mediaFiles,
|
|
547
|
+
galleryJsonPath,
|
|
548
|
+
scanPath,
|
|
549
|
+
subGalleries,
|
|
550
|
+
useDefaultSettings,
|
|
551
|
+
ctaBanner,
|
|
552
|
+
configOptions,
|
|
553
|
+
ui
|
|
554
|
+
);
|
|
509
555
|
ui.success(
|
|
510
556
|
`Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`
|
|
511
557
|
);
|
|
@@ -529,6 +575,11 @@ async function init(options, ui) {
|
|
|
529
575
|
try {
|
|
530
576
|
const scanPath = path7__default.default.resolve(options.photos);
|
|
531
577
|
const outputPath = options.gallery ? path7__default.default.resolve(options.gallery) : scanPath;
|
|
578
|
+
const configOptions = {
|
|
579
|
+
theme: options.theme,
|
|
580
|
+
thumbnailSize: options.thumbnailSize,
|
|
581
|
+
thumbnailEdge: options.thumbnailEdge
|
|
582
|
+
};
|
|
532
583
|
const result = await processDirectory(
|
|
533
584
|
scanPath,
|
|
534
585
|
outputPath,
|
|
@@ -536,6 +587,7 @@ async function init(options, ui) {
|
|
|
536
587
|
options.default,
|
|
537
588
|
options.force,
|
|
538
589
|
options.ctaBanner,
|
|
590
|
+
configOptions,
|
|
539
591
|
ui
|
|
540
592
|
);
|
|
541
593
|
ui.box(
|
|
@@ -585,9 +637,25 @@ async function getVideoDimensions(filePath) {
|
|
|
585
637
|
}
|
|
586
638
|
return dimensions;
|
|
587
639
|
}
|
|
588
|
-
async function createVideoThumbnails(inputPath, videoDimensions, outputPath, outputPathRetina,
|
|
640
|
+
async function createVideoThumbnails(inputPath, videoDimensions, outputPath, outputPathRetina, size, sizeDimension = "auto", verbose = false) {
|
|
589
641
|
const aspectRatio = videoDimensions.width / videoDimensions.height;
|
|
590
|
-
|
|
642
|
+
let width;
|
|
643
|
+
let height;
|
|
644
|
+
if (sizeDimension === "width") {
|
|
645
|
+
width = size;
|
|
646
|
+
height = Math.round(size / aspectRatio);
|
|
647
|
+
} else if (sizeDimension === "height") {
|
|
648
|
+
width = Math.round(size * aspectRatio);
|
|
649
|
+
height = size;
|
|
650
|
+
} else {
|
|
651
|
+
if (videoDimensions.width > videoDimensions.height) {
|
|
652
|
+
width = size;
|
|
653
|
+
height = Math.round(size / aspectRatio);
|
|
654
|
+
} else {
|
|
655
|
+
width = Math.round(size * aspectRatio);
|
|
656
|
+
height = size;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
591
659
|
const tempFramePath = `${outputPath}.temp.png`;
|
|
592
660
|
return new Promise((resolve, reject) => {
|
|
593
661
|
const ffmpeg = child_process.spawn("ffmpeg", [
|
|
@@ -628,7 +696,7 @@ async function createVideoThumbnails(inputPath, videoDimensions, outputPath, out
|
|
|
628
696
|
}
|
|
629
697
|
|
|
630
698
|
// src/modules/thumbnails/index.ts
|
|
631
|
-
async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, lastMediaTimestamp) {
|
|
699
|
+
async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, thumbnailSizeDimension = "auto", lastMediaTimestamp) {
|
|
632
700
|
const fileMtime = await getFileMtime(imagePath);
|
|
633
701
|
if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs8__default.default.existsSync(thumbnailPath)) {
|
|
634
702
|
return void 0;
|
|
@@ -647,7 +715,8 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
647
715
|
metadata,
|
|
648
716
|
thumbnailPath,
|
|
649
717
|
thumbnailPathRetina,
|
|
650
|
-
thumbnailSize
|
|
718
|
+
thumbnailSize,
|
|
719
|
+
thumbnailSizeDimension
|
|
651
720
|
);
|
|
652
721
|
const blurHash = await generateBlurHash(thumbnailPath);
|
|
653
722
|
return {
|
|
@@ -666,7 +735,7 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
666
735
|
lastMediaTimestamp: fileMtime.toISOString()
|
|
667
736
|
};
|
|
668
737
|
}
|
|
669
|
-
async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumbnailSize, verbose, lastMediaTimestamp) {
|
|
738
|
+
async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumbnailSize, thumbnailSizeDimension = "auto", verbose, lastMediaTimestamp) {
|
|
670
739
|
const fileMtime = await getFileMtime(videoPath);
|
|
671
740
|
if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs8__default.default.existsSync(thumbnailPath)) {
|
|
672
741
|
return void 0;
|
|
@@ -678,6 +747,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
678
747
|
thumbnailPath,
|
|
679
748
|
thumbnailPathRetina,
|
|
680
749
|
thumbnailSize,
|
|
750
|
+
thumbnailSizeDimension,
|
|
681
751
|
verbose
|
|
682
752
|
);
|
|
683
753
|
const blurHash = await generateBlurHash(thumbnailPath);
|
|
@@ -697,7 +767,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
697
767
|
lastMediaTimestamp: fileMtime.toISOString()
|
|
698
768
|
};
|
|
699
769
|
}
|
|
700
|
-
async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath,
|
|
770
|
+
async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailConfig, ui) {
|
|
701
771
|
try {
|
|
702
772
|
const filePath = path7__default.default.resolve(path7__default.default.join(mediaBasePath, mediaFile.filename));
|
|
703
773
|
const fileName = mediaFile.filename;
|
|
@@ -708,7 +778,22 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
|
|
|
708
778
|
const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : void 0;
|
|
709
779
|
const verbose = ui.level === consola.LogLevels.debug;
|
|
710
780
|
ui.debug(` Processing ${mediaFile.type}: ${fileName}`);
|
|
711
|
-
const updatedMediaFile = await (mediaFile.type === "image" ? processImage(
|
|
781
|
+
const updatedMediaFile = await (mediaFile.type === "image" ? processImage(
|
|
782
|
+
filePath,
|
|
783
|
+
thumbnailPath,
|
|
784
|
+
thumbnailPathRetina,
|
|
785
|
+
thumbnailConfig.size,
|
|
786
|
+
thumbnailConfig.edge,
|
|
787
|
+
lastMediaTimestamp
|
|
788
|
+
) : processVideo(
|
|
789
|
+
filePath,
|
|
790
|
+
thumbnailPath,
|
|
791
|
+
thumbnailPathRetina,
|
|
792
|
+
thumbnailConfig.size,
|
|
793
|
+
thumbnailConfig.edge,
|
|
794
|
+
verbose,
|
|
795
|
+
lastMediaTimestamp
|
|
796
|
+
));
|
|
712
797
|
if (!updatedMediaFile) {
|
|
713
798
|
ui.debug(` Skipping ${fileName} because it has already been processed`);
|
|
714
799
|
if (mediaFile.thumbnail && !mediaFile.thumbnail.blurHash && fs8__default.default.existsSync(thumbnailPath)) {
|
|
@@ -744,19 +829,30 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
|
|
|
744
829
|
return { ...mediaFile, thumbnail: void 0 };
|
|
745
830
|
}
|
|
746
831
|
}
|
|
747
|
-
async function processGalleryThumbnails(galleryDir, ui) {
|
|
832
|
+
async function processGalleryThumbnails(galleryDir, ui, cliThumbnailConfig) {
|
|
748
833
|
const galleryJsonPath = path7__default.default.join(galleryDir, "gallery", "gallery.json");
|
|
749
834
|
const thumbnailsPath = path7__default.default.join(galleryDir, "gallery", "images");
|
|
750
835
|
ui.start(`Creating thumbnails: ${galleryDir}`);
|
|
751
836
|
try {
|
|
752
837
|
fs8__default.default.mkdirSync(thumbnailsPath, { recursive: true });
|
|
753
838
|
const galleryData = parseGalleryJson(galleryJsonPath, ui);
|
|
754
|
-
const
|
|
839
|
+
const galleryThumbnailConfig = theme.extractThumbnailConfigFromGallery(galleryData);
|
|
840
|
+
let themeConfig;
|
|
841
|
+
if (galleryData.theme) {
|
|
842
|
+
try {
|
|
843
|
+
const themeDir = await resolveThemeDir(galleryData.theme, ui);
|
|
844
|
+
themeConfig = theme.loadThemeConfig(themeDir);
|
|
845
|
+
} catch {
|
|
846
|
+
ui.debug(`Could not load theme config from ${galleryData.theme}, using defaults`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const thumbnailConfig = theme.mergeThumbnailConfig(cliThumbnailConfig, galleryThumbnailConfig, themeConfig);
|
|
850
|
+
ui.debug(`Thumbnail config: size=${thumbnailConfig.size}, edge=${thumbnailConfig.edge}`);
|
|
755
851
|
const mediaBasePath = galleryData.mediaBasePath ?? path7__default.default.join(galleryDir);
|
|
756
852
|
let processedCount = 0;
|
|
757
853
|
for (const section of galleryData.sections) {
|
|
758
854
|
for (const [index, mediaFile] of section.images.entries()) {
|
|
759
|
-
section.images[index] = await processMediaFile(mediaFile, mediaBasePath, thumbnailsPath,
|
|
855
|
+
section.images[index] = await processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailConfig, ui);
|
|
760
856
|
}
|
|
761
857
|
processedCount += section.images.length;
|
|
762
858
|
}
|
|
@@ -775,10 +871,11 @@ async function thumbnails(options, ui) {
|
|
|
775
871
|
ui.error("No galleries found.");
|
|
776
872
|
return { processedGalleryCount: 0, processedMediaCount: 0 };
|
|
777
873
|
}
|
|
874
|
+
const cliThumbnailConfig = options.thumbnailSize !== void 0 || options.thumbnailEdge !== void 0 ? { size: options.thumbnailSize, edge: options.thumbnailEdge } : void 0;
|
|
778
875
|
let totalGalleries = 0;
|
|
779
876
|
let totalProcessed = 0;
|
|
780
877
|
for (const galleryDir of galleryDirs) {
|
|
781
|
-
const processed = await processGalleryThumbnails(galleryDir, ui);
|
|
878
|
+
const processed = await processGalleryThumbnails(galleryDir, ui, cliThumbnailConfig);
|
|
782
879
|
if (processed > 0) {
|
|
783
880
|
++totalGalleries;
|
|
784
881
|
totalProcessed += processed;
|
|
@@ -837,7 +934,7 @@ async function scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, u
|
|
|
837
934
|
}
|
|
838
935
|
return galleryData;
|
|
839
936
|
}
|
|
840
|
-
async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnails, ui, baseUrl, thumbsBaseUrl) {
|
|
937
|
+
async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnails, ui, baseUrl, thumbsBaseUrl, cliThumbnailConfig, cliTheme) {
|
|
841
938
|
ui.start(`Building gallery ${galleryDir}`);
|
|
842
939
|
const galleryJsonPath = path7__default.default.join(galleryDir, "gallery", "gallery.json");
|
|
843
940
|
let galleryData = parseGalleryJson(galleryJsonPath, ui);
|
|
@@ -890,18 +987,35 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
|
|
|
890
987
|
galleryData.thumbsBaseUrl = thumbsBaseUrl;
|
|
891
988
|
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
892
989
|
}
|
|
990
|
+
if (cliTheme && galleryData.theme !== cliTheme) {
|
|
991
|
+
ui.debug("Updating gallery.json with theme");
|
|
992
|
+
galleryData.theme = cliTheme;
|
|
993
|
+
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
994
|
+
}
|
|
995
|
+
if (cliThumbnailConfig) {
|
|
996
|
+
const needsUpdate = cliThumbnailConfig.size !== void 0 && galleryData.thumbnails?.size !== cliThumbnailConfig.size || cliThumbnailConfig.edge !== void 0 && galleryData.thumbnails?.edge !== cliThumbnailConfig.edge;
|
|
997
|
+
if (needsUpdate) {
|
|
998
|
+
ui.debug("Updating gallery.json with thumbnail settings");
|
|
999
|
+
galleryData.thumbnails = {
|
|
1000
|
+
...galleryData.thumbnails,
|
|
1001
|
+
...cliThumbnailConfig.size !== void 0 && { size: cliThumbnailConfig.size },
|
|
1002
|
+
...cliThumbnailConfig.edge !== void 0 && { edge: cliThumbnailConfig.edge }
|
|
1003
|
+
};
|
|
1004
|
+
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
893
1007
|
if (!galleryData.metadata.image) {
|
|
894
1008
|
ui.debug("Updating gallery.json with social media card URL");
|
|
895
1009
|
galleryData.metadata.image = thumbsBaseUrl ? `${thumbsBaseUrl}/${path7__default.default.basename(socialMediaCardImagePath)}` : `${galleryData.url || ""}/${path7__default.default.relative(galleryDir, socialMediaCardImagePath)}`;
|
|
896
1010
|
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
897
1011
|
}
|
|
898
1012
|
if (shouldCreateThumbnails) {
|
|
899
|
-
await processGalleryThumbnails(galleryDir, ui);
|
|
1013
|
+
await processGalleryThumbnails(galleryDir, ui, cliThumbnailConfig);
|
|
900
1014
|
}
|
|
901
1015
|
ui.debug("Building gallery from template");
|
|
902
1016
|
try {
|
|
903
|
-
|
|
904
|
-
|
|
1017
|
+
process4__default.default.env.GALLERY_JSON_PATH = galleryJsonPath;
|
|
1018
|
+
process4__default.default.env.GALLERY_OUTPUT_DIR = path7__default.default.join(galleryDir, "gallery");
|
|
905
1019
|
child_process.execSync("npx astro build", { cwd: templateDir, stdio: ui.level === consola.LogLevels.debug ? "inherit" : "ignore" });
|
|
906
1020
|
} catch (error) {
|
|
907
1021
|
ui.error(`Build failed for ${galleryDir}`);
|
|
@@ -918,6 +1032,25 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
|
|
|
918
1032
|
fs8__default.default.rmSync(buildDir, { recursive: true, force: true });
|
|
919
1033
|
ui.success(`Gallery built successfully`);
|
|
920
1034
|
}
|
|
1035
|
+
function isLocalThemePath(theme) {
|
|
1036
|
+
return theme.startsWith("./") || theme.startsWith("../") || theme.startsWith("/");
|
|
1037
|
+
}
|
|
1038
|
+
async function resolveThemeDir(theme, ui) {
|
|
1039
|
+
if (isLocalThemePath(theme)) {
|
|
1040
|
+
const themeDir = path7__default.default.resolve(theme);
|
|
1041
|
+
const packageJsonPath = path7__default.default.join(themeDir, "package.json");
|
|
1042
|
+
if (!fs8__default.default.existsSync(packageJsonPath)) {
|
|
1043
|
+
throw new Error(`Theme directory not found or invalid: ${themeDir}. package.json not found.`);
|
|
1044
|
+
}
|
|
1045
|
+
ui.debug(`Using local theme: ${themeDir}`);
|
|
1046
|
+
return themeDir;
|
|
1047
|
+
} else {
|
|
1048
|
+
const themePath = await undefined(`${theme}/package.json`);
|
|
1049
|
+
const themeDir = path7__default.default.dirname(new URL(themePath).pathname);
|
|
1050
|
+
ui.debug(`Using npm theme package: ${theme} (${themeDir})`);
|
|
1051
|
+
return themeDir;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
921
1054
|
async function build(options, ui) {
|
|
922
1055
|
try {
|
|
923
1056
|
const galleryDirs = findGalleries(options.gallery, options.recursive);
|
|
@@ -925,20 +1058,41 @@ async function build(options, ui) {
|
|
|
925
1058
|
ui.error("No galleries found.");
|
|
926
1059
|
return { processedGalleryCount: 0 };
|
|
927
1060
|
}
|
|
928
|
-
const
|
|
929
|
-
const themeDir = path7__default.default.dirname(new URL(themePath).pathname);
|
|
1061
|
+
const cliThumbnailConfig = options.thumbnailSize !== void 0 || options.thumbnailEdge !== void 0 ? { size: options.thumbnailSize, edge: options.thumbnailEdge } : void 0;
|
|
930
1062
|
let totalGalleries = 0;
|
|
931
1063
|
for (const dir of galleryDirs) {
|
|
1064
|
+
const galleryJsonPath = path7__default.default.join(dir, "gallery", "gallery.json");
|
|
1065
|
+
const galleryData = parseGalleryJson(galleryJsonPath, ui);
|
|
1066
|
+
const themeIdentifier = options.theme || galleryData.theme || "@simple-photo-gallery/theme-modern";
|
|
1067
|
+
const themeDir = await resolveThemeDir(themeIdentifier, ui);
|
|
932
1068
|
const baseUrl = options.baseUrl ? `${options.baseUrl}${path7__default.default.relative(options.gallery, dir)}` : void 0;
|
|
933
1069
|
const thumbsBaseUrl = options.thumbsBaseUrl ? `${options.thumbsBaseUrl}${path7__default.default.relative(options.gallery, dir)}` : void 0;
|
|
934
|
-
await buildGallery(
|
|
1070
|
+
await buildGallery(
|
|
1071
|
+
path7__default.default.resolve(dir),
|
|
1072
|
+
themeDir,
|
|
1073
|
+
options.scan,
|
|
1074
|
+
options.thumbnails,
|
|
1075
|
+
ui,
|
|
1076
|
+
baseUrl,
|
|
1077
|
+
thumbsBaseUrl,
|
|
1078
|
+
cliThumbnailConfig,
|
|
1079
|
+
options.theme
|
|
1080
|
+
);
|
|
935
1081
|
++totalGalleries;
|
|
936
1082
|
}
|
|
937
1083
|
ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? "gallery" : "galleries"} successfully`);
|
|
938
1084
|
return { processedGalleryCount: totalGalleries };
|
|
939
1085
|
} catch (error) {
|
|
940
|
-
if (error instanceof Error
|
|
941
|
-
|
|
1086
|
+
if (error instanceof Error) {
|
|
1087
|
+
if (error.message.includes("Cannot find package")) {
|
|
1088
|
+
ui.error(
|
|
1089
|
+
`Theme package not found: ${options.theme || "@simple-photo-gallery/theme-modern"}. Make sure it's installed.`
|
|
1090
|
+
);
|
|
1091
|
+
} else if (error.message.includes("Theme directory not found") || error.message.includes("package.json not found")) {
|
|
1092
|
+
ui.error(error.message);
|
|
1093
|
+
} else {
|
|
1094
|
+
ui.error("Error building gallery");
|
|
1095
|
+
}
|
|
942
1096
|
} else {
|
|
943
1097
|
ui.error("Error building gallery");
|
|
944
1098
|
}
|
|
@@ -996,6 +1150,162 @@ async function clean(options, ui) {
|
|
|
996
1150
|
throw error;
|
|
997
1151
|
}
|
|
998
1152
|
}
|
|
1153
|
+
function findMonorepoRoot(startDir) {
|
|
1154
|
+
let dir = path7__default.default.resolve(startDir);
|
|
1155
|
+
while (true) {
|
|
1156
|
+
const pkgPath = path7__default.default.join(dir, "package.json");
|
|
1157
|
+
if (fs8__default.default.existsSync(pkgPath)) {
|
|
1158
|
+
try {
|
|
1159
|
+
const pkg = JSON.parse(fs8__default.default.readFileSync(pkgPath, "utf8"));
|
|
1160
|
+
if (pkg && typeof pkg === "object" && "workspaces" in pkg) {
|
|
1161
|
+
return dir;
|
|
1162
|
+
}
|
|
1163
|
+
} catch {
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const parent = path7__default.default.dirname(dir);
|
|
1167
|
+
if (parent === dir) {
|
|
1168
|
+
return void 0;
|
|
1169
|
+
}
|
|
1170
|
+
dir = parent;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function validateThemeName(name) {
|
|
1174
|
+
if (!name || name.trim().length === 0) {
|
|
1175
|
+
throw new Error("Theme name cannot be empty");
|
|
1176
|
+
}
|
|
1177
|
+
if (!/^[a-z0-9-]+$/i.test(name)) {
|
|
1178
|
+
throw new Error("Theme name can only contain letters, numbers, and hyphens");
|
|
1179
|
+
}
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
async function ensureDirectory(dirPath, ui) {
|
|
1183
|
+
try {
|
|
1184
|
+
await fs8__default.default.promises.mkdir(dirPath, { recursive: true });
|
|
1185
|
+
ui.debug(`Created directory: ${dirPath}`);
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
1188
|
+
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
var EXCLUDE_PATTERNS = ["node_modules", ".astro", "dist", "_build", ".git", "*.log", ".DS_Store"];
|
|
1193
|
+
function shouldExclude(name) {
|
|
1194
|
+
if (name === "README.md" || name === "README_BASE.md") {
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
return EXCLUDE_PATTERNS.some((pattern) => {
|
|
1198
|
+
if (pattern.includes("*")) {
|
|
1199
|
+
const regexPattern = pattern.split("*").join(".*");
|
|
1200
|
+
const regex = new RegExp(regexPattern);
|
|
1201
|
+
return regex.test(name);
|
|
1202
|
+
}
|
|
1203
|
+
return name === pattern;
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
async function copyDirectory(src, dest, ui) {
|
|
1207
|
+
await fs8__default.default.promises.mkdir(dest, { recursive: true });
|
|
1208
|
+
const entries = await fs8__default.default.promises.readdir(src, { withFileTypes: true });
|
|
1209
|
+
for (const entry of entries) {
|
|
1210
|
+
if (shouldExclude(entry.name)) {
|
|
1211
|
+
ui.debug(`Skipping excluded file/directory: ${entry.name}`);
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
const srcPath = path7__default.default.join(src, entry.name);
|
|
1215
|
+
const destPath = path7__default.default.join(dest, entry.name);
|
|
1216
|
+
if (entry.isDirectory()) {
|
|
1217
|
+
await copyDirectory(srcPath, destPath, ui);
|
|
1218
|
+
} else {
|
|
1219
|
+
await fs8__default.default.promises.copyFile(srcPath, destPath);
|
|
1220
|
+
ui.debug(`Copied file: ${destPath}`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function findBaseThemePath() {
|
|
1225
|
+
const moduleDir = path7__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
1226
|
+
const bundledTemplatePath = path7__default.default.resolve(moduleDir, "../../../src/modules/create-theme/templates/base");
|
|
1227
|
+
if (fs8__default.default.existsSync(bundledTemplatePath)) {
|
|
1228
|
+
return bundledTemplatePath;
|
|
1229
|
+
}
|
|
1230
|
+
const monorepoRoot = findMonorepoRoot(process4__default.default.cwd());
|
|
1231
|
+
const workspaceRoot = monorepoRoot ?? process4__default.default.cwd();
|
|
1232
|
+
const workspaceBaseThemePath = path7__default.default.join(workspaceRoot, "themes", "base");
|
|
1233
|
+
if (fs8__default.default.existsSync(workspaceBaseThemePath)) {
|
|
1234
|
+
return workspaceBaseThemePath;
|
|
1235
|
+
}
|
|
1236
|
+
throw new Error(
|
|
1237
|
+
`Base theme template not found. Tried:
|
|
1238
|
+
- ${bundledTemplatePath}
|
|
1239
|
+
- ${workspaceBaseThemePath}
|
|
1240
|
+
|
|
1241
|
+
Please ensure the templates are included in the package or themes/base exists in the workspace.`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
async function updatePackageJson(themeDir, themeName, ui) {
|
|
1245
|
+
const packageJsonPath = path7__default.default.join(themeDir, "package.json");
|
|
1246
|
+
const packageJsonContent = await fs8__default.default.promises.readFile(packageJsonPath, "utf8");
|
|
1247
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
1248
|
+
packageJson.name = themeName;
|
|
1249
|
+
await fs8__default.default.promises.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf8");
|
|
1250
|
+
ui.debug(`Updated package.json with theme name: ${themeName}`);
|
|
1251
|
+
}
|
|
1252
|
+
async function createReadmeFromBase(baseThemePath, themeDir, themeName, ui) {
|
|
1253
|
+
const readmeBasePath = path7__default.default.join(baseThemePath, "README_BASE.md");
|
|
1254
|
+
const readmePath = path7__default.default.join(themeDir, "README.md");
|
|
1255
|
+
if (!fs8__default.default.existsSync(readmeBasePath)) {
|
|
1256
|
+
throw new Error(`README_BASE.md not found in template: ${readmeBasePath}`);
|
|
1257
|
+
}
|
|
1258
|
+
let readme = await fs8__default.default.promises.readFile(readmeBasePath, "utf8");
|
|
1259
|
+
const displayName = themeName.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1260
|
+
readme = readme.replaceAll("{THEME_NAME}", displayName);
|
|
1261
|
+
readme = readme.replaceAll("{THEME_NAME_LOWER}", displayName.toLowerCase());
|
|
1262
|
+
await fs8__default.default.promises.writeFile(readmePath, readme, "utf8");
|
|
1263
|
+
ui.debug(`Created README.md from README_BASE.md for theme: ${themeName}`);
|
|
1264
|
+
}
|
|
1265
|
+
async function createTheme(options, ui) {
|
|
1266
|
+
try {
|
|
1267
|
+
validateThemeName(options.name);
|
|
1268
|
+
let themeDir;
|
|
1269
|
+
if (options.path) {
|
|
1270
|
+
themeDir = path7__default.default.resolve(options.path);
|
|
1271
|
+
} else {
|
|
1272
|
+
const monorepoRoot = findMonorepoRoot(process4__default.default.cwd());
|
|
1273
|
+
const baseDir = monorepoRoot ?? process4__default.default.cwd();
|
|
1274
|
+
const themesBaseDir = path7__default.default.resolve(baseDir, "themes");
|
|
1275
|
+
themeDir = path7__default.default.join(themesBaseDir, options.name);
|
|
1276
|
+
if (!fs8__default.default.existsSync(themesBaseDir)) {
|
|
1277
|
+
await ensureDirectory(themesBaseDir, ui);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (fs8__default.default.existsSync(themeDir)) {
|
|
1281
|
+
throw new Error(`Theme directory already exists: ${themeDir}. Cannot overwrite existing theme.`);
|
|
1282
|
+
}
|
|
1283
|
+
ui.start(`Creating theme: ${options.name}`);
|
|
1284
|
+
const baseThemePath = findBaseThemePath();
|
|
1285
|
+
ui.debug(`Using base theme from: ${baseThemePath}`);
|
|
1286
|
+
ui.debug("Copying base theme files...");
|
|
1287
|
+
await copyDirectory(baseThemePath, themeDir, ui);
|
|
1288
|
+
ui.debug("Updating theme-specific files...");
|
|
1289
|
+
await updatePackageJson(themeDir, options.name, ui);
|
|
1290
|
+
await createReadmeFromBase(baseThemePath, themeDir, options.name, ui);
|
|
1291
|
+
ui.success(`Theme created successfully at: ${themeDir}`);
|
|
1292
|
+
ui.info(`
|
|
1293
|
+
Next steps:`);
|
|
1294
|
+
ui.info(`1. cd ${themeDir}`);
|
|
1295
|
+
ui.info(`2. yarn install`);
|
|
1296
|
+
ui.info(`3. Customize your theme in src/pages/index.astro`);
|
|
1297
|
+
ui.info(`4. Initialize a gallery (run from directory with your images): spg init -p <images-folder>`);
|
|
1298
|
+
ui.info(`5. Build a gallery with your theme: spg build --theme ${themeDir} -g <gallery-folder>`);
|
|
1299
|
+
return { processedGalleryCount: 0 };
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
if (error instanceof Error) {
|
|
1302
|
+
ui.error(error.message);
|
|
1303
|
+
} else {
|
|
1304
|
+
ui.error("Failed to create theme");
|
|
1305
|
+
}
|
|
1306
|
+
throw error;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
999
1309
|
|
|
1000
1310
|
// src/modules/telemetry/index.ts
|
|
1001
1311
|
async function telemetry(options, ui, telemetryService2) {
|
|
@@ -1025,7 +1335,7 @@ var ApiTelemetryClient = class {
|
|
|
1025
1335
|
axios__default.default.post(this.endpoint, event, {
|
|
1026
1336
|
headers: {
|
|
1027
1337
|
"content-type": "application/json",
|
|
1028
|
-
"user-agent": `simple-photo-gallery/${event.packageVersion} (${
|
|
1338
|
+
"user-agent": `simple-photo-gallery/${event.packageVersion} (${process4__default.default.platform}; ${process4__default.default.arch})`
|
|
1029
1339
|
}
|
|
1030
1340
|
});
|
|
1031
1341
|
} catch {
|
|
@@ -1035,7 +1345,7 @@ var ApiTelemetryClient = class {
|
|
|
1035
1345
|
var ConsoleTelemetryClient = class {
|
|
1036
1346
|
async record(event) {
|
|
1037
1347
|
const serialized = JSON.stringify(event, null, 2);
|
|
1038
|
-
|
|
1348
|
+
process4.stdout.write(`TELEMETRY EVENT: ${serialized}
|
|
1039
1349
|
`);
|
|
1040
1350
|
}
|
|
1041
1351
|
};
|
|
@@ -1062,11 +1372,11 @@ var TelemetryService = class {
|
|
|
1062
1372
|
if (override) {
|
|
1063
1373
|
return override === "1";
|
|
1064
1374
|
}
|
|
1065
|
-
if (
|
|
1375
|
+
if (process4__default.default.env.CI || process4__default.default.env.DO_NOT_TRACK) {
|
|
1066
1376
|
return false;
|
|
1067
1377
|
}
|
|
1068
|
-
if (
|
|
1069
|
-
return
|
|
1378
|
+
if (process4__default.default.env.SPG_TELEMETRY) {
|
|
1379
|
+
return process4__default.default.env.SPG_TELEMETRY === "1";
|
|
1070
1380
|
}
|
|
1071
1381
|
const stored = this.getStoredPreference();
|
|
1072
1382
|
if (stored === void 0) {
|
|
@@ -1108,7 +1418,7 @@ var TelemetryService = class {
|
|
|
1108
1418
|
durationMs: now - startedAt,
|
|
1109
1419
|
packageName: this.packageName,
|
|
1110
1420
|
packageVersion: this.packageVersion,
|
|
1111
|
-
nodeVersion:
|
|
1421
|
+
nodeVersion: process4__default.default.version,
|
|
1112
1422
|
osPlatform: os__default.default.platform(),
|
|
1113
1423
|
osRelease: os__default.default.release(),
|
|
1114
1424
|
osArch: os__default.default.arch(),
|
|
@@ -1142,7 +1452,7 @@ var TelemetryService = class {
|
|
|
1142
1452
|
/** Returns the telemetry client. */
|
|
1143
1453
|
getClient() {
|
|
1144
1454
|
if (!this.client) {
|
|
1145
|
-
switch (
|
|
1455
|
+
switch (process4__default.default.env.SPG_TELEMETRY_PROVIDER) {
|
|
1146
1456
|
case "none": {
|
|
1147
1457
|
this.client = void 0;
|
|
1148
1458
|
break;
|
|
@@ -1234,7 +1544,7 @@ async function waitForUpdateCheck(checkPromise) {
|
|
|
1234
1544
|
// package.json
|
|
1235
1545
|
var package_default = {
|
|
1236
1546
|
name: "simple-photo-gallery",
|
|
1237
|
-
version: "2.0
|
|
1547
|
+
version: "2.1.0"};
|
|
1238
1548
|
|
|
1239
1549
|
// src/index.ts
|
|
1240
1550
|
var program = new commander.Command();
|
|
@@ -1276,7 +1586,7 @@ function withCommandContext(handler) {
|
|
|
1276
1586
|
} catch (error) {
|
|
1277
1587
|
ui.debug(error);
|
|
1278
1588
|
errorInfo = error instanceof Error ? { name: error.name, message: error.message } : { name: "UnknownError", message: String(error) };
|
|
1279
|
-
|
|
1589
|
+
process4__default.default.exitCode = 1;
|
|
1280
1590
|
}
|
|
1281
1591
|
const updateInfo = await waitForUpdateCheck(updateCheckPromise);
|
|
1282
1592
|
if (updateInfo) {
|
|
@@ -1298,14 +1608,21 @@ function withCommandContext(handler) {
|
|
|
1298
1608
|
program.command("init").description("Initialize a gallery by scaning a folder for images and videos").option(
|
|
1299
1609
|
"-p, --photos <path>",
|
|
1300
1610
|
"Path to the folder where the photos are stored. Default: current working directory",
|
|
1301
|
-
|
|
1611
|
+
process4__default.default.cwd()
|
|
1302
1612
|
).option(
|
|
1303
1613
|
"-g, --gallery <path>",
|
|
1304
1614
|
"Path to the directory where the gallery will be initialized. Default: same directory as the photos folder"
|
|
1305
|
-
).option("-r, --recursive", "Recursively create galleries from all photos subdirectories", false).option("-d, --default", "Use default gallery settings instead of asking the user", false).option("-f, --force", "Force override existing galleries without asking", false).option("--cta-banner", "Add a Simple Photo Gallery call-to-action banner to the end of the gallery", false).action(withCommandContext((options, ui) => init(options, ui)));
|
|
1306
|
-
program.command("thumbnails").description("Create thumbnails for all media files in the gallery").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory",
|
|
1307
|
-
program.command("build").description("Build the HTML gallery in the specified directory").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory",
|
|
1308
|
-
|
|
1615
|
+
).option("-r, --recursive", "Recursively create galleries from all photos subdirectories", false).option("-d, --default", "Use default gallery settings instead of asking the user", false).option("-f, --force", "Force override existing galleries without asking", false).option("--cta-banner", "Add a Simple Photo Gallery call-to-action banner to the end of the gallery", false).option("--theme <package|path>", "Theme package name or local path to store in gallery.json").option("--thumbnail-size <pixels>", "Thumbnail size in pixels to store in gallery.json", Number.parseInt).option("--thumbnail-edge <mode>", "How thumbnail size is applied: auto, width, or height").action(withCommandContext((options, ui) => init(options, ui)));
|
|
1616
|
+
program.command("thumbnails").description("Create thumbnails for all media files in the gallery").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process4__default.default.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).option("--thumbnail-size <pixels>", "Override thumbnail size in pixels", Number.parseInt).option("--thumbnail-edge <mode>", "Override how thumbnail size is applied: auto, width, or height").action(withCommandContext((options, ui) => thumbnails(options, ui)));
|
|
1617
|
+
program.command("build").description("Build the HTML gallery in the specified directory").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process4__default.default.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).option("-b, --base-url <url>", "Base URL where the photos are hosted").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(
|
|
1618
|
+
"--theme <package|path>",
|
|
1619
|
+
"Theme package name (e.g., @simple-photo-gallery/theme-modern) or local path (e.g., ./themes/my-theme)"
|
|
1620
|
+
).option("--thumbnail-size <pixels>", "Override thumbnail size in pixels", Number.parseInt).option("--thumbnail-edge <mode>", "Override how thumbnail size is applied: auto, width, or height").action(withCommandContext((options, ui) => build(options, ui)));
|
|
1621
|
+
program.command("clean").description("Remove all gallery files and folders (index.html, gallery/)").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process4__default.default.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withCommandContext((options, ui) => clean(options, ui)));
|
|
1622
|
+
program.command("create-theme").description("Create a new theme template").argument("<name>", "Name of the theme to create").option("-p, --path <path>", "Path where the theme should be created. Default: ./themes/<name>").action(async (name, options, command) => {
|
|
1623
|
+
const handler = withCommandContext((opts, ui) => createTheme({ name, path: opts.path }, ui));
|
|
1624
|
+
await handler(options, command);
|
|
1625
|
+
});
|
|
1309
1626
|
program.command("telemetry").description("Manage anonymous telemetry preferences. Use 1 to enable, 0 to disable, or no argument to check status").option("-s, --state <state>", "Enable (1) or disable (0) telemetry", parseTelemetryOption).action(withCommandContext((options, ui) => telemetry(options, ui, telemetryService)));
|
|
1310
1627
|
program.parse();
|
|
1311
1628
|
//# sourceMappingURL=index.cjs.map
|