@canopy-iiif/app 1.4.14 → 1.4.16

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 CHANGED
@@ -138,6 +138,23 @@ function resolveThumbnailPreferences() {
138
138
  };
139
139
  }
140
140
 
141
+ function ensureThumbnailValue(target, url, width, height) {
142
+ if (!target) return false;
143
+ const current = target.thumbnail;
144
+ const hasCurrent =
145
+ typeof current === "string" ? current.trim().length > 0 : Boolean(current);
146
+ if (hasCurrent) return false;
147
+ if (!url) return false;
148
+ const normalized = String(url || "").trim();
149
+ if (!normalized) return false;
150
+ target.thumbnail = normalized;
151
+ if (typeof width === "number" && Number.isFinite(width) && width > 0)
152
+ target.thumbnailWidth = width;
153
+ if (typeof height === "number" && Number.isFinite(height) && height > 0)
154
+ target.thumbnailHeight = height;
155
+ return true;
156
+ }
157
+
141
158
  async function resolveHeroMedia(manifest) {
142
159
  if (!manifest) return null;
143
160
  try {
@@ -959,6 +976,16 @@ async function ensureFeaturedInCache(cfg) {
959
976
  delete entry.ogImageHeight;
960
977
  touched = true;
961
978
  }
979
+ if (
980
+ ensureThumbnailValue(
981
+ entry,
982
+ heroMedia && heroMedia.heroThumbnail,
983
+ heroMedia && heroMedia.heroThumbnailWidth,
984
+ heroMedia && heroMedia.heroThumbnailHeight
985
+ )
986
+ ) {
987
+ touched = true;
988
+ }
962
989
  } catch (_) {}
963
990
 
964
991
  if (touched) await saveManifestIndex(idx);
@@ -1191,6 +1218,12 @@ async function rebuildManifestIndexFromCache() {
1191
1218
  entry.ogImageWidth = OG_IMAGE_WIDTH;
1192
1219
  entry.ogImageHeight = OG_IMAGE_HEIGHT;
1193
1220
  }
1221
+ ensureThumbnailValue(
1222
+ entry,
1223
+ heroMedia.heroThumbnail,
1224
+ heroMedia.heroThumbnailWidth,
1225
+ heroMedia.heroThumbnailHeight
1226
+ );
1194
1227
  }
1195
1228
  } catch (_) {}
1196
1229
  nextIndex.byId.push(entry);
