@canopy-iiif/app 0.10.2 → 0.10.3
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/lib/build/iiif.js +242 -161
- package/lib/build/mdx.js +11 -4
- package/lib/build/pages.js +82 -7
- package/lib/common.js +3 -1
- package/lib/head.js +91 -1
- package/lib/iiif/thumbnail.js +13 -0
- package/lib/index.js +2 -1
- package/package.json +1 -1
- package/ui/tailwind-config.js +5 -1
package/lib/build/iiif.js
CHANGED
|
@@ -15,10 +15,13 @@ const {
|
|
|
15
15
|
} = require("../common");
|
|
16
16
|
const mdx = require("./mdx");
|
|
17
17
|
const {log, logLine, logResponse} = require("./log");
|
|
18
|
+
const { getPageContext } = require("../page-context");
|
|
19
|
+
const PageContext = getPageContext();
|
|
18
20
|
const {
|
|
19
21
|
getThumbnail,
|
|
20
22
|
getRepresentativeImage,
|
|
21
23
|
buildIiifImageUrlFromService,
|
|
24
|
+
buildIiifImageUrlForDimensions,
|
|
22
25
|
findPrimaryCanvasImage,
|
|
23
26
|
buildIiifImageSrcset,
|
|
24
27
|
} = require("../iiif/thumbnail");
|
|
@@ -44,6 +47,9 @@ const DEFAULT_CHUNK_SIZE = 20;
|
|
|
44
47
|
const DEFAULT_FETCH_CONCURRENCY = 5;
|
|
45
48
|
const HERO_THUMBNAIL_SIZE = 800;
|
|
46
49
|
const HERO_IMAGE_SIZES_ATTR = "(min-width: 1024px) 1280px, 100vw";
|
|
50
|
+
const OG_IMAGE_WIDTH = 1200;
|
|
51
|
+
const OG_IMAGE_HEIGHT = 630;
|
|
52
|
+
const HERO_REPRESENTATIVE_SIZE = Math.max(HERO_THUMBNAIL_SIZE, OG_IMAGE_WIDTH);
|
|
47
53
|
|
|
48
54
|
function resolvePositiveInteger(value, fallback) {
|
|
49
55
|
const num = Number(value);
|
|
@@ -69,6 +75,73 @@ function resolveThumbnailPreferences() {
|
|
|
69
75
|
};
|
|
70
76
|
}
|
|
71
77
|
|
|
78
|
+
async function resolveHeroMedia(manifest) {
|
|
79
|
+
if (!manifest) return null;
|
|
80
|
+
try {
|
|
81
|
+
const heroSource = (() => {
|
|
82
|
+
if (manifest && manifest.thumbnail) {
|
|
83
|
+
const clone = { ...manifest };
|
|
84
|
+
try {
|
|
85
|
+
delete clone.thumbnail;
|
|
86
|
+
} catch (_) {
|
|
87
|
+
clone.thumbnail = undefined;
|
|
88
|
+
}
|
|
89
|
+
return clone;
|
|
90
|
+
}
|
|
91
|
+
return manifest;
|
|
92
|
+
})();
|
|
93
|
+
const heroRep = await getRepresentativeImage(
|
|
94
|
+
heroSource || manifest,
|
|
95
|
+
HERO_REPRESENTATIVE_SIZE,
|
|
96
|
+
true
|
|
97
|
+
);
|
|
98
|
+
const canvasImage = findPrimaryCanvasImage(manifest);
|
|
99
|
+
const heroService =
|
|
100
|
+
(canvasImage && canvasImage.service) ||
|
|
101
|
+
(heroRep && heroRep.service);
|
|
102
|
+
const heroPreferred = buildIiifImageUrlFromService(
|
|
103
|
+
heroService,
|
|
104
|
+
HERO_THUMBNAIL_SIZE
|
|
105
|
+
);
|
|
106
|
+
const heroFallbackId = (() => {
|
|
107
|
+
if (canvasImage && canvasImage.id) return String(canvasImage.id);
|
|
108
|
+
if (heroRep && heroRep.id) return String(heroRep.id);
|
|
109
|
+
return '';
|
|
110
|
+
})();
|
|
111
|
+
const heroWidth = (() => {
|
|
112
|
+
if (canvasImage && typeof canvasImage.width === 'number')
|
|
113
|
+
return canvasImage.width;
|
|
114
|
+
if (heroRep && typeof heroRep.width === 'number') return heroRep.width;
|
|
115
|
+
return undefined;
|
|
116
|
+
})();
|
|
117
|
+
const heroHeight = (() => {
|
|
118
|
+
if (canvasImage && typeof canvasImage.height === 'number')
|
|
119
|
+
return canvasImage.height;
|
|
120
|
+
if (heroRep && typeof heroRep.height === 'number')
|
|
121
|
+
return heroRep.height;
|
|
122
|
+
return undefined;
|
|
123
|
+
})();
|
|
124
|
+
const heroSrcset = buildIiifImageSrcset(heroService);
|
|
125
|
+
const ogImage = heroService
|
|
126
|
+
? buildIiifImageUrlForDimensions(
|
|
127
|
+
heroService,
|
|
128
|
+
OG_IMAGE_WIDTH,
|
|
129
|
+
OG_IMAGE_HEIGHT
|
|
130
|
+
)
|
|
131
|
+
: '';
|
|
132
|
+
return {
|
|
133
|
+
heroThumbnail: heroPreferred || heroFallbackId || '',
|
|
134
|
+
heroThumbnailWidth: heroWidth,
|
|
135
|
+
heroThumbnailHeight: heroHeight,
|
|
136
|
+
heroThumbnailSrcset: heroSrcset || '',
|
|
137
|
+
heroThumbnailSizes: heroSrcset ? HERO_IMAGE_SIZES_ATTR : '',
|
|
138
|
+
ogImage: ogImage || '',
|
|
139
|
+
};
|
|
140
|
+
} catch (_) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
72
145
|
function firstLabelString(label) {
|
|
73
146
|
if (!label) return "Untitled";
|
|
74
147
|
if (typeof label === "string") return label;
|
|
@@ -133,6 +206,19 @@ function extractSummaryValues(manifest) {
|
|
|
133
206
|
return unique.join(" ");
|
|
134
207
|
}
|
|
135
208
|
|
|
209
|
+
function normalizeSummaryText(value) {
|
|
210
|
+
if (!value) return "";
|
|
211
|
+
return String(value).replace(/\s+/g, " ").trim();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function truncateSummary(value, max = 240) {
|
|
215
|
+
const normalized = normalizeSummaryText(value);
|
|
216
|
+
if (!normalized) return "";
|
|
217
|
+
if (normalized.length <= max) return normalized;
|
|
218
|
+
const slice = normalized.slice(0, Math.max(0, max - 3)).trimEnd();
|
|
219
|
+
return `${slice}...`;
|
|
220
|
+
}
|
|
221
|
+
|
|
136
222
|
function stripHtml(value) {
|
|
137
223
|
try {
|
|
138
224
|
return String(value || "")
|
|
@@ -655,104 +741,59 @@ async function ensureFeaturedInCache(cfg) {
|
|
|
655
741
|
}
|
|
656
742
|
|
|
657
743
|
try {
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
delete clone.thumbnail;
|
|
663
|
-
} catch (_) {
|
|
664
|
-
clone.thumbnail = undefined;
|
|
665
|
-
}
|
|
666
|
-
return clone;
|
|
667
|
-
}
|
|
668
|
-
return manifest;
|
|
669
|
-
})();
|
|
670
|
-
const heroRep = await getRepresentativeImage(
|
|
671
|
-
heroSource || manifest,
|
|
672
|
-
HERO_THUMBNAIL_SIZE,
|
|
673
|
-
true
|
|
674
|
-
);
|
|
675
|
-
const canvasImage = findPrimaryCanvasImage(manifest);
|
|
676
|
-
const heroService =
|
|
677
|
-
(canvasImage && canvasImage.service) ||
|
|
678
|
-
(heroRep && heroRep.service);
|
|
679
|
-
const preferredHeroThumbnail = buildIiifImageUrlFromService(
|
|
680
|
-
heroService,
|
|
681
|
-
HERO_THUMBNAIL_SIZE
|
|
682
|
-
);
|
|
683
|
-
const heroSrcset = buildIiifImageSrcset(heroService);
|
|
684
|
-
const heroFallbackId = (() => {
|
|
685
|
-
if (canvasImage && canvasImage.id) return String(canvasImage.id);
|
|
686
|
-
if (heroRep && heroRep.id) return String(heroRep.id);
|
|
687
|
-
return "";
|
|
688
|
-
})();
|
|
689
|
-
const heroWidth = (() => {
|
|
690
|
-
if (canvasImage && typeof canvasImage.width === "number") {
|
|
691
|
-
return canvasImage.width;
|
|
692
|
-
}
|
|
693
|
-
if (heroRep && typeof heroRep.width === "number") {
|
|
694
|
-
return heroRep.width;
|
|
695
|
-
}
|
|
696
|
-
return undefined;
|
|
697
|
-
})();
|
|
698
|
-
const heroHeight = (() => {
|
|
699
|
-
if (canvasImage && typeof canvasImage.height === "number") {
|
|
700
|
-
return canvasImage.height;
|
|
701
|
-
}
|
|
702
|
-
if (heroRep && typeof heroRep.height === "number") {
|
|
703
|
-
return heroRep.height;
|
|
704
|
-
}
|
|
705
|
-
return undefined;
|
|
706
|
-
})();
|
|
707
|
-
if (preferredHeroThumbnail || heroFallbackId) {
|
|
708
|
-
const nextHero = preferredHeroThumbnail || heroFallbackId;
|
|
709
|
-
if (entry.heroThumbnail !== nextHero) {
|
|
710
|
-
entry.heroThumbnail = nextHero;
|
|
744
|
+
const heroMedia = await resolveHeroMedia(manifest);
|
|
745
|
+
if (heroMedia && heroMedia.heroThumbnail) {
|
|
746
|
+
if (entry.heroThumbnail !== heroMedia.heroThumbnail) {
|
|
747
|
+
entry.heroThumbnail = heroMedia.heroThumbnail;
|
|
711
748
|
touched = true;
|
|
712
749
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (entry.heroThumbnailSrcset !== undefined) {
|
|
720
|
-
delete entry.heroThumbnailSrcset;
|
|
721
|
-
touched = true;
|
|
722
|
-
}
|
|
723
|
-
if (entry.heroThumbnailSizes !== undefined) {
|
|
724
|
-
delete entry.heroThumbnailSizes;
|
|
725
|
-
touched = true;
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
if (typeof heroWidth === "number") {
|
|
729
|
-
if (entry.heroThumbnailWidth !== heroWidth) touched = true;
|
|
730
|
-
entry.heroThumbnailWidth = heroWidth;
|
|
731
|
-
} else if (entry.heroThumbnailWidth !== undefined) {
|
|
732
|
-
delete entry.heroThumbnailWidth;
|
|
750
|
+
} else if (entry.heroThumbnail !== undefined) {
|
|
751
|
+
delete entry.heroThumbnail;
|
|
752
|
+
touched = true;
|
|
753
|
+
}
|
|
754
|
+
if (heroMedia && typeof heroMedia.heroThumbnailWidth === "number") {
|
|
755
|
+
if (entry.heroThumbnailWidth !== heroMedia.heroThumbnailWidth)
|
|
733
756
|
touched = true;
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
757
|
+
entry.heroThumbnailWidth = heroMedia.heroThumbnailWidth;
|
|
758
|
+
} else if (entry.heroThumbnailWidth !== undefined) {
|
|
759
|
+
delete entry.heroThumbnailWidth;
|
|
760
|
+
touched = true;
|
|
761
|
+
}
|
|
762
|
+
if (heroMedia && typeof heroMedia.heroThumbnailHeight === "number") {
|
|
763
|
+
if (entry.heroThumbnailHeight !== heroMedia.heroThumbnailHeight)
|
|
740
764
|
touched = true;
|
|
741
|
-
|
|
742
|
-
} else {
|
|
743
|
-
|
|
744
|
-
|
|
765
|
+
entry.heroThumbnailHeight = heroMedia.heroThumbnailHeight;
|
|
766
|
+
} else if (entry.heroThumbnailHeight !== undefined) {
|
|
767
|
+
delete entry.heroThumbnailHeight;
|
|
768
|
+
touched = true;
|
|
769
|
+
}
|
|
770
|
+
if (heroMedia && heroMedia.heroThumbnailSrcset) {
|
|
771
|
+
if (entry.heroThumbnailSrcset !== heroMedia.heroThumbnailSrcset)
|
|
745
772
|
touched = true;
|
|
746
|
-
|
|
747
|
-
if (entry.
|
|
748
|
-
|
|
773
|
+
entry.heroThumbnailSrcset = heroMedia.heroThumbnailSrcset;
|
|
774
|
+
if (entry.heroThumbnailSizes !== HERO_IMAGE_SIZES_ATTR) touched = true;
|
|
775
|
+
entry.heroThumbnailSizes = HERO_IMAGE_SIZES_ATTR;
|
|
776
|
+
} else {
|
|
777
|
+
if (entry.heroThumbnailSrcset !== undefined) {
|
|
778
|
+
delete entry.heroThumbnailSrcset;
|
|
749
779
|
touched = true;
|
|
750
780
|
}
|
|
751
|
-
if (entry.
|
|
752
|
-
delete entry.
|
|
781
|
+
if (entry.heroThumbnailSizes !== undefined) {
|
|
782
|
+
delete entry.heroThumbnailSizes;
|
|
753
783
|
touched = true;
|
|
754
784
|
}
|
|
755
785
|
}
|
|
786
|
+
if (heroMedia && heroMedia.ogImage) {
|
|
787
|
+
if (entry.ogImage !== heroMedia.ogImage) touched = true;
|
|
788
|
+
entry.ogImage = heroMedia.ogImage;
|
|
789
|
+
entry.ogImageWidth = OG_IMAGE_WIDTH;
|
|
790
|
+
entry.ogImageHeight = OG_IMAGE_HEIGHT;
|
|
791
|
+
} else if (entry.ogImage !== undefined) {
|
|
792
|
+
delete entry.ogImage;
|
|
793
|
+
delete entry.ogImageWidth;
|
|
794
|
+
delete entry.ogImageHeight;
|
|
795
|
+
touched = true;
|
|
796
|
+
}
|
|
756
797
|
} catch (_) {}
|
|
757
798
|
|
|
758
799
|
if (touched) await saveManifestIndex(idx);
|
|
@@ -962,58 +1003,22 @@ async function rebuildManifestIndexFromCache() {
|
|
|
962
1003
|
}
|
|
963
1004
|
} catch (_) {}
|
|
964
1005
|
try {
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
}
|
|
975
|
-
return manifest;
|
|
976
|
-
})();
|
|
977
|
-
const heroRep = await getRepresentativeImage(
|
|
978
|
-
heroSource || manifest,
|
|
979
|
-
HERO_THUMBNAIL_SIZE,
|
|
980
|
-
true
|
|
981
|
-
);
|
|
982
|
-
const canvasImage = findPrimaryCanvasImage(manifest);
|
|
983
|
-
const heroService =
|
|
984
|
-
(canvasImage && canvasImage.service) ||
|
|
985
|
-
(heroRep && heroRep.service);
|
|
986
|
-
const preferredHero = buildIiifImageUrlFromService(
|
|
987
|
-
heroService,
|
|
988
|
-
HERO_THUMBNAIL_SIZE
|
|
989
|
-
);
|
|
990
|
-
const heroFallbackId = (() => {
|
|
991
|
-
if (canvasImage && canvasImage.id) return String(canvasImage.id);
|
|
992
|
-
if (heroRep && heroRep.id) return String(heroRep.id);
|
|
993
|
-
return "";
|
|
994
|
-
})();
|
|
995
|
-
const heroWidth = (() => {
|
|
996
|
-
if (canvasImage && typeof canvasImage.width === "number")
|
|
997
|
-
return canvasImage.width;
|
|
998
|
-
if (heroRep && typeof heroRep.width === "number") return heroRep.width;
|
|
999
|
-
return undefined;
|
|
1000
|
-
})();
|
|
1001
|
-
const heroHeight = (() => {
|
|
1002
|
-
if (canvasImage && typeof canvasImage.height === "number")
|
|
1003
|
-
return canvasImage.height;
|
|
1004
|
-
if (heroRep && typeof heroRep.height === "number")
|
|
1005
|
-
return heroRep.height;
|
|
1006
|
-
return undefined;
|
|
1007
|
-
})();
|
|
1008
|
-
if (preferredHero || heroFallbackId) {
|
|
1009
|
-
entry.heroThumbnail = preferredHero || heroFallbackId;
|
|
1010
|
-
if (typeof heroWidth === "number") entry.heroThumbnailWidth = heroWidth;
|
|
1011
|
-
if (typeof heroHeight === "number") entry.heroThumbnailHeight = heroHeight;
|
|
1012
|
-
const heroSrcset = buildIiifImageSrcset(heroService);
|
|
1013
|
-
if (heroSrcset) {
|
|
1014
|
-
entry.heroThumbnailSrcset = heroSrcset;
|
|
1006
|
+
const heroMedia = await resolveHeroMedia(manifest);
|
|
1007
|
+
if (heroMedia && heroMedia.heroThumbnail) {
|
|
1008
|
+
entry.heroThumbnail = heroMedia.heroThumbnail;
|
|
1009
|
+
if (typeof heroMedia.heroThumbnailWidth === "number")
|
|
1010
|
+
entry.heroThumbnailWidth = heroMedia.heroThumbnailWidth;
|
|
1011
|
+
if (typeof heroMedia.heroThumbnailHeight === "number")
|
|
1012
|
+
entry.heroThumbnailHeight = heroMedia.heroThumbnailHeight;
|
|
1013
|
+
if (heroMedia.heroThumbnailSrcset) {
|
|
1014
|
+
entry.heroThumbnailSrcset = heroMedia.heroThumbnailSrcset;
|
|
1015
1015
|
entry.heroThumbnailSizes = HERO_IMAGE_SIZES_ATTR;
|
|
1016
1016
|
}
|
|
1017
|
+
if (heroMedia.ogImage) {
|
|
1018
|
+
entry.ogImage = heroMedia.ogImage;
|
|
1019
|
+
entry.ogImageWidth = OG_IMAGE_WIDTH;
|
|
1020
|
+
entry.ogImageHeight = OG_IMAGE_HEIGHT;
|
|
1021
|
+
}
|
|
1017
1022
|
}
|
|
1018
1023
|
} catch (_) {}
|
|
1019
1024
|
nextIndex.byId.push(entry);
|
|
@@ -1297,6 +1302,13 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1297
1302
|
if (!manifest) continue;
|
|
1298
1303
|
manifest = await normalizeToV3(manifest);
|
|
1299
1304
|
const title = firstLabelString(manifest.label);
|
|
1305
|
+
let summaryRaw = '';
|
|
1306
|
+
try {
|
|
1307
|
+
summaryRaw = extractSummaryValues(manifest);
|
|
1308
|
+
} catch (_) {
|
|
1309
|
+
summaryRaw = '';
|
|
1310
|
+
}
|
|
1311
|
+
const summaryForMeta = truncateSummary(summaryRaw || title);
|
|
1300
1312
|
const baseSlug =
|
|
1301
1313
|
slugify(title || "untitled", {
|
|
1302
1314
|
lower: true,
|
|
@@ -1354,26 +1366,74 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1354
1366
|
const {loadAppWrapper} = require("./mdx");
|
|
1355
1367
|
const app = await loadAppWrapper();
|
|
1356
1368
|
|
|
1369
|
+
let heroMedia = null;
|
|
1370
|
+
try {
|
|
1371
|
+
heroMedia = await resolveHeroMedia(manifest);
|
|
1372
|
+
} catch (_) {
|
|
1373
|
+
heroMedia = null;
|
|
1374
|
+
}
|
|
1375
|
+
const normalizedHref = href.split(path.sep).join("/");
|
|
1376
|
+
const pageHref = rootRelativeHref(normalizedHref);
|
|
1377
|
+
const pageDescription = summaryForMeta || title;
|
|
1378
|
+
const pageDetails = {
|
|
1379
|
+
title,
|
|
1380
|
+
href: pageHref,
|
|
1381
|
+
url: pageHref,
|
|
1382
|
+
slug,
|
|
1383
|
+
type: "work",
|
|
1384
|
+
description: pageDescription,
|
|
1385
|
+
meta: {
|
|
1386
|
+
title,
|
|
1387
|
+
description: pageDescription,
|
|
1388
|
+
type: "work",
|
|
1389
|
+
url: pageHref,
|
|
1390
|
+
},
|
|
1391
|
+
};
|
|
1392
|
+
const ogImageForPage = heroMedia && heroMedia.ogImage ? heroMedia.ogImage : '';
|
|
1393
|
+
if (ogImageForPage) {
|
|
1394
|
+
pageDetails.image = ogImageForPage;
|
|
1395
|
+
pageDetails.ogImage = ogImageForPage;
|
|
1396
|
+
pageDetails.meta.image = ogImageForPage;
|
|
1397
|
+
pageDetails.meta.ogImage = ogImageForPage;
|
|
1398
|
+
}
|
|
1399
|
+
const pageContextValue = { navigation: null, page: pageDetails };
|
|
1357
1400
|
const mdxContent = React.createElement(WorksLayoutComp, {manifest});
|
|
1358
|
-
const siteTree =
|
|
1401
|
+
const siteTree = mdxContent;
|
|
1359
1402
|
const wrappedApp =
|
|
1360
1403
|
app && app.App
|
|
1361
1404
|
? React.createElement(app.App, null, siteTree)
|
|
1362
1405
|
: siteTree;
|
|
1406
|
+
const withContext =
|
|
1407
|
+
PageContext && pageContextValue
|
|
1408
|
+
? React.createElement(
|
|
1409
|
+
PageContext.Provider,
|
|
1410
|
+
{ value: pageContextValue },
|
|
1411
|
+
wrappedApp
|
|
1412
|
+
)
|
|
1413
|
+
: wrappedApp;
|
|
1363
1414
|
const page = MDXProvider
|
|
1364
1415
|
? React.createElement(
|
|
1365
1416
|
MDXProvider,
|
|
1366
1417
|
{components: compMap},
|
|
1367
|
-
|
|
1418
|
+
withContext
|
|
1368
1419
|
)
|
|
1369
|
-
:
|
|
1420
|
+
: withContext;
|
|
1370
1421
|
const body = ReactDOMServer.renderToStaticMarkup(page);
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1422
|
+
let head = "";
|
|
1423
|
+
if (app && app.Head) {
|
|
1424
|
+
const headElement = React.createElement(app.Head, {
|
|
1425
|
+
page: pageContextValue.page,
|
|
1426
|
+
navigation: null,
|
|
1427
|
+
});
|
|
1428
|
+
const wrappedHead = PageContext
|
|
1429
|
+
? React.createElement(
|
|
1430
|
+
PageContext.Provider,
|
|
1431
|
+
{ value: pageContextValue },
|
|
1432
|
+
headElement
|
|
1375
1433
|
)
|
|
1376
|
-
:
|
|
1434
|
+
: headElement;
|
|
1435
|
+
head = ReactDOMServer.renderToStaticMarkup(wrappedHead);
|
|
1436
|
+
}
|
|
1377
1437
|
const needsHydrateViewer =
|
|
1378
1438
|
body.includes("data-canopy-viewer") ||
|
|
1379
1439
|
body.includes("data-canopy-scroll") ||
|
|
@@ -1438,7 +1498,7 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1438
1498
|
else if (needsRelated && sliderRel) jsRel = sliderRel;
|
|
1439
1499
|
else if (viewerRel) jsRel = viewerRel;
|
|
1440
1500
|
|
|
1441
|
-
|
|
1501
|
+
const headSegments = [head];
|
|
1442
1502
|
const needsReact = !!(
|
|
1443
1503
|
needsHydrateViewer ||
|
|
1444
1504
|
needsRelated
|
|
@@ -1474,7 +1534,7 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1474
1534
|
if (searchFormRel && jsRel !== searchFormRel)
|
|
1475
1535
|
extraScripts.push(`<script defer src="${searchFormRel}"></script>`);
|
|
1476
1536
|
if (extraScripts.length)
|
|
1477
|
-
|
|
1537
|
+
headSegments.push(extraScripts.join(""));
|
|
1478
1538
|
try {
|
|
1479
1539
|
const {BASE_PATH} = require("../common");
|
|
1480
1540
|
if (BASE_PATH)
|
|
@@ -1484,12 +1544,13 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1484
1544
|
)}</script>` + vendorTag;
|
|
1485
1545
|
} catch (_) {}
|
|
1486
1546
|
let pageBody = body;
|
|
1547
|
+
const headExtra = headSegments.join("") + vendorTag;
|
|
1487
1548
|
let html = htmlShell({
|
|
1488
1549
|
title,
|
|
1489
1550
|
body: pageBody,
|
|
1490
1551
|
cssHref: null,
|
|
1491
1552
|
scriptHref: jsRel,
|
|
1492
|
-
headExtra
|
|
1553
|
+
headExtra,
|
|
1493
1554
|
});
|
|
1494
1555
|
try {
|
|
1495
1556
|
html = require("../common").applyBaseToHtml(html);
|
|
@@ -1516,14 +1577,38 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1516
1577
|
e.id === String(manifest.id || id) &&
|
|
1517
1578
|
e.type === "Manifest"
|
|
1518
1579
|
);
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1580
|
+
if (entry) {
|
|
1581
|
+
entry.thumbnail = String(thumbUrl);
|
|
1582
|
+
if (typeof thumbWidth === "number")
|
|
1583
|
+
entry.thumbnailWidth = thumbWidth;
|
|
1584
|
+
if (typeof thumbHeight === "number")
|
|
1585
|
+
entry.thumbnailHeight = thumbHeight;
|
|
1586
|
+
if (heroMedia && heroMedia.heroThumbnail) {
|
|
1587
|
+
entry.heroThumbnail = heroMedia.heroThumbnail;
|
|
1588
|
+
if (typeof heroMedia.heroThumbnailWidth === "number")
|
|
1589
|
+
entry.heroThumbnailWidth = heroMedia.heroThumbnailWidth;
|
|
1590
|
+
if (typeof heroMedia.heroThumbnailHeight === "number")
|
|
1591
|
+
entry.heroThumbnailHeight = heroMedia.heroThumbnailHeight;
|
|
1592
|
+
if (heroMedia.heroThumbnailSrcset) {
|
|
1593
|
+
entry.heroThumbnailSrcset = heroMedia.heroThumbnailSrcset;
|
|
1594
|
+
entry.heroThumbnailSizes = HERO_IMAGE_SIZES_ATTR;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
if (heroMedia && heroMedia.ogImage) {
|
|
1598
|
+
entry.ogImage = heroMedia.ogImage;
|
|
1599
|
+
entry.ogImageWidth = OG_IMAGE_WIDTH;
|
|
1600
|
+
entry.ogImageHeight = OG_IMAGE_HEIGHT;
|
|
1601
|
+
} else {
|
|
1602
|
+
try {
|
|
1603
|
+
if (entry.ogImage !== undefined) delete entry.ogImage;
|
|
1604
|
+
if (entry.ogImageWidth !== undefined)
|
|
1605
|
+
delete entry.ogImageWidth;
|
|
1606
|
+
if (entry.ogImageHeight !== undefined)
|
|
1607
|
+
delete entry.ogImageHeight;
|
|
1608
|
+
} catch (_) {}
|
|
1526
1609
|
}
|
|
1610
|
+
await saveManifestIndex(idx);
|
|
1611
|
+
}
|
|
1527
1612
|
}
|
|
1528
1613
|
}
|
|
1529
1614
|
} catch (_) {}
|
|
@@ -1538,11 +1623,7 @@ async function buildIiifCollectionPages(CONFIG) {
|
|
|
1538
1623
|
}
|
|
1539
1624
|
}
|
|
1540
1625
|
if (summaryOptions && summaryOptions.enabled) {
|
|
1541
|
-
|
|
1542
|
-
summaryValue = extractSummaryValues(manifest);
|
|
1543
|
-
} catch (_) {
|
|
1544
|
-
summaryValue = "";
|
|
1545
|
-
}
|
|
1626
|
+
summaryValue = summaryRaw || "";
|
|
1546
1627
|
}
|
|
1547
1628
|
if (annotationsOptions && annotationsOptions.enabled) {
|
|
1548
1629
|
try {
|
package/lib/build/mdx.js
CHANGED
|
@@ -471,10 +471,17 @@ async function compileMdxFile(filePath, outPath, Layout, extraProps = {}) {
|
|
|
471
471
|
? React.createElement(MDXProvider, { components: compMap }, withApp)
|
|
472
472
|
: withApp;
|
|
473
473
|
const body = ReactDOMServer.renderToStaticMarkup(page);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
:
|
|
474
|
+
let head = '';
|
|
475
|
+
if (app && app.Head) {
|
|
476
|
+
const headElement = React.createElement(app.Head, {
|
|
477
|
+
page: contextValue.page,
|
|
478
|
+
navigation: contextValue.navigation,
|
|
479
|
+
});
|
|
480
|
+
const wrappedHead = PageContext
|
|
481
|
+
? React.createElement(PageContext.Provider, { value: contextValue }, headElement)
|
|
482
|
+
: headElement;
|
|
483
|
+
head = ReactDOMServer.renderToStaticMarkup(wrappedHead);
|
|
484
|
+
}
|
|
478
485
|
return { body, head };
|
|
479
486
|
}
|
|
480
487
|
|
package/lib/build/pages.js
CHANGED
|
@@ -3,6 +3,31 @@ const { log } = require('./log');
|
|
|
3
3
|
const mdx = require('./mdx');
|
|
4
4
|
const navigation = require('../components/navigation');
|
|
5
5
|
|
|
6
|
+
function normalizeWhitespace(value) {
|
|
7
|
+
if (!value) return '';
|
|
8
|
+
return String(value).replace(/\s+/g, ' ').trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function truncateDescription(value, max = 240) {
|
|
12
|
+
const normalized = normalizeWhitespace(value);
|
|
13
|
+
if (!normalized) return '';
|
|
14
|
+
if (normalized.length <= max) return normalized;
|
|
15
|
+
const slice = normalized.slice(0, Math.max(0, max - 3)).trimEnd();
|
|
16
|
+
return `${slice}...`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isPlainObject(value) {
|
|
20
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function readFrontmatterString(data, key) {
|
|
24
|
+
if (!data) return '';
|
|
25
|
+
const raw = data[key];
|
|
26
|
+
if (raw == null) return '';
|
|
27
|
+
if (typeof raw === 'string') return raw.trim();
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
|
|
6
31
|
// Cache: dir -> frontmatter data for _layout.mdx in that dir
|
|
7
32
|
const LAYOUT_META = new Map();
|
|
8
33
|
|
|
@@ -50,11 +75,60 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
|
|
|
50
75
|
const pageInfo = navigation.getPageInfo(normalizedRel);
|
|
51
76
|
const navData = navigation.buildNavigationForFile(normalizedRel);
|
|
52
77
|
const mergedProps = { ...(extraProps || {}) };
|
|
78
|
+
const frontmatter =
|
|
79
|
+
typeof mdx.parseFrontmatter === 'function'
|
|
80
|
+
? mdx.parseFrontmatter(source)
|
|
81
|
+
: { data: null, content: source };
|
|
82
|
+
const frontmatterData = frontmatter && isPlainObject(frontmatter.data) ? frontmatter.data : null;
|
|
83
|
+
let layoutMeta = null;
|
|
84
|
+
try {
|
|
85
|
+
layoutMeta = await getNearestDirLayoutMeta(filePath);
|
|
86
|
+
} catch (_) {
|
|
87
|
+
layoutMeta = null;
|
|
88
|
+
}
|
|
89
|
+
const directType = frontmatterData && typeof frontmatterData.type === 'string' ? frontmatterData.type.trim() : '';
|
|
90
|
+
const layoutType = layoutMeta && typeof layoutMeta.type === 'string' ? String(layoutMeta.type).trim() : '';
|
|
91
|
+
const resolvedType = directType || layoutType || (!frontmatterData ? 'page' : '');
|
|
92
|
+
const frontmatterDescription = frontmatterData && typeof frontmatterData.description === 'string'
|
|
93
|
+
? truncateDescription(frontmatterData.description)
|
|
94
|
+
: '';
|
|
95
|
+
const extractedPlain = typeof mdx.extractPlainText === 'function'
|
|
96
|
+
? mdx.extractPlainText(source)
|
|
97
|
+
: '';
|
|
98
|
+
const derivedDescription = truncateDescription(extractedPlain);
|
|
99
|
+
const resolvedDescription = frontmatterDescription || derivedDescription;
|
|
100
|
+
const ogImageFrontmatter =
|
|
101
|
+
readFrontmatterString(frontmatterData, 'og:image') ||
|
|
102
|
+
readFrontmatterString(frontmatterData, 'ogImage');
|
|
103
|
+
const genericImage = readFrontmatterString(frontmatterData, 'image');
|
|
104
|
+
const resolvedImage = ogImageFrontmatter || genericImage;
|
|
105
|
+
const frontmatterMeta = frontmatterData && isPlainObject(frontmatterData.meta) ? frontmatterData.meta : null;
|
|
53
106
|
const headings = mdx.extractHeadings(source);
|
|
54
|
-
|
|
107
|
+
const basePage = pageInfo ? { ...pageInfo } : {};
|
|
108
|
+
if (title) basePage.title = title;
|
|
109
|
+
if (resolvedType) basePage.type = resolvedType;
|
|
110
|
+
if (resolvedDescription) basePage.description = resolvedDescription;
|
|
111
|
+
if (basePage.href && !basePage.url) basePage.url = basePage.href;
|
|
112
|
+
if (resolvedImage) {
|
|
113
|
+
basePage.image = resolvedImage;
|
|
114
|
+
basePage.ogImage = ogImageFrontmatter || resolvedImage;
|
|
115
|
+
}
|
|
116
|
+
const existingMeta = basePage.meta && typeof basePage.meta === 'object' ? basePage.meta : {};
|
|
117
|
+
const pageMeta = { ...existingMeta };
|
|
118
|
+
if (title) pageMeta.title = title;
|
|
119
|
+
if (resolvedDescription) pageMeta.description = resolvedDescription;
|
|
120
|
+
if (resolvedType) pageMeta.type = resolvedType;
|
|
121
|
+
if (basePage.url || basePage.href) pageMeta.url = basePage.url || basePage.href || pageMeta.url;
|
|
122
|
+
if (resolvedImage) {
|
|
123
|
+
pageMeta.image = resolvedImage;
|
|
124
|
+
if (!pageMeta.ogImage) pageMeta.ogImage = resolvedImage;
|
|
125
|
+
}
|
|
126
|
+
if (frontmatterMeta) Object.assign(pageMeta, frontmatterMeta);
|
|
127
|
+
if (Object.keys(pageMeta).length) basePage.meta = pageMeta;
|
|
128
|
+
if (Object.keys(basePage).length) {
|
|
55
129
|
mergedProps.page = mergedProps.page
|
|
56
|
-
? { ...
|
|
57
|
-
:
|
|
130
|
+
? { ...basePage, ...mergedProps.page }
|
|
131
|
+
: basePage;
|
|
58
132
|
}
|
|
59
133
|
if (navData && !mergedProps.navigation) {
|
|
60
134
|
mergedProps.navigation = navData;
|
|
@@ -116,7 +190,7 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
|
|
|
116
190
|
const { BASE_PATH } = require('../common');
|
|
117
191
|
if (BASE_PATH) vendorTag = `<script>window.CANOPY_BASE_PATH=${JSON.stringify(BASE_PATH)}</script>` + vendorTag;
|
|
118
192
|
} catch (_) {}
|
|
119
|
-
|
|
193
|
+
const headSegments = [head];
|
|
120
194
|
const extraScripts = [];
|
|
121
195
|
if (heroRel && jsRel !== heroRel) extraScripts.push(`<script defer src="${heroRel}"></script>`);
|
|
122
196
|
if (facetsRel && jsRel !== facetsRel) extraScripts.push(`<script defer src="${facetsRel}"></script>`);
|
|
@@ -133,9 +207,10 @@ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
|
|
|
133
207
|
} catch (_) {}
|
|
134
208
|
extraStyles.push(`<link rel="stylesheet" href="${rel}">`);
|
|
135
209
|
}
|
|
136
|
-
if (extraStyles.length)
|
|
137
|
-
if (extraScripts.length)
|
|
138
|
-
const
|
|
210
|
+
if (extraStyles.length) headSegments.push(extraStyles.join(''));
|
|
211
|
+
if (extraScripts.length) headSegments.push(extraScripts.join(''));
|
|
212
|
+
const headExtra = headSegments.join('') + vendorTag;
|
|
213
|
+
const html = htmlShell({ title, body, cssHref: null, scriptHref: jsRel, headExtra });
|
|
139
214
|
const { applyBaseToHtml } = require('../common');
|
|
140
215
|
return applyBaseToHtml(html);
|
|
141
216
|
}
|
package/lib/common.js
CHANGED
|
@@ -70,7 +70,9 @@ function htmlShell({ title, body, cssHref, scriptHref, headExtra }) {
|
|
|
70
70
|
const cssTag = cssHref ? `<link rel="stylesheet" href="${cssHref}">` : '';
|
|
71
71
|
const appearance = resolveThemeAppearance();
|
|
72
72
|
const htmlClass = appearance === 'dark' ? ' class="dark"' : '';
|
|
73
|
-
|
|
73
|
+
const hasCustomTitle = /<title\b/i.test(extra);
|
|
74
|
+
const titleTag = hasCustomTitle ? '' : `<title>${title}</title>`;
|
|
75
|
+
return `<!doctype html><html lang="en"${htmlClass}><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/>${titleTag}${extra}${cssTag}${scriptTag}</head><body>${body}</body></html>`;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
function withBase(href) {
|
package/lib/head.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const React = require('react');
|
|
2
|
-
const { withBase, rootRelativeHref } = require('./common');
|
|
2
|
+
const { withBase, rootRelativeHref, absoluteUrl } = require('./common');
|
|
3
|
+
const { getPageContext } = require('./page-context');
|
|
3
4
|
|
|
4
5
|
const DEFAULT_STYLESHEET_PATH = '/styles/styles.css';
|
|
5
6
|
|
|
@@ -14,8 +15,97 @@ function Stylesheet(props = {}) {
|
|
|
14
15
|
return React.createElement('link', { rel, href: resolved, ...rest });
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
function normalizeText(value) {
|
|
19
|
+
if (!value) return '';
|
|
20
|
+
return String(value).replace(/\s+/g, ' ').trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function truncateText(value, max = 240) {
|
|
24
|
+
const normalized = normalizeText(value);
|
|
25
|
+
if (!normalized) return '';
|
|
26
|
+
if (normalized.length <= max) return normalized;
|
|
27
|
+
const slice = normalized.slice(0, Math.max(0, max - 3)).trimEnd();
|
|
28
|
+
return `${slice}...`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveOgType(pageType) {
|
|
32
|
+
const type = String(pageType || '').toLowerCase();
|
|
33
|
+
if (type === 'work' || type === 'article') return 'article';
|
|
34
|
+
if (type === 'docs' || type === 'documentation') return 'article';
|
|
35
|
+
return 'website';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveUrl(value) {
|
|
39
|
+
if (!value) return '';
|
|
40
|
+
const raw = typeof value === 'string' ? value.trim() : String(value || '');
|
|
41
|
+
if (!raw) return '';
|
|
42
|
+
return absoluteUrl(raw);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Meta(props = {}) {
|
|
46
|
+
const PageContext = getPageContext();
|
|
47
|
+
const context = PageContext ? React.useContext(PageContext) : null;
|
|
48
|
+
const ctxPage = context && context.page ? context.page : null;
|
|
49
|
+
const explicitPage = props.page || null;
|
|
50
|
+
const page = explicitPage || ctxPage || {};
|
|
51
|
+
const fallbackTitle = ctxPage && ctxPage.title ? ctxPage.title : '';
|
|
52
|
+
const metaFromPage = page && page.meta && typeof page.meta === 'object' ? page.meta : null;
|
|
53
|
+
const rawTitle =
|
|
54
|
+
props.title ||
|
|
55
|
+
(metaFromPage && metaFromPage.title) ||
|
|
56
|
+
page.title ||
|
|
57
|
+
fallbackTitle;
|
|
58
|
+
const pageTitle = normalizeText(rawTitle);
|
|
59
|
+
const siteTitle = normalizeText(props.siteTitle) || '';
|
|
60
|
+
const defaultTitle = siteTitle || 'Canopy IIIF';
|
|
61
|
+
const title = pageTitle ? pageTitle : defaultTitle;
|
|
62
|
+
const fullTitle = siteTitle ? (pageTitle ? `${pageTitle} | ${siteTitle}` : siteTitle) : title;
|
|
63
|
+
const rawDescription =
|
|
64
|
+
props.description ||
|
|
65
|
+
(metaFromPage && metaFromPage.description) ||
|
|
66
|
+
page.description ||
|
|
67
|
+
'';
|
|
68
|
+
const description = truncateText(rawDescription);
|
|
69
|
+
const resolvedType = props.type || (metaFromPage && metaFromPage.type) || page.type || '';
|
|
70
|
+
const ogType = resolveOgType(resolvedType);
|
|
71
|
+
const relativeUrl =
|
|
72
|
+
props.url ||
|
|
73
|
+
(metaFromPage && metaFromPage.url) ||
|
|
74
|
+
page.url ||
|
|
75
|
+
page.href ||
|
|
76
|
+
'';
|
|
77
|
+
const absolute = relativeUrl ? absoluteUrl(relativeUrl) : '';
|
|
78
|
+
const ogImageRaw =
|
|
79
|
+
props.image ||
|
|
80
|
+
props.ogImage ||
|
|
81
|
+
(metaFromPage && (metaFromPage.ogImage || metaFromPage.image)) ||
|
|
82
|
+
page.ogImage ||
|
|
83
|
+
page.image ||
|
|
84
|
+
'';
|
|
85
|
+
const image = ogImageRaw ? resolveUrl(ogImageRaw) : '';
|
|
86
|
+
const twitterImageRaw = props.twitterImage || ogImageRaw;
|
|
87
|
+
const twitterImage = twitterImageRaw ? resolveUrl(twitterImageRaw) : '';
|
|
88
|
+
const twitterCard = props.twitterCard || (twitterImage ? 'summary_large_image' : 'summary');
|
|
89
|
+
|
|
90
|
+
const nodes = [];
|
|
91
|
+
if (fullTitle) nodes.push(React.createElement('title', { key: 'meta-title' }, fullTitle));
|
|
92
|
+
if (description) nodes.push(React.createElement('meta', { key: 'meta-description', name: 'description', content: description }));
|
|
93
|
+
if (fullTitle) nodes.push(React.createElement('meta', { key: 'og-title', property: 'og:title', content: fullTitle }));
|
|
94
|
+
if (description) nodes.push(React.createElement('meta', { key: 'og-description', property: 'og:description', content: description }));
|
|
95
|
+
if (absolute) nodes.push(React.createElement('meta', { key: 'og-url', property: 'og:url', content: absolute }));
|
|
96
|
+
if (ogType) nodes.push(React.createElement('meta', { key: 'og-type', property: 'og:type', content: ogType }));
|
|
97
|
+
if (image) nodes.push(React.createElement('meta', { key: 'og-image', property: 'og:image', content: image }));
|
|
98
|
+
if (twitterCard) nodes.push(React.createElement('meta', { key: 'twitter-card', name: 'twitter:card', content: twitterCard }));
|
|
99
|
+
if (fullTitle) nodes.push(React.createElement('meta', { key: 'twitter-title', name: 'twitter:title', content: fullTitle }));
|
|
100
|
+
if (description) nodes.push(React.createElement('meta', { key: 'twitter-description', name: 'twitter:description', content: description }));
|
|
101
|
+
if (twitterImage) nodes.push(React.createElement('meta', { key: 'twitter-image', name: 'twitter:image', content: twitterImage }));
|
|
102
|
+
|
|
103
|
+
return React.createElement(React.Fragment, null, nodes);
|
|
104
|
+
}
|
|
105
|
+
|
|
17
106
|
module.exports = {
|
|
18
107
|
stylesheetHref,
|
|
19
108
|
Stylesheet,
|
|
20
109
|
DEFAULT_STYLESHEET_PATH,
|
|
110
|
+
Meta,
|
|
21
111
|
};
|
package/lib/iiif/thumbnail.js
CHANGED
|
@@ -199,6 +199,18 @@ function buildIiifImageUrlFromService(service, preferredSize = 800) {
|
|
|
199
199
|
return buildIiifImageUrlFromNormalizedService(normalized, preferredSize);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
function buildIiifImageUrlForDimensions(service, width = 1200, height = 630) {
|
|
203
|
+
const normalized = normalizeImageServiceCandidate(service);
|
|
204
|
+
if (!normalized || !isIiifImageService(normalized)) return '';
|
|
205
|
+
const baseId = normalizeServiceBaseId(normalized.id);
|
|
206
|
+
if (!baseId) return '';
|
|
207
|
+
const safeWidth = Math.max(1, Math.floor(Number(width) || 0));
|
|
208
|
+
const safeHeight = Math.max(1, Math.floor(Number(height) || 0));
|
|
209
|
+
const quality = selectServiceQuality(normalized);
|
|
210
|
+
const format = selectServiceFormat(normalized);
|
|
211
|
+
return `${baseId}/full/!${safeWidth},${safeHeight}/0/${quality}.${format}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
202
214
|
function buildIiifImageSrcset(service, steps = [360, 640, 960, 1280, 1600]) {
|
|
203
215
|
const normalized = normalizeImageServiceCandidate(service);
|
|
204
216
|
if (!normalized || !isIiifImageService(normalized)) return '';
|
|
@@ -339,6 +351,7 @@ module.exports = {
|
|
|
339
351
|
getThumbnail,
|
|
340
352
|
getThumbnailUrl,
|
|
341
353
|
buildIiifImageUrlFromService,
|
|
354
|
+
buildIiifImageUrlForDimensions,
|
|
342
355
|
findPrimaryCanvasImage,
|
|
343
356
|
buildIiifImageSrcset,
|
|
344
357
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
const { stylesheetHref, Stylesheet } = require('./head');
|
|
1
|
+
const { stylesheetHref, Stylesheet, Meta } = require('./head');
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
build: require('./build/build').build,
|
|
5
5
|
dev: require('./build/dev').dev,
|
|
6
6
|
stylesheetHref,
|
|
7
7
|
Stylesheet,
|
|
8
|
+
Meta,
|
|
8
9
|
};
|
package/package.json
CHANGED
package/ui/tailwind-config.js
CHANGED
|
@@ -33,7 +33,11 @@ const resolveProjectRoot = (metaUrl, explicitRoot) => {
|
|
|
33
33
|
const href = typeof metaUrl === "string" ? metaUrl : metaUrl.href;
|
|
34
34
|
if (href) {
|
|
35
35
|
const fromUrl = fileURLToPath(href);
|
|
36
|
-
|
|
36
|
+
const dir = path.dirname(fromUrl);
|
|
37
|
+
if (dir.includes(`${path.sep}node_modules${path.sep}`)) {
|
|
38
|
+
return process.cwd();
|
|
39
|
+
}
|
|
40
|
+
return dir;
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
43
|
return process.cwd();
|