@canopy-iiif/app 1.6.20 → 1.6.22

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/dev.js CHANGED
@@ -15,6 +15,10 @@ const {
15
15
  ASSETS_DIR,
16
16
  ensureDirSync,
17
17
  } = require("../common");
18
+ const {
19
+ injectThemeTokens,
20
+ stripTailwindThemeLayer,
21
+ } = require("./styles");
18
22
  const APP_COMPONENTS_DIR = path.join(process.cwd(), "app", "components");
19
23
 
20
24
  function resolveTailwindCli() {
@@ -60,14 +64,6 @@ const HAS_APP_WORKSPACE = (() => {
60
64
  return false;
61
65
  }
62
66
  })();
63
-
64
- function stripTailwindThemeLayer(targetPath) {
65
- try {
66
- const raw = fs.readFileSync(targetPath, "utf8");
67
- const cleaned = raw.replace(/@layer theme\{[\s\S]*?\}(?=@layer|$)/g, "");
68
- if (cleaned !== raw) fs.writeFileSync(targetPath, cleaned, "utf8");
69
- } catch (_) {}
70
- }
71
67
  let pendingModuleReload = false;
72
68
  let building = false;
73
69
  let buildAgain = false;
@@ -992,6 +988,7 @@ async function dev() {
992
988
  }
993
989
  throw new Error("[tailwind] Initial Tailwind build failed.");
994
990
  }
991
+ injectThemeTokens(outputCss);
995
992
  stripTailwindThemeLayer(outputCss);