@@ -1850,46 +1883,133 @@ async function buildIiifCollectionPages(CONFIG) {
1850
1883
  thumbUrl = String(t.url);
1851
1884
  thumbWidth = typeof t.width === "number" ? t.width : undefined;
1852
1885
  thumbHeight = typeof t.height === "number" ? t.height : undefined;
1853
- const idx = await loadManifestIndex();
1854
- if (Array.isArray(idx.byId)) {
1855
- const entry = idx.byId.find(
1856
- (e) =>
1857
- e &&
1858
- e.id === String(manifest.id || id) &&
1859
- e.type === "Manifest"
1860
- );
1886
+ }
1887
+ } catch (_) {}
1888
+ try {
1889
+ const idx = await loadManifestIndex();
1890
+ if (Array.isArray(idx.byId)) {
1891
+ const entry = idx.byId.find(
1892
+ (e) =>
1893
+ e &&
1894
+ e.id === String(manifest.id || id) &&
1895
+ e.type === "Manifest"
1896
+ );
1861
1897
  if (entry) {
1862
- entry.thumbnail = String(thumbUrl);
1863
- if (typeof thumbWidth === "number")
1864
- entry.thumbnailWidth = thumbWidth;
1865
- if (typeof thumbHeight === "number")
1866
- entry.thumbnailHeight = thumbHeight;
1898
+ let touched = false;
1899
+ if (thumbUrl) {
1900
+ const nextThumb = String(thumbUrl);
1901
+ if (entry.thumbnail !== nextThumb) {
1902
+ entry.thumbnail = nextThumb;
1903
+ touched = true;
1904
+ }
1905
+ if (
1906
+ typeof thumbWidth === "number" &&
1907
+ entry.thumbnailWidth !== thumbWidth
1908
+ ) {
1909
+ entry.thumbnailWidth = thumbWidth;
1910
+ touched = true;
1911
+ }
1912
+ if (
1913
+ typeof thumbHeight === "number" &&
1914
+ entry.thumbnailHeight !== thumbHeight
1915
+ ) {
1916
+ entry.thumbnailHeight = thumbHeight;
1917
+ touched = true;
1918
+ }
1919
+ }
1867
1920
  if (heroMedia && heroMedia.heroThumbnail) {
1868
- entry.heroThumbnail = heroMedia.heroThumbnail;
1869
- if (typeof heroMedia.heroThumbnailWidth === "number")
1921
+ if (entry.heroThumbnail !== heroMedia.heroThumbnail) {
1922
+ entry.heroThumbnail = heroMedia.heroThumbnail;
1923
+ touched = true;
1924
+ }
1925
+ if (
1926
+ typeof heroMedia.heroThumbnailWidth === "number" &&
1927
+ entry.heroThumbnailWidth !== heroMedia.heroThumbnailWidth
1928
+ ) {
1870
1929
  entry.heroThumbnailWidth = heroMedia.heroThumbnailWidth;
1871
- if (typeof heroMedia.heroThumbnailHeight === "number")
1930
+ touched = true;
1931
+ }
1932
+ if (
1933
+ typeof heroMedia.heroThumbnailHeight === "number" &&
1934
+ entry.heroThumbnailHeight !== heroMedia.heroThumbnailHeight
1935
+ ) {
1872
1936
  entry.heroThumbnailHeight = heroMedia.heroThumbnailHeight;
1937
+ touched = true;
1938
+ }
1873
1939
  if (heroMedia.heroThumbnailSrcset) {
1874
- entry.heroThumbnailSrcset = heroMedia.heroThumbnailSrcset;
1875
- entry.heroThumbnailSizes = HERO_IMAGE_SIZES_ATTR;
1940
+ if (
1941
+ entry.heroThumbnailSrcset !== heroMedia.heroThumbnailSrcset
1942
+ ) {
1943
+ entry.heroThumbnailSrcset = heroMedia.heroThumbnailSrcset;
1944
+ touched = true;
1945
+ }
1946
+ if (entry.heroThumbnailSizes !== HERO_IMAGE_SIZES_ATTR) {
1947
+ entry.heroThumbnailSizes = HERO_IMAGE_SIZES_ATTR;
1948
+ touched = true;
1949
+ }
1950
+ }
1951
+ } else {
1952
+ if (entry.heroThumbnail !== undefined) {
1953
+ delete entry.heroThumbnail;
1954
+ touched = true;
1955
+ }
1956
+ if (entry.heroThumbnailWidth !== undefined) {
1957
+ delete entry.heroThumbnailWidth;
1958
+ touched = true;
1959
+ }
1960
+ if (entry.heroThumbnailHeight !== undefined) {
1961
+ delete entry.heroThumbnailHeight;
1962
+ touched = true;
1963
+ }
1964
+ if (entry.heroThumbnailSrcset !== undefined) {
1965
+ delete entry.heroThumbnailSrcset;
1966
+ touched = true;
1967
+ }
1968
+ if (entry.heroThumbnailSizes !== undefined) {
1969
+ delete entry.heroThumbnailSizes;
1970
+ touched = true;
1876
1971
  }
1877
1972
  }
1878
1973
  if (heroMedia && heroMedia.ogImage) {
1879
- entry.ogImage = heroMedia.ogImage;
1880
- entry.ogImageWidth = OG_IMAGE_WIDTH;
1881
- entry.ogImageHeight = OG_IMAGE_HEIGHT;
1974
+ if (entry.ogImage !== heroMedia.ogImage) {
1975
+ entry.ogImage = heroMedia.ogImage;
1976
+ touched = true;
1977
+ }
1978
+ if (entry.ogImageWidth !== OG_IMAGE_WIDTH) {
1979
+ entry.ogImageWidth = OG_IMAGE_WIDTH;
1980
+ touched = true;
1981
+ }
1982
+ if (entry.ogImageHeight !== OG_IMAGE_HEIGHT) {
1983
+ entry.ogImageHeight = OG_IMAGE_HEIGHT;
1984
+ touched = true;
1985
+ }
1882
1986
  } else {
1883
1987
  try {
1884
- if (entry.ogImage !== undefined) delete entry.ogImage;
1885
- if (entry.ogImageWidth !== undefined)
1988
+ if (entry.ogImage !== undefined) {
1989
+ delete entry.ogImage;
1990
+ touched = true;
1991
+ }
1992
+ if (entry.ogImageWidth !== undefined) {
1886
1993
  delete entry.ogImageWidth;
1887
- if (entry.ogImageHeight !== undefined)
1994
+ touched = true;
1995
+ }
1996
+ if (entry.ogImageHeight !== undefined) {
1888
1997
  delete entry.ogImageHeight;
1998
+ touched = true;
1999
+ }
1889
2000
  } catch (_) {}
1890
2001
  }
1891
- await saveManifestIndex(idx);
1892
- }
2002
+ if (
2003
+ ensureThumbnailValue(
2004
+ entry,
2005
+ heroMedia && heroMedia.heroThumbnail,
2006
+ heroMedia && heroMedia.heroThumbnailWidth,
2007
+ heroMedia && heroMedia.heroThumbnailHeight
2008
+ )
2009
+ ) {
2010
+ touched = true;
2011
+ }
2012
+ if (touched) await saveManifestIndex(idx);
1893
2013
  }
1894
2014
  }
