@canopy-iiif/app 0.7.0 → 0.7.2

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
@@ -88,12 +88,12 @@ async function readJsonFromUri(uri, options = { log: false }) {
88
88
  if (options && options.log) {
89
89
  try {
90
90
  if (res && res.ok) {
91
- logLine(`✓ ${String(uri)} ${res.status}`, "yellow", {
91
+ logLine(`↓ ${String(uri)} ${res.status}`, "yellow", {
92
92
  bright: true,
93
93
  });
94
94
  } else {
95
95
  const code = res ? res.status : "ERR";
96
- logLine(`✗ ${String(uri)} ${code}`, "red", { bright: true });
96
+ logLine(`⊘ ${String(uri)} ${code}`, "red", { bright: true });
97
97
  }
98
98
  } catch (_) {}
99
99
  }
@@ -459,7 +459,7 @@ async function saveCachedCollection(collection, id, parentId) {
459
459
  try {
460
460
  if (process.env.CANOPY_IIIF_DEBUG === "1") {
461
461
  const { logLine } = require("./log");
462
- logLine(`IIIF: saved collection ${slug}.json`, "cyan", { dim: true });
462
+ logLine(`IIIF: saved collection ${slug}.json`, "cyan", { dim: true });
463
463
  }
464
464
  } catch (_) {}
465
465
  index.byId = Array.isArray(index.byId) ? index.byId : [];
@@ -502,7 +502,7 @@ async function buildIiifCollectionPages(CONFIG) {
502
502
  if (!collectionUri) return { searchRecords: [] };
503
503
 
504
504
  // Fetch top-level collection
505
- logLine("IIIF: Fetching collection...", "blue", { bright: true });
505
+ logLine("• Traversing IIIF Collection(s)", "blue", { dim: true });
506
506
  const root = await readJsonFromUri(collectionUri, { log: true });
507
507
  if (!root) {
508
508
  logLine("IIIF: Failed to fetch collection", "red");
@@ -573,7 +573,16 @@ async function buildIiifCollectionPages(CONFIG) {
573
573
  )
574
574
  );
575
575
  const chunks = Math.ceil(tasks.length / chunkSize);
576
- const searchRecords = [];
576
+ // Summary before processing chunks
577
+ try {
578
+ const collectionsCount = visitedCollections.size || 0;
579
+ logLine(
580
+ `• Fetching ${tasks.length} Manifest(s) in ${chunks} chunk(s) across ${collectionsCount} Collection(s)`,
581
+ "blue",
582
+ { dim: true }
583
+ );
584
+ } catch (_) {}
585
+ const iiifRecords = [];
577
586
  const unsafeThumbs = !!(
578
587
  cfg &&
579
588
  cfg.iiif &&
@@ -619,7 +628,7 @@ async function buildIiifCollectionPages(CONFIG) {
619
628
 
620
629
  for (let ci = 0; ci < chunks; ci++) {
621
630
  const chunk = tasks.slice(ci * chunkSize, (ci + 1) * chunkSize);
622
- logLine(`\nChunk (${ci + 1}/${chunks})`, "white", { dim: true });
631
+ logLine(`• Chunk ${ci + 1}/${chunks}`, "blue", { dim: true });
623
632
 
624
633
  const concurrency = Math.max(
625
634
  1,
@@ -655,14 +664,14 @@ async function buildIiifCollectionPages(CONFIG) {
655
664
  let manifest = await loadCachedManifestById(id);
656
665
  const lns = [];
657
666
  if (manifest) {
658
- lns.push([`✓ ${String(id)} Cached`, "yellow"]);
667
+ lns.push([`↓ ${String(id)} Cached`, "yellow"]);
659
668
  } else if (/^https?:\/\//i.test(String(id || ""))) {
660
669
  try {
661
670
  const res = await fetch(String(id), {
662
671
  headers: { Accept: "application/json" },
663
672
  }).catch(() => null);
664
673
  if (res && res.ok) {
665
- lns.push([`✓ ${String(id)} ${res.status}`, "yellow"]);
674
+ lns.push([`↓ ${String(id)} ${res.status}`, "yellow"]);
666
675
  const remote = await res.json();
667
676
  const norm = await normalizeToV3(remote);
668
677
  manifest = norm;
@@ -673,20 +682,20 @@ async function buildIiifCollectionPages(CONFIG) {
673
682
  );
674
683
  } else {
675
684
  lns.push([
676
- `✗ ${String(id)} ${res ? res.status : "ERR"}`,
685
+ `⊘ ${String(id)} ${res ? res.status : "ERR"}`,
677
686
  "red",
678
687
  ]);
679
688
  continue;
680
689
  }
681
690
  } catch (e) {
682
- lns.push([`✗ ${String(id)} ERR`, "red"]);
691
+ lns.push([`⊘ ${String(id)} ERR`, "red"]);
683
692
  continue;
684
693
  }
685
694
  } else if (/^file:\/\//i.test(String(id || ""))) {
686
695
  try {
687
696
  const local = await readJsonFromUri(String(id), { log: false });
688
697
  if (!local) {
689
- lns.push([`✗ ${String(id)} ERR`, "red"]);
698
+ lns.push([`⊘ ${String(id)} ERR`, "red"]);
690
699
  continue;
691
700
  }
692
701
  const norm = await normalizeToV3(local);
@@ -696,13 +705,13 @@ async function buildIiifCollectionPages(CONFIG) {
696
705
  String(id),
697
706
  String(it.parent || "")
698
707
  );
699
- lns.push([`✓ ${String(id)} Cached`, "yellow"]);
708
+ lns.push([`↓ ${String(id)} Cached`, "yellow"]);
700
709
  } catch (_) {
701
- lns.push([`✗ ${String(id)} ERR`, "red"]);
710
+ lns.push([`⊘ ${String(id)} ERR`, "red"]);
702
711
  continue;
703
712
  }
704
713
  } else {
705
- lns.push([`✗ ${String(id)} SKIP`, "red"]);
714
+ lns.push([`⊘ ${String(id)} SKIP`, "red"]);
706
715
  continue;
707
716
  }
708
717
  if (!manifest) continue;
@@ -903,7 +912,7 @@ async function buildIiifCollectionPages(CONFIG) {
903
912
  } catch (_) {}
904
913
  await fsp.writeFile(outPath, html, "utf8");
905
914
  lns.push([
906
- `✓ Created ${path.relative(process.cwd(), outPath)}`,
915
+ `✔ Created ${path.relative(process.cwd(), outPath)}`,
907
916
  "green",
908
917
  ]);
909
918
  let thumbUrl = "";
@@ -935,7 +944,7 @@ async function buildIiifCollectionPages(CONFIG) {
935
944
  }
936
945
  }
937
946
  } catch (_) {}
938
- searchRecords.push({
947
+ iiifRecords.push({
939
948
  id: String(manifest.id || id),
940
949
  title,
941
950
  href: href.split(path.sep).join("/"),
@@ -962,7 +971,7 @@ async function buildIiifCollectionPages(CONFIG) {
962
971
  );
963
972
  await Promise.all(workers);
964
973
  }
965
- return { searchRecords };
974
+ return { iiifRecords };
966
975
  }
967
976
 
968
977
  module.exports = {
package/lib/build/log.js CHANGED
@@ -1,64 +1,31 @@
1
- let charm = null;
2
- try {
3
- charm = require('charm')();
4
- } catch (_) {
5
- charm = null;
6
- }
7
-
8
- const ANSI = {
9
- reset: '\u001b[0m',
10
- bright: '\u001b[1m',
11
- dim: '\u001b[2m',
12
- underscore: '\u001b[4m',
13
- colors: {
14
- black: '\u001b[30m',
15
- red: '\u001b[31m',
16
- green: '\u001b[32m',
17
- yellow: '\u001b[33m',
18
- blue: '\u001b[34m',
19
- magenta: '\u001b[35m',
20
- cyan: '\u001b[36m',
21
- white: '\u001b[37m'
22
- }
23
- };
1
+ const charm = require("charm")();
2
+ charm.pipe(process.stdout);
24
3
 
25
- function log(string, color = 'blue', options = { bright: false, dim: false, underscore: false }) {
26
- if (charm) {
27
- const c = require('charm')();
28
- c.pipe(process.stdout);
29
- if (options?.bright) c.display('bright');
30
- if (options?.dim) c.display('dim');
31
- if (options?.underscore) c.display('underscore');
32
- c.foreground(color).write(String(string)).display('reset');
33
- c.end();
34
- } else {
35
- const parts = [];
36
- if (options?.bright) parts.push(ANSI.bright);
37
- if (options?.dim) parts.push(ANSI.dim);
38
- if (options?.underscore) parts.push(ANSI.underscore);
39
- const clr = ANSI.colors[color] || '';
40
- parts.push(clr);
41
- parts.push(String(string));
42
- parts.push(ANSI.reset);
43
- process.stdout.write(parts.join(''));
44
- }
4
+ function log(
5
+ string,
6
+ color = "blue",
7
+ options = { bright: false, dim: false, underscore: false }
8
+ ) {
9
+ if (options?.bright) charm.display("bright");
10
+ if (options?.dim) charm.display("dim");
11
+ if (options?.underscore) charm.display("underscore");
12
+ charm.foreground(color).write(String(string)).display("reset");
45
13
  }
46
14
 
47
- function logLine(string, color = 'blue', options) {
48
- log(string + '\n', color, options);
15
+ function logLine(string, color = "blue", options) {
16
+ log(string + "\n", color, options);
49
17
  }
50
18
 
51
19
  function logResponse(string, response, success = true) {
52
20
  if (success) {
53
- log('✓ ', 'yellow', { dim: true });
54
- log(String(string), 'yellow');
55
- log(` ${response?.status ?? ''}\n`, 'yellow', { dim: true });
21
+ log("↓ ", "yellow", { dim: true });
22
+ log(String(string), "yellow");
23
+ log(` ${response?.status ?? ""}\n`, "yellow", { dim: true });
56
24
  } else {
57
- log('✗ ', 'red', { dim: true });
58
- log(String(string), 'red');
59
- log(` ${response?.status ?? ''}\n`, 'red', { dim: true });
25
+ log("⊘ ", "red", { dim: true });
26
+ log(String(string), "red");
27
+ log(` ${response?.status ?? ""}\n`, "red", { dim: true });
60
28
  }
61
29
  }
62
30
 
63
31
  module.exports = { log, logLine, logResponse };
64
-
@@ -0,0 +1,61 @@
1
+ const { fs, path, OUT_DIR } = require('../common');
2
+ const search = require('../search/search');
3
+ const runtimes = require('./runtimes');
4
+ const { generateFacets } = require('./facets');
5
+ const { logLine } = require('./log');
6
+
7
+ /**
8
+ * Ensure the search page and its runtime exist on first build.
9
+ * If `search.html` is missing, write an empty index, bundle the initial runtime,
10
+ * and build the page so subsequent steps can update artifacts incrementally.
11
+ */
12
+ async function ensureSearchInitialized() {
13
+ const searchPath = path.join(OUT_DIR, 'search.html');
14
+ const needCreatePage = !fs.existsSync(searchPath);
15
+ if (!needCreatePage) return;
16
+ try { logLine('• Preparing search (initial)...', 'blue', { bright: true }); } catch (_) {}
17
+ // Build result item template (compat no-op) so it can be inlined if needed
18
+ try { await search.ensureResultTemplate(); } catch (_) {}
19
+ try { logLine(' - Writing empty index...', 'blue'); } catch (_) {}
20
+ await search.writeSearchIndex([]);
21
+ try { logLine(' - Writing runtime...', 'blue'); } catch (_) {}
22
+ await runtimes.prepareSearchRuntime(process.env.CANOPY_BUNDLE_TIMEOUT || 10000, 'initial');
23
+ try { logLine(' - Building search.html...', 'blue'); } catch (_) {}
24
+ await search.buildSearchPage();
25
+ try { logLine('✓ Created search page', 'cyan'); } catch (_) {}
26
+ }
27
+
28
+ /**
29
+ * After the search index is built, finalize related artifacts:
30
+ * - Generate facets for IIIF works
31
+ * - Re-bundle the search runtime (final)
32
+ * - Rebuild result template and the search page
33
+ * - Log a concise breakdown by type
34
+ */
35
+ async function finalizeSearch(combinedRecords) {
36
+ const combined = Array.isArray(combinedRecords) ? combinedRecords : [];
37
+ await generateFacets(combined);
38
+ try { logLine('• Writing search runtime (final)...', 'blue', { bright: true }); } catch (_) {}
39
+ await runtimes.prepareSearchRuntime(process.env.CANOPY_BUNDLE_TIMEOUT || 10000, 'final');
40
+ try { await search.ensureResultTemplate(); } catch (_) {}
41
+ try { logLine('• Updating search.html...', 'blue'); } catch (_) {}
42
+ await search.buildSearchPage();
43
+ try { logLine('✓ Search page updated', 'cyan'); } catch (_) {}
44
+
45
+ // Itemize counts by type for a clearer summary
46
+ try {
47
+ const counts = new Map();
48
+ for (const r of combined) {
49
+ const t = String((r && r.type) || 'page').toLowerCase();
50
+ counts.set(t, (counts.get(t) || 0) + 1);
51
+ }
52
+ const parts = Array.from(counts.entries())
53
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
54
+ .map(([t, n]) => `${t}: ${n}`);
55
+ const breakdown = parts.length ? `: ${parts.join(', ')}` : '';
56
+ logLine(`✓ Search index: ${combined.length} total records${breakdown}`, 'cyan');
57
+ } catch (_) {}
58
+ }
59
+
60
+ module.exports = { ensureSearchInitialized, finalizeSearch };
61
+
@@ -1,64 +1,86 @@
1
- const { fs, fsp, path, OUT_DIR, CONTENT_DIR, ensureDirSync } = require('../common');
1
+ const {
2
+ fs,
3
+ fsp,
4
+ path,
5
+ OUT_DIR,
6
+ CONTENT_DIR,
7
+ ensureDirSync,
8
+ } = require("../common");
2
9
 
3
10
  async function ensureStyles() {
4
- const stylesDir = path.join(OUT_DIR, 'styles');
5
- const dest = path.join(stylesDir, 'styles.css');
6
- const customContentCss = path.join(CONTENT_DIR, '_styles.css');
7
- const appStylesDir = path.join(process.cwd(), 'app', 'styles');
8
- const customAppCss = path.join(appStylesDir, 'index.css');
11
+ const stylesDir = path.join(OUT_DIR, "styles");
12
+ const dest = path.join(stylesDir, "styles.css");
13
+ const customContentCss = path.join(CONTENT_DIR, "_styles.css");
14
+ const appStylesDir = path.join(process.cwd(), "app", "styles");
15
+ const customAppCss = path.join(appStylesDir, "index.css");
9
16
  ensureDirSync(stylesDir);
10
17
 
11
18
  const root = process.cwd();
12
19
  const twConfigsRoot = [
13
- path.join(root, 'tailwind.config.js'),
14
- path.join(root, 'tailwind.config.cjs'),
15
- path.join(root, 'tailwind.config.mjs'),
16
- path.join(root, 'tailwind.config.ts'),
20
+ path.join(root, "tailwind.config.js"),
21
+ path.join(root, "tailwind.config.cjs"),
22
+ path.join(root, "tailwind.config.mjs"),
23
+ path.join(root, "tailwind.config.ts"),
17
24
  ];
18
25
  const twConfigsApp = [
19
- path.join(appStylesDir, 'tailwind.config.js'),
20
- path.join(appStylesDir, 'tailwind.config.cjs'),
21
- path.join(appStylesDir, 'tailwind.config.mjs'),
22
- path.join(appStylesDir, 'tailwind.config.ts'),
26
+ path.join(appStylesDir, "tailwind.config.js"),
27
+ path.join(appStylesDir, "tailwind.config.cjs"),
28
+ path.join(appStylesDir, "tailwind.config.mjs"),
29
+ path.join(appStylesDir, "tailwind.config.ts"),
23
30
  ];
24
31
  let configPath = [...twConfigsApp, ...twConfigsRoot].find((p) => {
25
- try { return fs.existsSync(p); } catch (_) { return false; }
32
+ try {
33
+ return fs.existsSync(p);
34
+ } catch (_) {
35
+ return false;
36
+ }
26
37
  });
27
38
  if (!configPath) {
28
39
  try {
29
- const { CACHE_DIR } = require('../common');
30
- const genDir = path.join(CACHE_DIR, 'tailwind');
40
+ const { CACHE_DIR } = require("../common");
41
+ const genDir = path.join(CACHE_DIR, "tailwind");
31
42
  ensureDirSync(genDir);
32
- const genCfg = path.join(genDir, 'tailwind.config.js');
43
+ const genCfg = path.join(genDir, "tailwind.config.js");
33
44
  const cfg = `module.exports = {\n presets: [require('@canopy-iiif/app/ui/canopy-iiif-preset')],\n content: [\n './content/**/*.{mdx,html}',\n './site/**/*.html',\n './site/**/*.js',\n './packages/app/ui/**/*.{js,jsx,ts,tsx}',\n './packages/app/lib/iiif/components/**/*.{js,jsx}',\n ],\n theme: { extend: {} },\n plugins: [require('@canopy-iiif/app/ui/canopy-iiif-plugin')],\n};\n`;
34
- fs.writeFileSync(genCfg, cfg, 'utf8');
45
+ fs.writeFileSync(genCfg, cfg, "utf8");
35
46
  configPath = genCfg;
36
- } catch (_) { configPath = null; }
47
+ } catch (_) {
48
+ configPath = null;
49
+ }
37
50
  }
38
51
 
39
52
  const inputCss = fs.existsSync(customAppCss)
40
53
  ? customAppCss
41
- : (fs.existsSync(customContentCss) ? customContentCss : null);
54
+ : fs.existsSync(customContentCss)
55
+ ? customContentCss
56
+ : null;
42
57
 
43
58
  let generatedInput = null;
44
59
  if (!inputCss) {
45
60
  try {
46
- const { CACHE_DIR } = require('../common');
47
- const genDir = path.join(CACHE_DIR, 'tailwind');
61
+ const { CACHE_DIR } = require("../common");
62
+ const genDir = path.join(CACHE_DIR, "tailwind");
48
63
  ensureDirSync(genDir);
49
- generatedInput = path.join(genDir, 'index.css');
64
+ generatedInput = path.join(genDir, "index.css");
50
65
  const css = `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n`;
51
- fs.writeFileSync(generatedInput, css, 'utf8');
52
- } catch (_) { generatedInput = null; }
66
+ fs.writeFileSync(generatedInput, css, "utf8");
67
+ } catch (_) {
68
+ generatedInput = null;
69
+ }
53
70
  }
54
71
 
55
72
  function resolveTailwindCli() {
56
73
  try {
57
- const cliJs = require.resolve('tailwindcss/lib/cli.js');
74
+ const cliJs = require.resolve("tailwindcss/lib/cli.js");
58
75
  return { cmd: process.execPath, args: [cliJs] };
59
76
  } catch (_) {}
60
77
  try {
61
- const localBin = path.join(process.cwd(), 'node_modules', '.bin', process.platform === 'win32' ? 'tailwindcss.cmd' : 'tailwindcss');
78
+ const localBin = path.join(
79
+ process.cwd(),
80
+ "node_modules",
81
+ ".bin",
82
+ process.platform === "win32" ? "tailwindcss.cmd" : "tailwindcss"
83
+ );
62
84
  if (fs.existsSync(localBin)) return { cmd: localBin, args: [] };
63
85
  } catch (_) {}
64
86
  return null;
@@ -67,33 +89,53 @@ async function ensureStyles() {
67
89
  try {
68
90
  const cli = resolveTailwindCli();
69
91
  if (!cli) return false;
70
- const { spawnSync } = require('child_process');
71
- const args = ['-i', input, '-o', output];
72
- if (config) args.push('-c', config);
73
- if (minify) args.push('--minify');
74
- const res = spawnSync(cli.cmd, [...cli.args, ...args], { stdio: 'inherit', env: { ...process.env, BROWSERSLIST_IGNORE_OLD_DATA: '1' } });
92
+ const { spawnSync } = require("child_process");
93
+ const args = ["-i", input, "-o", output];
94
+ if (config) args.push("-c", config);
95
+ if (minify) args.push("--minify");
96
+ const res = spawnSync(cli.cmd, [...cli.args, ...args], {
97
+ stdio: "inherit",
98
+ env: { ...process.env, BROWSERSLIST_IGNORE_OLD_DATA: "1" },
99
+ });
75
100
  return !!res && res.status === 0;
76
- } catch (_) { return false; }
101
+ } catch (_) {
102
+ return false;
103
+ }
77
104
  }
78
105
 
79
106
  if (configPath && (inputCss || generatedInput)) {
80
- const ok = buildTailwindCli({ input: inputCss || generatedInput, output: dest, config: configPath, minify: true });
107
+ const ok = buildTailwindCli({
108
+ input: inputCss || generatedInput,
109
+ output: dest,
110
+ config: configPath,
111
+ minify: true,
112
+ });
81
113
  if (ok) return; // Tailwind compiled CSS
82
114
  }
83
115
 
84
116
  function isTailwindSource(p) {
85
- try { const s = fs.readFileSync(p, 'utf8'); return /@tailwind\s+(base|components|utilities)/.test(s); } catch (_) { return false; }
117
+ try {
118
+ const s = fs.readFileSync(p, "utf8");
119
+ return /@tailwind\s+(base|components|utilities)/.test(s);
120
+ } catch (_) {
121
+ return false;
122
+ }
86
123
  }
87
124
  if (fs.existsSync(customAppCss)) {
88
- if (!isTailwindSource(customAppCss)) { await fsp.copyFile(customAppCss, dest); return; }
125
+ if (!isTailwindSource(customAppCss)) {
126
+ await fsp.copyFile(customAppCss, dest);
127
+ return;
128
+ }
89
129
  }
90
130
  if (fs.existsSync(customContentCss)) {
91
- if (!isTailwindSource(customContentCss)) { await fsp.copyFile(customContentCss, dest); return; }
131
+ if (!isTailwindSource(customContentCss)) {
132
+ await fsp.copyFile(customContentCss, dest);
133
+ return;
134
+ }
92
135
  }
93
136
 
94
137
  const css = `:root{--max-w:760px;--muted:#6b7280}*{box-sizing:border-box}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Helvetica,Arial,sans-serif;max-width:var(--max-w);margin:2rem auto;padding:0 1rem;line-height:1.6}a{color:#2563eb;text-decoration:none}a:hover{text-decoration:underline}.site-header,.site-footer{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:1rem 0;border-bottom:1px solid #e5e7eb}.site-footer{border-bottom:0;border-top:1px solid #e5e7eb;color:var(--muted)}.brand{font-weight:600}.content pre{background:#f6f8fa;padding:1rem;overflow:auto}.content code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;background:#f6f8fa;padding:.1rem .3rem;border-radius:4px}.tabs{display:flex;gap:.5rem;align-items:center;border-bottom:1px solid #e5e7eb;margin:.5rem 0}.tab{background:none;border:0;color:#374151;padding:.25rem .5rem;border-radius:.375rem;cursor:pointer}.tab:hover{color:#111827}.tab-active{color:#2563eb;border:1px solid #e5e7eb;border-bottom:0;background:#fff}.masonry{column-gap:1rem;column-count:1}@media(min-width:768px){.masonry{column-count:2}}@media(min-width:1024px){.masonry{column-count:3}}.masonry>*{break-inside:avoid;margin-bottom:1rem;display:block}[data-grid-variant=masonry]{column-gap:var(--grid-gap,1rem);column-count:var(--cols-base,1)}@media(min-width:768px){[data-grid-variant=masonry]{column-count:var(--cols-md,2)}}@media(min-width:1024px){[data-grid-variant=masonry]{column-count:var(--cols-lg,3)}}[data-grid-variant=masonry]>*{break-inside:avoid;margin-bottom:var(--grid-gap,1rem);display:block}[data-grid-variant=grid]{display:grid;grid-template-columns:repeat(var(--cols-base,1),minmax(0,1fr));gap:var(--grid-gap,1rem)}@media(min-width:768px){[data-grid-variant=grid]{grid-template-columns:repeat(var(--cols-md,2),minmax(0,1fr))}}@media(min-width:1024px){[data-grid-variant=grid]{grid-template-columns:repeat(var(--cols-lg,3),minmax(0,1fr))}}`;
95
- await fsp.writeFile(dest, css, 'utf8');
138
+ await fsp.writeFile(dest, css, "utf8");
96
139
  }
97
140
 
98
141
  module.exports = { ensureStyles };
99
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
@@ -29,6 +29,7 @@
29
29
  "@iiif/helpers": "^1.4.0",
30
30
  "@mdx-js/mdx": "^3.1.0",
31
31
  "@mdx-js/react": "^3.1.0",
32
+ "charm": "^1.0.2",
32
33
  "cmdk": "^1.1.1",
33
34
  "js-yaml": "^4.1.0",
34
35
  "react-masonry-css": "^1.0.16",