@canopy-iiif/app 0.12.0 → 0.12.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/mdx.js CHANGED
@@ -81,6 +81,21 @@ function parseFrontmatter(src) {
81
81
  return {data, content};
82
82
  }
83
83
 
84
+ function isRoadmapEntry(frontmatterData) {
85
+ if (!frontmatterData || typeof frontmatterData !== "object") return false;
86
+ if (!Object.prototype.hasOwnProperty.call(frontmatterData, "roadmap")) return false;
87
+ const raw = frontmatterData.roadmap;
88
+ if (typeof raw === "boolean") return raw;
89
+ if (typeof raw === "number") return raw !== 0;
90
+ if (typeof raw === "string") {
91
+ const normalized = raw.trim().toLowerCase();
92
+ if (!normalized) return false;
93
+ if (["false", "0", "no", "off", "none"].includes(normalized)) return false;
94
+ return true;
95
+ }
96
+ return !!raw;
97
+ }
98
+
84
99
  // ESM-only in v3; load dynamically from CJS
85
100
  let MDXProviderCached = null;
86
101
  async function getMdxProvider() {
@@ -1159,6 +1174,7 @@ module.exports = {
1159
1174
  extractMarkdownSummary,
1160
1175
  isReservedFile,
1161
1176
  parseFrontmatter,
1177
+ isRoadmapEntry,
1162
1178
  compileMdxFile,
1163
1179
  compileMdxToComponent,
1164
1180
  loadCustomLayout,
@@ -77,8 +77,9 @@ function mapContentPathToOutput(filePath) {
77
77
  return path.join(OUT_DIR, outRel);
78
78
  }
79
79
 
80
- async function renderContentMdxToHtml(filePath, outPath, extraProps = {}) {
81
- const source = await fsp.readFile(filePath, 'utf8');
80
+ async function renderContentMdxToHtml(filePath, outPath, extraProps = {}, sourceOverride = null) {
81
+ const sourceRaw = sourceOverride != null ? sourceOverride : await fsp.readFile(filePath, 'utf8');
82
+ const source = typeof sourceRaw === 'string' ? sourceRaw : String(sourceRaw || '');
82
83
  const title = mdx.extractTitle(source);
83
84
  const relContentPath = path.relative(CONTENT_DIR, filePath);
84
85
  const normalizedRel = navigation.normalizeRelativePath(relContentPath);
@@ -259,12 +260,27 @@ async function processContentEntry(absPath, pagesMetadata = []) {
259
260
  if (/\.mdx$/i.test(absPath)) {
260
261
  if (mdx.isReservedFile(absPath)) return;
261
262
  const outPath = mapContentPathToOutput(absPath);
262
- ensureDirSync(path.dirname(outPath));
263
263
  try {
264
+ const source = await fsp.readFile(absPath, 'utf8');
265
+ const frontmatter = typeof mdx.parseFrontmatter === 'function'
266
+ ? mdx.parseFrontmatter(source)
267
+ : { data: null };
268
+ const frontmatterData = frontmatter && isPlainObject(frontmatter.data) ? frontmatter.data : null;
269
+ const isRoadmap = frontmatterData && typeof mdx.isRoadmapEntry === 'function'
270
+ ? mdx.isRoadmapEntry(frontmatterData)
271
+ : false;
272
+ if (isRoadmap) {
273
+ try { await fsp.rm(outPath, { force: true }); } catch (_) {}
274
+ try {
275
+ log(`• Skipped roadmap page ${path.relative(process.cwd(), absPath)}\n`, 'yellow', { dim: true });
276
+ } catch (_) {}
277
+ return;
278
+ }
279
+ ensureDirSync(path.dirname(outPath));
264
280
  try { log(`• Processing MDX ${absPath}\n`, 'blue'); } catch (_) {}
265
281
  const base = path.basename(absPath);
266
282
  const extra = base.toLowerCase() === 'sitemap.mdx' ? { pages: pagesMetadata } : {};
267
- const html = await renderContentMdxToHtml(absPath, outPath, extra);
283
+ const html = await renderContentMdxToHtml(absPath, outPath, extra, source);
268
284
  await fsp.writeFile(outPath, html || '', 'utf8');
269
285
  try { log(`✓ Built ${path.relative(process.cwd(), outPath)}\n`, 'green'); } catch (_) {}
270
286
  } catch (err) {
@@ -88,6 +88,20 @@ function collectPagesSync() {
88
88
  } catch (_) {
89
89
  raw = "";
90
90
  }
91
+ let frontmatterData = null;
92
+ if (raw && typeof mdx.parseFrontmatter === "function") {
93
+ try {
94
+ const parsed = mdx.parseFrontmatter(raw);
95
+ if (parsed && parsed.data && typeof parsed.data === "object") {
96
+ frontmatterData = parsed.data;
97
+ }
98
+ } catch (_) {
99
+ frontmatterData = null;
100
+ }
101
+ }
102
+ const isRoadmap = frontmatterData && typeof mdx.isRoadmapEntry === "function"
103
+ ? mdx.isRoadmapEntry(frontmatterData)
104
+ : false;
91
105
  const {
92
106
  slug,
93
107
  segments: slugSegments,
@@ -114,6 +128,7 @@ function collectPagesSync() {
114
128
  fallbackTitle,
115
129
  sortKey: pageSortKey(normalizedRel),
116
130
  topSegment: slugSegments[0] || firstSegment || "",
131
+ isRoadmap,
117
132
  };
118
133
  pages.push(page);
119
134
  }
@@ -138,6 +153,7 @@ function createNode(slug) {
138
153
  sortKey: slug || name,
139
154
  sourcePage: null,
140
155
  children: [],
156
+ isRoadmap: false,
141
157
  };
142
158
  }
143
159
 
@@ -173,6 +189,7 @@ function getNavigationCache() {
173
189
  node.relativePath = page.relativePath;
174
190
  node.sortKey = page.sortKey || node.sortKey;
175
191
  node.hasContent = true;
192
+ node.isRoadmap = !!page.isRoadmap;
176
193
  }
177
194
  }
178
195
 
@@ -240,6 +257,7 @@ function cloneNode(node, currentSlug) {
240
257
  hasContent: node.hasContent,
241
258
  relativePath: node.relativePath,
242
259
  children,
260
+ isRoadmap: !!node.isRoadmap,
243
261
  };
244
262
  }
245
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
@@ -717,7 +717,8 @@ function Hero({
717
717
  import React10 from "react";
718
718
  import navigationHelpers from "@canopy-iiif/app/lib/components/navigation.js";
719
719
  function resolveRelativeCandidate(page, current) {
720
- if (page && typeof page.relativePath === "string" && page.relativePath) return page.relativePath;
720
+ if (page && typeof page.relativePath === "string" && page.relativePath)
721
+ return page.relativePath;
721
722
  if (page && typeof page.slug === "string" && page.slug) return page.slug;
722
723
  if (typeof current === "string" && current) return current;
723
724
  return "";
@@ -731,32 +732,33 @@ function renderNodes(nodes, parentKey = "node") {
731
732
  const showChildren = hasChildren && (node.isExpanded || node.depth === 0);
732
733
  const depth = typeof node.depth === "number" ? Math.max(0, node.depth) : 0;
733
734
  const depthClass = `depth-${Math.min(depth, 5)}`;
734
- const classes = [
735
- "canopy-sub-navigation__link",
736
- depthClass
737
- ];
738
- if (!node.href) classes.push("is-label");
735
+ const isRoadmap = !!node.isRoadmap;
736
+ const isInteractive = !!(node.href && !isRoadmap);
737
+ const classes = ["canopy-sub-navigation__link", depthClass];
738
+ if (!node.href && !isRoadmap) classes.push("is-label");
739
+ if (isRoadmap) classes.push("is-disabled");
739
740
  if (node.isActive) classes.push("is-active");
740
741
  const linkClass = classes.join(" ");
741
- const Tag = node.href ? "a" : "span";
742
- return /* @__PURE__ */ React10.createElement(
743
- "li",
742
+ const Tag = isInteractive ? "a" : "span";
743
+ const badge = isRoadmap ? /* @__PURE__ */ React10.createElement("span", { className: "canopy-sub-navigation__badge" }, "Beta") : null;
744
+ return /* @__PURE__ */ React10.createElement("li", { key, className: "canopy-sub-navigation__item", "data-depth": depth }, /* @__PURE__ */ React10.createElement(
745
+ Tag,
744
746
  {
745
- key,
746
- className: "canopy-sub-navigation__item",
747
- "data-depth": depth
747
+ className: linkClass,
748
+ href: isInteractive ? node.href : void 0,
749
+ "aria-current": node.isActive ? "page" : void 0,
750
+ tabIndex: isInteractive ? void 0 : -1
748
751
  },
749
- /* @__PURE__ */ React10.createElement(
750
- Tag,
751
- {
752
- className: linkClass,
753
- href: node.href || void 0,
754
- "aria-current": node.isActive ? "page" : void 0
755
- },
756
- node.title || node.slug
757
- ),
758
- showChildren ? /* @__PURE__ */ React10.createElement("ul", { className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested", role: "list" }, renderNodes(node.children, key)) : null
759
- );
752
+ node.title || node.slug,
753
+ badge
754
+ ), showChildren ? /* @__PURE__ */ React10.createElement(
755
+ "ul",
756
+ {
757
+ className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
758
+ role: "list"
759
+ },
760
+ renderNodes(node.children, key)
761
+ ) : null);
760
762
  });
761
763
  }
762
764
  function SubNavigation({
@@ -775,7 +777,8 @@ function SubNavigation({
775
777
  const effectiveNavigation = navigationProp || contextNavigation;
776
778
  const effectivePage = page || contextPage;
777
779
  const resolvedNavigation = React10.useMemo(() => {
778
- if (effectiveNavigation && effectiveNavigation.root) return effectiveNavigation;
780
+ if (effectiveNavigation && effectiveNavigation.root)
781
+ return effectiveNavigation;
779
782
  const candidate = resolveRelativeCandidate(effectivePage, current);
780
783
  if (!candidate) return effectiveNavigation || null;
781
784
  const helpers2 = navigationHelpers && navigationHelpers.buildNavigationForFile ? navigationHelpers : null;
@@ -798,7 +801,16 @@ function SubNavigation({
798
801
  if (!Object.prototype.hasOwnProperty.call(inlineStyle, "--sub-nav-indent")) {
799
802
  inlineStyle["--sub-nav-indent"] = "0.85rem";
800
803
  }
801
- return /* @__PURE__ */ React10.createElement("nav", { className: combinedClassName, style: inlineStyle, "aria-label": navLabel }, finalHeading ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-sub-navigation__heading" }, finalHeading) : null, /* @__PURE__ */ React10.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, renderNodes([rootNode], rootNode.slug || "root")));
804
+ return /* @__PURE__ */ React10.createElement(
805
+ "nav",
806
+ {
807
+ className: combinedClassName,
808
+ style: inlineStyle,
809
+ "aria-label": navLabel
810
+ },
811
+ finalHeading ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-sub-navigation__heading" }, finalHeading) : null,
812
+ /* @__PURE__ */ React10.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, renderNodes([rootNode], rootNode.slug || "root"))
813
+ );
802
814
  }
803
815
 
804
816
  // ui/src/layout/Layout.jsx
@@ -2437,6 +2449,7 @@ function DocsCodeBlock(props = {}) {
2437
2449
  display: "inline"
2438
2450
  };
2439
2451
  const showFilename = Boolean(filename);
2452
+ const showHeader = showFilename || enableCopy;
2440
2453
  const { style: preStyleOverride, className: preClassName, ...preRest } = rest;
2441
2454
  const mergedPreStyle = Object.assign({}, preStyle, preStyleOverride || {});
2442
2455
  const lineElements = lines.map((line, index) => {
@@ -2453,7 +2466,7 @@ function DocsCodeBlock(props = {}) {
2453
2466
  return React30.createElement(
2454
2467
  "div",
2455
2468
  { style: containerStyle },
2456
- React30.createElement(
2469
+ showHeader ? React30.createElement(
2457
2470
  "div",
2458
2471
  { style: headerStyle },
2459
2472
  React30.createElement("span", null, showFilename ? filename : null),
@@ -2475,7 +2488,7 @@ function DocsCodeBlock(props = {}) {
2475
2488
  },
2476
2489
  copied ? "Copied" : "Copy"
2477
2490
  ) : null
2478
- ),
2491
+ ) : null,
2479
2492
  React30.createElement(
2480
2493
  "pre",
2481
2494
  { ...preRest, className: preClassName, style: mergedPreStyle },