1895
2015
  } catch (_) {}
@@ -1916,20 +2036,21 @@ async function buildIiifCollectionPages(CONFIG) {
1916
2036
  annotationValue = "";
1917
2037
  }
1918
2038
  }
1919
- const navThumbnail =
1920
- thumbUrl || (heroMedia && heroMedia.heroThumbnail) || "";
1921
- const navThumbWidth =
1922
- typeof thumbWidth === "number"
1923
- ? thumbWidth
1924
- : heroMedia && typeof heroMedia.heroThumbnailWidth === "number"
2039
+ const fallbackThumbnail =
2040
+ (heroMedia && heroMedia.heroThumbnail) || "";
2041
+ const fallbackThumbWidth =
2042
+ heroMedia && typeof heroMedia.heroThumbnailWidth === "number"
1925
2043
  ? heroMedia.heroThumbnailWidth
1926
2044
  : undefined;
1927
- const navThumbHeight =
1928
- typeof thumbHeight === "number"
1929
- ? thumbHeight
1930
- : heroMedia && typeof heroMedia.heroThumbnailHeight === "number"
2045
+ const fallbackThumbHeight =
2046
+ heroMedia && typeof heroMedia.heroThumbnailHeight === "number"
1931
2047
  ? heroMedia.heroThumbnailHeight
1932
2048
  : undefined;
