@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/build.js +37 -93
- package/lib/build/dev.js +493 -204
- package/lib/build/iiif.js +26 -17
- package/lib/build/log.js +19 -52
- package/lib/build/search-workflow.js +61 -0
- package/lib/build/styles.js +82 -40
- package/package.json +2 -1
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(
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
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([
|
|
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([
|
|
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
|
-
|
|
685
|
+
`⊘ ${String(id)} → ${res ? res.status : "ERR"}`,
|
|
677
686
|
"red",
|
|
678
687
|
]);
|
|
679
688
|
continue;
|
|
680
689
|
}
|
|
681
690
|
} catch (e) {
|
|
682
|
-
lns.push([
|
|
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([
|
|
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([
|
|
708
|
+
lns.push([`↓ ${String(id)} → Cached`, "yellow"]);
|
|
700
709
|
} catch (_) {
|
|
701
|
-
lns.push([
|
|
710
|
+
lns.push([`⊘ ${String(id)} → ERR`, "red"]);
|
|
702
711
|
continue;
|
|
703
712
|
}
|
|
704
713
|
} else {
|
|
705
|
-
lns.push([
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
974
|
+
return { iiifRecords };
|
|
966
975
|
}
|
|
967
976
|
|
|
968
977
|
module.exports = {
|
package/lib/build/log.js
CHANGED
|
@@ -1,64 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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 =
|
|
48
|
-
log(string +
|
|
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(
|
|
54
|
-
log(String(string),
|
|
55
|
-
log(`
|
|
21
|
+
log("↓ ", "yellow", { dim: true });
|
|
22
|
+
log(String(string), "yellow");
|
|
23
|
+
log(` → ${response?.status ?? ""}\n`, "yellow", { dim: true });
|
|
56
24
|
} else {
|
|
57
|
-
log(
|
|
58
|
-
log(String(string),
|
|
59
|
-
log(`
|
|
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
|
+
|
package/lib/build/styles.js
CHANGED
|
@@ -1,64 +1,86 @@
|
|
|
1
|
-
const {
|
|
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,
|
|
5
|
-
const dest = path.join(stylesDir,
|
|
6
|
-
const customContentCss = path.join(CONTENT_DIR,
|
|
7
|
-
const appStylesDir = path.join(process.cwd(),
|
|
8
|
-
const customAppCss = path.join(appStylesDir,
|
|
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,
|
|
14
|
-
path.join(root,
|
|
15
|
-
path.join(root,
|
|
16
|
-
path.join(root,
|
|
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,
|
|
20
|
-
path.join(appStylesDir,
|
|
21
|
-
path.join(appStylesDir,
|
|
22
|
-
path.join(appStylesDir,
|
|
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 {
|
|
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(
|
|
30
|
-
const genDir = path.join(CACHE_DIR,
|
|
40
|
+
const { CACHE_DIR } = require("../common");
|
|
41
|
+
const genDir = path.join(CACHE_DIR, "tailwind");
|
|
31
42
|
ensureDirSync(genDir);
|
|
32
|
-
const genCfg = path.join(genDir,
|
|
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,
|
|
45
|
+
fs.writeFileSync(genCfg, cfg, "utf8");
|
|
35
46
|
configPath = genCfg;
|
|
36
|
-
} catch (_) {
|
|
47
|
+
} catch (_) {
|
|
48
|
+
configPath = null;
|
|
49
|
+
}
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
const inputCss = fs.existsSync(customAppCss)
|
|
40
53
|
? customAppCss
|
|
41
|
-
:
|
|
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(
|
|
47
|
-
const genDir = path.join(CACHE_DIR,
|
|
61
|
+
const { CACHE_DIR } = require("../common");
|
|
62
|
+
const genDir = path.join(CACHE_DIR, "tailwind");
|
|
48
63
|
ensureDirSync(genDir);
|
|
49
|
-
generatedInput = path.join(genDir,
|
|
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,
|
|
52
|
-
} catch (_) {
|
|
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(
|
|
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(
|
|
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(
|
|
71
|
-
const args = [
|
|
72
|
-
if (config) args.push(
|
|
73
|
-
if (minify) args.push(
|
|
74
|
-
const res = spawnSync(cli.cmd, [...cli.args, ...args], {
|
|
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 (_) {
|
|
101
|
+
} catch (_) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
if (configPath && (inputCss || generatedInput)) {
|
|
80
|
-
const ok = buildTailwindCli({
|
|
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 {
|
|
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)) {
|
|
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)) {
|
|
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,
|
|
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.
|
|
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",
|