996
993
  console.log(
997
994
  `[tailwind] initial build ok (${fileSizeKb(outputCss)} KB) →`,
@@ -1017,6 +1014,7 @@ async function dev() {
1017
1014
  cssWatcherAttached = true;
1018
1015
  try {
1019
1016
  fs.watch(outputCss, { persistent: false }, () => {
1017
+ injectThemeTokens(outputCss);
1020
1018
  stripTailwindThemeLayer(outputCss);
1021
1019
  if (!unmuted) {
1022
1020
  unmuted = true;
@@ -1044,6 +1042,7 @@ async function dev() {
1044
1042
  }
1045
1043
  throw new Error("[tailwind] On-demand Tailwind compile failed.");
1046
1044
  }
1045
+ injectThemeTokens(outputCss);
1047
1046
  stripTailwindThemeLayer(outputCss);
1048
1047
  console.log(
1049
1048
  `[tailwind] compiled (${fileSizeKb(outputCss)} KB) →`,
package/lib/build/iiif.js CHANGED
@@ -15,6 +15,7 @@ const {
15
15
  canopyBodyClassForType,
16
16
  readSiteMetadata,
17
17
  readPrimaryNavigation,
18
+ withBase,
18
19
  } = require("../common");
19
20
  const {resolveCanopyConfigPath} = require("../config-path");
20
21
  const mdx = require("./mdx");
@@ -2033,15 +2034,10 @@ async function buildIiifCollectionPages(CONFIG) {
2033
2034
  try {
2034
2035
  let components = {};
2035
2036
  try {
2036
- components = await import("@canopy-iiif/app/ui/server");
2037
+ components = await mdx.loadUiComponents();
2037
2038
  } catch (_) {
2038
- try {
2039
- components = await import("@canopy-iiif/app/ui");
2040
- } catch (_) {
2041
- components = {};
2042
- }
2039
+ components = {};
2043
2040
  }
2044
- const {withBase} = require("../common");
2045
2041
  const Anchor = function A(props) {
2046
2042
  let {href = "", ...rest} = props || {};
2047
2043
  href = withBase(href);
@@ -2056,8 +2052,7 @@ async function buildIiifCollectionPages(CONFIG) {
2056
2052
  } catch (_) {
2057
2053
  MDXProvider = null;
2058
2054
  }
2059
- const {loadAppWrapper} = require("./mdx");
2060
- const app = await loadAppWrapper();
2055
+ const app = await mdx.loadAppWrapper();
2061
2056
 
2062
2057
  let heroMedia = null;
2063
2058
  try {
@@ -2174,6 +2169,9 @@ async function buildIiifCollectionPages(CONFIG) {
2174
2169
  const needsTimeline = body.includes("data-canopy-timeline");
2175
2170
  const needsMap = body.includes("data-canopy-map");
2176
2171
  const needsSearchForm = body.includes("data-canopy-search-form");
2172
+ const needsCustomClients = body.includes(
2173
+ "data-canopy-client-component",
2174
+ );
2177
2175
  const needsHydrate =
2178
2176
  body.includes("data-canopy-hydrate") ||
2179
2177
  needsHydrateViewer ||
@@ -2213,11 +2211,39 @@ async function buildIiifCollectionPages(CONFIG) {
2213
2211
  const searchFormRel = needsSearchForm
2214
2212
  ? relativeRuntimeScript(outPath, "canopy-search-form.js", true)
2215
2213
  : null;
2214
+ let customClientRel = null;
2215
+ if (needsCustomClients) {
2216
+ try {
2217
+ await mdx.ensureCustomClientRuntime();
2218
+ const customAbs = path.join(
2219
+ OUT_DIR,
2220
+ "scripts",
2221
+ "canopy-custom-components.js",
2222
+ );
2223
+ let rel = path
2224
+ .relative(path.dirname(outPath), customAbs)
2225
+ .split(path.sep)
2226
+ .join("/");
2227
+ try {
2228
+ const st = fs.statSync(customAbs);
2229
+ rel += `?v=${Math.floor(st.mtimeMs || Date.now())}`;
2230
+ } catch (_) {}
2231
+ customClientRel = rel;
2232
+ } catch (e) {
2233
+ try {
2234
+ console.warn(
2235
+ "[canopy][mdx] failed to build custom client runtime:",
2236
+ e && e.message ? e.message : e,
2237
+ );
2238
+ } catch (_) {}
2239
+ }
2240
+ }
2216
2241
 
2217
2242
  const moduleScriptRels = [];
2218
2243
  if (viewerRel) moduleScriptRels.push(viewerRel);
2219
2244
  if (sliderRel) moduleScriptRels.push(sliderRel);
2220
2245
  if (imageStoryRel) moduleScriptRels.push(imageStoryRel);
2246
+ if (customClientRel) moduleScriptRels.push(customClientRel);
2221
2247
  const primaryClassicScripts = [];
2222
2248
  if (heroRel) primaryClassicScripts.push(heroRel);
2223
2249
  if (relatedRel) primaryClassicScripts.push(relatedRel);
@@ -2238,7 +2264,8 @@ async function buildIiifCollectionPages(CONFIG) {
2238
2264
  needsHydrateViewer ||
2239
2265
  needsRelated ||
2240
2266
  needsTimeline ||
2241
- needsMap
2267
+ needsMap ||
2268
+ (customClientRel && needsCustomClients)
2242
2269
  );
2243
2270
  let vendorTag = "";
2244
2271
  if (needsReact) {
package/lib/build/mdx.js CHANGED
@@ -1904,6 +1904,7 @@ module.exports = {
1904
1904
  compileMdxToComponent,
1905
1905
  loadCustomLayout,
1906
1906
  loadAppWrapper,
1907
+ loadUiComponents,
1907
1908
  ensureClientRuntime,
1908
1909
  ensureSliderRuntime,
1909
1910
  ensureTimelineRuntime,
@@ -7,6 +7,49 @@ const {
7
7
  ensureDirSync,
8
8
  } = require("../common");
9
9
 
10
+ function resolveTailwindCli() {
11
+ const localBin = path.join(
12
+ process.cwd(),
13
+ "node_modules",
14
+ ".bin",
15
+ process.platform === "win32" ? "tailwindcss.cmd" : "tailwindcss"
16
+ );
17
+ if (fs.existsSync(localBin)) return {cmd: localBin, args: []};
18
+ return {cmd: "tailwindcss", args: []};
19
+ }
20
+
21
+ function injectThemeTokens(targetPath) {
22
+ try {
23
+ const {loadCanopyTheme} = require("@canopy-iiif/app/ui/theme");
24
+ const theme = loadCanopyTheme();
25
+ const themeCss = theme && theme.css ? theme.css.trim() : "";
26
+ if (!themeCss) return;
27
+
28
+ let existing = "";
29
+ try {
30
+ existing = fs.readFileSync(targetPath, "utf8");
31
+ } catch (_) {}
32
+
33
+ const marker = "/* canopy-theme */";
34
+ const markerEnd = "/* canopy-theme:end */";
35
+ const markerRegex = new RegExp(`${marker}[\\s\\S]*?${markerEnd}\\n?`, "g");
36
+ const sanitized = existing.replace(markerRegex, "").replace(/\s+$/, "");
37
+
38
+ const themeBlock = `${marker}\n${themeCss}\n${markerEnd}\n`;
39
+ const separator = sanitized ? "\n" : "";
40
+ const next = `${sanitized}${separator}${themeBlock}`;
41
+ fs.writeFileSync(targetPath, next, "utf8");
42
+ } catch (_) {}
43
+ }
44
+
45
+ function stripTailwindThemeLayer(targetPath) {
46
+ try {
47
+ const raw = fs.readFileSync(targetPath, "utf8");
48
+ const cleaned = raw.replace(/@layer theme\{[\s\S]*?\}(?=@layer|$)/g, "");
49
+ if (cleaned !== raw) fs.writeFileSync(targetPath, cleaned, "utf8");
50
+ } catch (_) {}
51
+ }
52
+
10
53
  async function ensureStyles() {
11
54
  const stylesDir = path.join(OUT_DIR, "styles");
12
55
  const dest = path.join(stylesDir, "styles.css");
@@ -69,16 +112,6 @@ async function ensureStyles() {
69
112
  }
70
113
  }
71
114
 
72
- function resolveTailwindCli() {
73
- const localBin = path.join(
74
- process.cwd(),
75
- "node_modules",
76
- ".bin",
77
- process.platform === "win32" ? "tailwindcss.cmd" : "tailwindcss"
78
- );
79
- if (fs.existsSync(localBin)) return { cmd: localBin, args: [] };
80
- return { cmd: 'tailwindcss', args: [] };
81
- }
82
115
  function buildTailwindCli({ input, output, config, minify = true }) {
83
116
  try {
84
117
  const cli = resolveTailwindCli();
@@ -97,38 +130,6 @@ async function ensureStyles() {
97
130
  }
98
131
  }
99
132
 
100
- function injectThemeTokens(targetPath) {
101
- try {
102
- const { loadCanopyTheme } = require("@canopy-iiif/app/ui/theme");
103
- const theme = loadCanopyTheme();
104
- const themeCss = theme && theme.css ? theme.css.trim() : "";
105
- if (!themeCss) return;
106
-
107
- let existing = "";
108
- try {
109
- existing = fs.readFileSync(targetPath, "utf8");
110
- } catch (_) {}
111
-
112
- const marker = "/* canopy-theme */";
113
- const markerEnd = "/* canopy-theme:end */";
114
- const markerRegex = new RegExp(`${marker}[\\s\\S]*?${markerEnd}\\n?`, "g");
115
- const sanitized = existing.replace(markerRegex, "").replace(/\s+$/, "");
116
-
117
- const themeBlock = `${marker}\n${themeCss}\n${markerEnd}\n`;
118
- const separator = sanitized ? "\n" : "";
119
- const next = `${sanitized}${separator}${themeBlock}`;
120
- fs.writeFileSync(targetPath, next, "utf8");
121
- } catch (_) {}
122
- }
123
-
124
- function stripTailwindThemeLayer(targetPath) {
125
- try {
126
- const raw = fs.readFileSync(targetPath, "utf8");
127
- const cleaned = raw.replace(/@layer theme\{[\s\S]*?\}(?=@layer|$)/g, "");
128
- if (cleaned !== raw) fs.writeFileSync(targetPath, cleaned, "utf8");
129
- } catch (_) {}
130
- }
131
-
132
133
  if (configPath && (inputCss || generatedInput)) {
133
134
  const ok = buildTailwindCli({
134
135
  input: inputCss || generatedInput,
@@ -174,4 +175,8 @@ async function ensureStyles() {
174
175
  stripTailwindThemeLayer(dest);
175
176
  }
176
177
 
177
- module.exports = { ensureStyles };
178
+ module.exports = {
179
+ ensureStyles,
180
+ injectThemeTokens,
181
+ stripTailwindThemeLayer,
182
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.6.20",
3
+ "version": "1.6.22",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
package/ui/dist/index.mjs CHANGED
@@ -2065,13 +2065,31 @@ async function mountImageStory(element, props = {}) {
2065
2065
  }
2066
2066
 
2067
2067
  // ui/src/iiif/ImageStory.jsx
2068
+ var DEFAULT_IMAGE_STORY_HEIGHT = 600;
2069
+ var NUMERIC_HEIGHT_PATTERN = /^[+-]?(?:\d+|\d*\.\d+)$/;
2070
+ function resolveContainerHeight(value) {
2071
+ if (typeof value === "number" && Number.isFinite(value)) {
2072
+ return `${value}px`;
2073
+ }
2074
+ if (typeof value === "string") {
2075
+ const trimmed = value.trim();
2076
+ if (!trimmed) {
2077
+ return `${DEFAULT_IMAGE_STORY_HEIGHT}px`;
2078
+ }
2079
+ if (NUMERIC_HEIGHT_PATTERN.test(trimmed)) {
2080
+ return `${trimmed}px`;
2081
+ }
2082
+ return trimmed;
2083
+ }
2084
+ return `${DEFAULT_IMAGE_STORY_HEIGHT}px`;
2085
+ }
2068
2086
  var ImageStory = (props = {}) => {
2069
2087
  const {
2070
2088
  iiifContent,
2071
2089
  disablePanAndZoom,
2072
2090
  pointOfInterestSvgUrl,
2073
2091
  viewerOptions,
2074
- height = 600,
2092
+ height = DEFAULT_IMAGE_STORY_HEIGHT,
2075
2093
  className,
2076
2094
  style,
2077
2095
  ...rest
@@ -2080,6 +2098,9 @@ var ImageStory = (props = {}) => {
2080
2098
  const resolvedClassName = useMemo2(() => {
2081
2099
  return ["canopy-image-story", className].filter(Boolean).join(" ");
2082
2100
  }, [className]);
2101
+ const resolvedHeight = useMemo2(() => {
2102
+ return resolveContainerHeight(height);
2103
+ }, [height]);
2083
2104
  const serializedProps = useMemo2(() => {
2084
2105
  return serializeImageStoryProps({
2085
2106
  iiifContent,
@@ -2125,7 +2146,7 @@ var ImageStory = (props = {}) => {
2125
2146
  "data-canopy-image-story": "1",
2126
2147
  style: {
2127
2148
  width: "100%",
2128
- height: typeof height === "number" ? `${height}px` : height,
2149
+ height: resolvedHeight,
2129
2150
  ...style || {}
2130
2151
  },
2131
2152
  ...rest