2049
+ const navThumbnail = thumbUrl || fallbackThumbnail;
2050
+ const navThumbWidth =
2051
+ typeof thumbWidth === "number" ? thumbWidth : fallbackThumbWidth;
2052
+ const navThumbHeight =
2053
+ typeof thumbHeight === "number" ? thumbHeight : fallbackThumbHeight;
1933
2054
  const navRecord = navPlace.buildManifestNavPlaceRecord({
1934
2055
  manifest,
1935
2056
  slug,
@@ -1942,16 +2063,19 @@ async function buildIiifCollectionPages(CONFIG) {
1942
2063
  });
1943
2064
  if (navRecord) navPlaceRecords.push(navRecord);
1944
2065
 
2066
+ const recordThumbnail = navThumbnail;
2067
+ const recordThumbWidth = navThumbWidth;
2068
+ const recordThumbHeight = navThumbHeight;
1945
2069
  iiifRecords.push({
1946
2070
  id: String(manifest.id || id),
1947
2071
  title,
1948
2072
  href: pageHref,
1949
2073
  type: "work",
1950
- thumbnail: thumbUrl || undefined,
2074
+ thumbnail: recordThumbnail || undefined,
1951
2075
  thumbnailWidth:
1952
- typeof thumbWidth === "number" ? thumbWidth : undefined,
2076
+ typeof recordThumbWidth === "number" ? recordThumbWidth : undefined,
1953
2077
  thumbnailHeight:
1954
- typeof thumbHeight === "number" ? thumbHeight : undefined,
2078
+ typeof recordThumbHeight === "number" ? recordThumbHeight : undefined,
1955
2079
  searchMetadataValues:
1956
2080
  metadataValues && metadataValues.length
1957
2081
  ? metadataValues
package/lib/common.js CHANGED
@@ -12,6 +12,7 @@ const { readBasePath, withBasePath } = require('./base-path');
12
12
 
13
13
  const BASE_PATH = readBasePath();
14
14
  let cachedAppearance = null;
15
+ let cachedAccent = null;
15
16
 
16
17
  function resolveThemeAppearance() {
17
18
  if (cachedAppearance) return cachedAppearance;
@@ -29,6 +30,21 @@ function resolveThemeAppearance() {
29
30
  return cachedAppearance;
30
31
  }
31
32
 
33
+ function resolveThemeAccent() {
34
+ if (cachedAccent) return cachedAccent;
35
+ cachedAccent = 'indigo';
36
+ try {
37
+ const { loadCanopyTheme } = require('@canopy-iiif/app/ui/theme');
38
+ if (typeof loadCanopyTheme === 'function') {
39
+ const theme = loadCanopyTheme();
40
+ const accent = theme && theme.accent && theme.accent.name ? String(theme.accent.name) : '';
41
+ const normalized = accent.trim().toLowerCase();
42
+ if (normalized) cachedAccent = normalized;
43
+ }
44
+ } catch (_) {}
45
+ return cachedAccent;
46
+ }
47
+
32
48
  function readYamlConfigBaseUrl() {
33
49
  try {
34
50
  const y = require('js-yaml');
@@ -100,12 +116,16 @@ function htmlShell({ title, body, cssHref, scriptHref, headExtra, bodyClass }) {
100
116
  const extra = headExtra ? String(headExtra) : '';
101
117
  const cssTag = cssHref ? `<link rel="stylesheet" href="${cssHref}">` : '';
102
118
  const appearance = resolveThemeAppearance();
103
- const htmlClass = appearance === 'dark' ? ' class="dark"' : '';
119
+ const accent = resolveThemeAccent();
120
+ const htmlAttrs = [];
121
+ if (appearance === 'dark') htmlAttrs.push('class="dark"');
122
+ htmlAttrs.push(`data-accent="${accent || 'indigo'}"`);
123
+ const htmlAttr = htmlAttrs.length ? ` ${htmlAttrs.join(' ')}` : '';
104
124
  const hasCustomTitle = /<title\b/i.test(extra);
105
125
  const titleTag = hasCustomTitle ? '' : `<title>${title}</title>`;
106
126
  const bodyClassName = normalizeClassList(bodyClass);
107
127
  const bodyAttr = bodyClassName ? ` class="${bodyClassName}"` : '';
108
- 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${bodyAttr}>${body}</body></html>`;
128
+ return `<!doctype html><html lang="en"${htmlAttr}><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/>${titleTag}${extra}${cssTag}${scriptTag}</head><body${bodyAttr}>${body}</body></html>`;
109
129
  }
110
130
 
111
131
  function withBase(href) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.4.14",
3
+ "version": "1.4.16",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
@@ -22,8 +22,7 @@
22
22
 
23
23
  &:hover,
24
24
  &:focus-visible {
25
- background-color: var(--color-accent-800);
26
- color: var(--color-gray-50);
25
+ background-color: var(--color-accent-600);
27
26
  }
28
27
  }
29
28
 
@@ -42,10 +41,36 @@
42
41
  );
43
42
  background-color: color-mix(
44
43
  in srgb,
45
- var(--color-accent-200) 25%,
44
+ var(--color-accent-300) 25%,
46
45
  transparent
47
46
  );
48
- color: var(--color-accent-700);
47
+ color: var(--color-accent-900);
48
+ }
49
+ }
50
+ }
51
+
52
+ html[data-accent="amber"],
53
+ html[data-accent="yellow"],
54
+ html[data-accent="lime"],
55
+ html[data-accent="mint"],
56
+ html[data-accent="sky"] {
57
+ .canopy-button--primary {
58
+ color: var(--color-gray-900);
59
+ }
60
+ }
61
+
62
+ html.dark {
63
+ .canopy-button--primary {
64
+ color: var(--color-gray-900);
65
+ }
66
+
67
+ &[data-accent="amber"],
68
+ &[data-accent="yellow"],
69
+ &[data-accent="lime"],
70
+ &[data-accent="mint"],
71
+ &[data-accent="sky"] {
72
+ .canopy-button--primary {
73
+ color: var(--color-gray-50);
49
74
  }
50
75
  }
51
76
  }
@@ -19,7 +19,7 @@
19
19
  position: relative;
20
20
  width: 100%;
21
21
  padding-bottom: var(--canopy-card-padding);
22
- background-color: rgb(229 231 235); /* slate-200 */
22
+ background-color: var(--color-gray-100);
23
23
  overflow: hidden;
24
24
 
25
25
  > img {
@@ -164,8 +164,7 @@ section[data-footnotes] ul li,
164
164
  box-shadow: 0 18px 32px -22px rgba(15, 23, 42, 0.55);
165
165
  }
166
166
  .canopy-button--primary:hover, .canopy-button--primary:focus-visible {
167
- background-color: var(--color-accent-800);
168
- color: var(--color-gray-50);
167
+ background-color: var(--color-accent-600);
169
168
  }
170
169
  .canopy-button--secondary {
171
170
  border: 1px solid color-mix(in srgb, var(--color-gray-300) 60%, transparent);
@@ -174,8 +173,21 @@ section[data-footnotes] ul li,
174
173
  }
175
174
  .canopy-button--secondary:hover, .canopy-button--secondary:focus-visible {
176
175
  border-color: color-mix(in srgb, var(--color-accent-400) 65%, transparent);
177
- background-color: color-mix(in srgb, var(--color-accent-200) 25%, transparent);
178
- color: var(--color-accent-700);
176
+ background-color: color-mix(in srgb, var(--color-accent-300) 25%, transparent);
177
+ color: var(--color-accent-900);
178
+ }
179
+ html[data-accent=amber] .canopy-button--primary,
180
+ html[data-accent=yellow] .canopy-button--primary,
181
+ html[data-accent=lime] .canopy-button--primary,
182
+ html[data-accent=mint] .canopy-button--primary,
183
+ html[data-accent=sky] .canopy-button--primary {
184
+ color: var(--color-gray-900);
185
+ }
186
+ html.dark .canopy-button--primary {
187
+ color: var(--color-gray-900);
188
+ }
189
+ html.dark[data-accent=amber] .canopy-button--primary, html.dark[data-accent=yellow] .canopy-button--primary, html.dark[data-accent=lime] .canopy-button--primary, html.dark[data-accent=mint] .canopy-button--primary, html.dark[data-accent=sky] .canopy-button--primary {
190
+ color: var(--color-gray-50);
179
191
  }
180
192
  .canopy-button-group {
181
193
  display: flex;
@@ -231,7 +243,7 @@ section[data-footnotes] ul li,
231
243
  position: relative;
232
244
  width: 100%;
233
245
  padding-bottom: var(--canopy-card-padding);
234
- background-color: rgb(229, 231, 235); /* slate-200 */
246
+ background-color: var(--color-gray-100);
235
247
  overflow: hidden;
236
248
  }
237
249
  .canopy-card .canopy-card-media > img {