@canopy-iiif/app 1.4.0 → 1.4.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.
@@ -2416,11 +2416,11 @@ function References({
2416
2416
  }
2417
2417
 
2418
2418
  // ui/src/content/timeline/MdxTimeline.jsx
2419
- import React28 from "react";
2419
+ import React30 from "react";
2420
2420
  import ReactDOMServer from "react-dom/server";
2421
2421
 
2422
2422
  // ui/src/content/timeline/Timeline.jsx
2423
- import React27 from "react";
2423
+ import React28 from "react";
2424
2424
 
2425
2425
  // ui/src/content/timeline/date-utils.js
2426
2426
  var FALLBACK_LOCALE = (() => {
@@ -2580,6 +2580,56 @@ function clampProgress(value) {
2580
2580
  return value;
2581
2581
  }
2582
2582
 
2583
+ // ui/src/layout/ReferencedManifestCard.jsx
2584
+ import React27 from "react";
2585
+ function normalizeMetadata(metadata, summary) {
2586
+ if (Array.isArray(metadata) && metadata.length) {
2587
+ return metadata.filter(Boolean);
2588
+ }
2589
+ if (summary) return [summary];
2590
+ return [];
2591
+ }
2592
+ function ReferencedManifestCard({
2593
+ manifest = null,
2594
+ href,
2595
+ title,
2596
+ summary,
2597
+ metadata,
2598
+ thumbnail,
2599
+ type,
2600
+ className = "",
2601
+ ...rest
2602
+ }) {
2603
+ var _a, _b, _c, _d, _e, _f;
2604
+ const record = manifest || {};
2605
+ const resolvedHref = (_a = href != null ? href : record.href) != null ? _a : "";
2606
+ const resolvedTitle = (_c = (_b = title != null ? title : record.title) != null ? _b : record.href) != null ? _c : "";
2607
+ const resolvedSummary = (_d = summary != null ? summary : record.summary) != null ? _d : "";
2608
+ const resolvedMetadata = normalizeMetadata(
2609
+ metadata != null ? metadata : record.metadata,
2610
+ resolvedSummary
2611
+ );
2612
+ const resolvedThumbnail = (_e = thumbnail != null ? thumbnail : record.thumbnail) != null ? _e : null;
2613
+ const resolvedType = (_f = type != null ? type : record.type) != null ? _f : "work";
2614
+ const classes = [
2615
+ "canopy-referenced-manifest-card",
2616
+ className
2617
+ ].filter(Boolean).join(" ");
2618
+ return /* @__PURE__ */ React27.createElement(
2619
+ TeaserCard,
2620
+ {
2621
+ href: resolvedHref || void 0,
2622
+ title: resolvedTitle || resolvedHref || "",
2623
+ summary: resolvedSummary,
2624
+ metadata: resolvedMetadata,
2625
+ thumbnail: resolvedThumbnail,
2626
+ type: resolvedType,
2627
+ className: classes,
2628
+ ...rest
2629
+ }
2630
+ );
2631
+ }
2632
+
2583
2633
  // ui/src/content/timeline/Timeline.jsx
2584
2634
  var DAY_MS = 24 * 60 * 60 * 1e3;
2585
2635
  var DEFAULT_TRACK_HEIGHT = 640;
@@ -2747,24 +2797,14 @@ function TimelineConnector({ side, isActive, highlight }) {
2747
2797
  "canopy-timeline__connector-dot",
2748
2798
  highlight || isActive ? "is-active" : ""
2749
2799
  ].filter(Boolean).join(" ");
2750
- return /* @__PURE__ */ React27.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React27.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement("span", { className: dotClasses }), /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__connector-line" })));
2800
+ return /* @__PURE__ */ React28.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React28.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("span", { className: dotClasses }), /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__connector-line" })));
2751
2801
  }
2752
2802
  function renderResourceSection(point) {
2753
2803
  if (!point) return null;
2754
2804
  const manifestCards = Array.isArray(point.manifests) ? point.manifests.filter(Boolean) : [];
2755
2805
  const legacyResources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
2756
2806
  if (!manifestCards.length && !legacyResources.length) return null;
2757
- return /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React27.createElement("div", { key: manifest.id || manifest.href }, /* @__PURE__ */ React27.createElement(
2758
- TeaserCard,
2759
- {
2760
- href: manifest.href,
2761
- title: manifest.title || manifest.href,
2762
- summary: manifest.summary,
2763
- metadata: Array.isArray(manifest.metadata) && manifest.metadata.length ? manifest.metadata : manifest.summary ? [manifest.summary] : [],
2764
- thumbnail: manifest.thumbnail,
2765
- type: manifest.type || "work"
2766
- }
2767
- ))), legacyResources.map((resource, idx) => /* @__PURE__ */ React27.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React27.createElement(
2807
+ return /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React28.createElement("div", { key: manifest.id || manifest.href }, /* @__PURE__ */ React28.createElement(ReferencedManifestCard, { manifest }))), legacyResources.map((resource, idx) => /* @__PURE__ */ React28.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React28.createElement(
2768
2808
  TeaserCard,
2769
2809
  {
2770
2810
  href: resource.href,
@@ -2789,26 +2829,26 @@ function Timeline({
2789
2829
  ...rest
2790
2830
  }) {
2791
2831
  const payloadPoints = payload && Array.isArray(payload.points) ? payload.points : null;
2792
- const rawPoints = React27.useMemo(() => {
2832
+ const rawPoints = React28.useMemo(() => {
2793
2833
  if (Array.isArray(pointsProp) && pointsProp.length) return pointsProp;
2794
2834
  if (payloadPoints && payloadPoints.length) return payloadPoints;
2795
2835
  return [];
2796
2836
  }, [pointsProp, payloadPoints]);
2797
- const sanitizedPoints = React27.useMemo(
2837
+ const sanitizedPoints = React28.useMemo(
2798
2838
  () => sanitizePoints(rawPoints),
2799
2839
  [rawPoints]
2800
2840
  );
2801
2841
  const localeValue = payload && payload.locale ? payload.locale : localeProp;
2802
- const baseLocale = React27.useMemo(
2842
+ const baseLocale = React28.useMemo(
2803
2843
  () => createLocale(localeValue),
2804
2844
  [localeValue]
2805
2845
  );
2806
2846
  const rangeInput = payload && payload.range ? payload.range : rangeProp || {};
2807
- const rangeOverrides = React27.useMemo(
2847
+ const rangeOverrides = React28.useMemo(
2808
2848
  () => deriveRangeOverrides(sanitizedPoints, rangeInput),
2809
2849
  [sanitizedPoints, rangeInput]
2810
2850
  );
2811
- const effectiveRange = React27.useMemo(
2851
+ const effectiveRange = React28.useMemo(
2812
2852
  () => normalizeRange({
2813
2853
  ...rangeOverrides,
2814
2854
  locale: baseLocale
@@ -2817,7 +2857,7 @@ function Timeline({
2817
2857
  );
2818
2858
  const spanStart = effectiveRange.startDate.getTime();
2819
2859
  const span = effectiveRange.span;
2820
- const pointsWithPosition = React27.useMemo(() => {
2860
+ const pointsWithPosition = React28.useMemo(() => {
2821
2861
  if (!sanitizedPoints.length) return [];
2822
2862
  return sanitizedPoints.map((point, index) => {
2823
2863
  const timestamp = point.meta.timestamp;
@@ -2831,29 +2871,29 @@ function Timeline({
2831
2871
  };
2832
2872
  });
2833
2873
  }, [sanitizedPoints, spanStart, span]);
2834
- const [activeId, setActiveId] = React27.useState(
2874
+ const [activeId, setActiveId] = React28.useState(
2835
2875
  () => getActivePointId(pointsWithPosition)
2836
2876
  );
2837
- React27.useEffect(() => {
2877
+ React28.useEffect(() => {
2838
2878
  setActiveId(getActivePointId(pointsWithPosition));
2839
2879
  }, [pointsWithPosition]);
2840
2880
  const thresholdValue = typeof thresholdProp === "number" ? thresholdProp : payload && payload.threshold != null ? payload.threshold : null;
2841
2881
  const stepsValue = typeof steps === "number" ? Number(steps) : payload && typeof payload.steps === "number" ? Number(payload.steps) : null;
2842
- const thresholdMs = React27.useMemo(
2882
+ const thresholdMs = React28.useMemo(
2843
2883
  () => getThresholdMs(thresholdValue, effectiveRange.granularity),
2844
2884
  [thresholdValue, effectiveRange.granularity]
2845
2885
  );
2846
- const groupedEntries = React27.useMemo(
2886
+ const groupedEntries = React28.useMemo(
2847
2887
  () => buildGroupedEntries(pointsWithPosition, thresholdMs, {
2848
2888
  granularity: effectiveRange.granularity,
2849
2889
  locale: baseLocale
2850
2890
  }),
2851
2891
  [pointsWithPosition, thresholdMs, effectiveRange.granularity, baseLocale]
2852
2892
  );
2853
- const [expandedGroupIds, setExpandedGroupIds] = React27.useState(
2893
+ const [expandedGroupIds, setExpandedGroupIds] = React28.useState(
2854
2894
  () => /* @__PURE__ */ new Set()
2855
2895
  );
2856
- React27.useEffect(() => {
2896
+ React28.useEffect(() => {
2857
2897
  setExpandedGroupIds((prev) => {
2858
2898
  if (!prev || prev.size === 0) return prev;
2859
2899
  const validIds = new Set(
@@ -2868,7 +2908,7 @@ function Timeline({
2868
2908
  return changed ? next : prev;
2869
2909
  });
2870
2910
  }, [groupedEntries]);
2871
- const toggleGroup = React27.useCallback((groupId) => {
2911
+ const toggleGroup = React28.useCallback((groupId) => {
2872
2912
  setExpandedGroupIds((prev) => {
2873
2913
  const next = new Set(prev || []);
2874
2914
  if (next.has(groupId)) next.delete(groupId);
@@ -2891,7 +2931,7 @@ function Timeline({
2891
2931
  point.id === activeId ? "is-active" : "",
2892
2932
  point.highlight ? "is-highlighted" : ""
2893
2933
  ].filter(Boolean).join(" ");
2894
- const connector = /* @__PURE__ */ React27.createElement(
2934
+ const connector = /* @__PURE__ */ React28.createElement(
2895
2935
  TimelineConnector,
2896
2936
  {
2897
2937
  side: point.side,
@@ -2899,9 +2939,9 @@ function Timeline({
2899
2939
  highlight: point.highlight
2900
2940
  }
2901
2941
  );
2902
- const body = /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
2942
+ const body = /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
2903
2943
  const resourceSection = renderResourceSection(point);
2904
- return /* @__PURE__ */ React27.createElement(
2944
+ return /* @__PURE__ */ React28.createElement(
2905
2945
  "div",
2906
2946
  {
2907
2947
  key: point.id,
@@ -2909,7 +2949,7 @@ function Timeline({
2909
2949
  style: wrapperStyle,
2910
2950
  role: "listitem"
2911
2951
  },
2912
- point.side === "left" ? /* @__PURE__ */ React27.createElement(React27.Fragment, null, /* @__PURE__ */ React27.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React27.createElement(React27.Fragment, null, connector, /* @__PURE__ */ React27.createElement("div", { className: cardClasses }, body, resourceSection))
2952
+ point.side === "left" ? /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React28.createElement(React28.Fragment, null, connector, /* @__PURE__ */ React28.createElement("div", { className: cardClasses }, body, resourceSection))
2913
2953
  );
2914
2954
  }
2915
2955
  function renderGroupEntry(entry) {
@@ -2920,7 +2960,7 @@ function Timeline({
2920
2960
  const wrapperStyle = { top: `${entry.progress * 100}%` };
2921
2961
  const isExpanded = expandedGroupIds.has(entry.id);
2922
2962
  const hasActivePoint = entry.points.some((point) => point.id === activeId);
2923
- const connector = /* @__PURE__ */ React27.createElement(
2963
+ const connector = /* @__PURE__ */ React28.createElement(
2924
2964
  TimelineConnector,
2925
2965
  {
2926
2966
  side: entry.side,
@@ -2934,7 +2974,7 @@ function Timeline({
2934
2974
  hasActivePoint ? "is-active" : ""
2935
2975
  ].filter(Boolean).join(" ");
2936
2976
  const countLabel = `${entry.count} event${entry.count > 1 ? "s" : ""}`;
2937
- const header = /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__point-date" }, entry.label), /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__group-count" }, countLabel)), /* @__PURE__ */ React27.createElement(
2977
+ const header = /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__point-date" }, entry.label), /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__group-count" }, countLabel)), /* @__PURE__ */ React28.createElement(
2938
2978
  "button",
2939
2979
  {
2940
2980
  type: "button",
@@ -2944,7 +2984,7 @@ function Timeline({
2944
2984
  },
2945
2985
  isExpanded ? "Hide details" : "Show details"
2946
2986
  ));
2947
- const groupPoints = isExpanded ? /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React27.createElement(
2987
+ const groupPoints = isExpanded ? /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React28.createElement(
2948
2988
  "button",
2949
2989
  {
2950
2990
  key: point.id,
@@ -2955,11 +2995,11 @@ function Timeline({
2955
2995
  ].filter(Boolean).join(" "),
2956
2996
  onClick: () => setActiveId(point.id)
2957
2997
  },
2958
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label),
2959
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
2998
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label),
2999
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
2960
3000
  ))) : null;
2961
- const groupCard = /* @__PURE__ */ React27.createElement("div", { className: groupClasses }, header, groupPoints);
2962
- return /* @__PURE__ */ React27.createElement(
3001
+ const groupCard = /* @__PURE__ */ React28.createElement("div", { className: groupClasses }, header, groupPoints);
3002
+ return /* @__PURE__ */ React28.createElement(
2963
3003
  "div",
2964
3004
  {
2965
3005
  key: entry.id,
@@ -2967,17 +3007,17 @@ function Timeline({
2967
3007
  style: wrapperStyle,
2968
3008
  role: "listitem"
2969
3009
  },
2970
- entry.side === "left" ? /* @__PURE__ */ React27.createElement(React27.Fragment, null, groupCard, connector) : /* @__PURE__ */ React27.createElement(React27.Fragment, null, connector, groupCard)
3010
+ entry.side === "left" ? /* @__PURE__ */ React28.createElement(React28.Fragment, null, groupCard, connector) : /* @__PURE__ */ React28.createElement(React28.Fragment, null, connector, groupCard)
2971
3011
  );
2972
3012
  }
2973
- return /* @__PURE__ */ React27.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React27.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React27.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React27.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React27.createElement(
3013
+ return /* @__PURE__ */ React28.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React28.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React28.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React28.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React28.createElement(
2974
3014
  "div",
2975
3015
  {
2976
3016
  className: "canopy-timeline__list",
2977
3017
  role: "list",
2978
3018
  style: { minHeight: trackHeight }
2979
3019
  },
2980
- /* @__PURE__ */ React27.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
3020
+ /* @__PURE__ */ React28.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
2981
3021
  renderSteps(stepsValue, effectiveRange),
2982
3022
  groupedEntries.map((entry) => {
2983
3023
  if (entry.type === "group") return renderGroupEntry(entry);
@@ -2992,7 +3032,7 @@ function renderSteps(stepSize, range) {
2992
3032
  const markers = [];
2993
3033
  if (startYear < endYear) {
2994
3034
  markers.push(
2995
- /* @__PURE__ */ React27.createElement(
3035
+ /* @__PURE__ */ React28.createElement(
2996
3036
  "span",
2997
3037
  {
2998
3038
  key: "timeline-step-start",
@@ -3000,12 +3040,12 @@ function renderSteps(stepSize, range) {
3000
3040
  style: { top: "0%" },
3001
3041
  "aria-hidden": "true"
3002
3042
  },
3003
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__step-line" }),
3004
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
3043
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__step-line" }),
3044
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
3005
3045
  )
3006
3046
  );
3007
3047
  markers.push(
3008
- /* @__PURE__ */ React27.createElement(
3048
+ /* @__PURE__ */ React28.createElement(
3009
3049
  "span",
3010
3050
  {
3011
3051
  key: "timeline-step-end",
@@ -3013,8 +3053,8 @@ function renderSteps(stepSize, range) {
3013
3053
  style: { top: "100%" },
3014
3054
  "aria-hidden": "true"
3015
3055
  },
3016
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__step-line" }),
3017
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
3056
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__step-line" }),
3057
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
3018
3058
  )
3019
3059
  );
3020
3060
  }
@@ -3024,7 +3064,7 @@ function renderSteps(stepSize, range) {
3024
3064
  const progress = (timestamp - range.startDate.getTime()) / range.span;
3025
3065
  if (progress <= 0 || progress >= 1) continue;
3026
3066
  markers.push(
3027
- /* @__PURE__ */ React27.createElement(
3067
+ /* @__PURE__ */ React28.createElement(
3028
3068
  "span",
3029
3069
  {
3030
3070
  key: `timeline-step-${year}`,
@@ -3032,8 +3072,8 @@ function renderSteps(stepSize, range) {
3032
3072
  style: { top: `calc(${progress * 100}% - 0.5px)` },
3033
3073
  "aria-hidden": "true"
3034
3074
  },
3035
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__step-line" }),
3036
- /* @__PURE__ */ React27.createElement("span", { className: "canopy-timeline__step-label" }, year)
3075
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__step-line" }),
3076
+ /* @__PURE__ */ React28.createElement("span", { className: "canopy-timeline__step-label" }, year)
3037
3077
  )
3038
3078
  );
3039
3079
  }
@@ -3046,7 +3086,8 @@ function TimelinePoint() {
3046
3086
  }
3047
3087
  TimelinePoint.displayName = "TimelinePoint";
3048
3088
 
3049
- // ui/src/content/timeline/MdxTimeline.jsx
3089
+ // ui/src/utils/manifestReferences.js
3090
+ import React29 from "react";
3050
3091
  import navigationHelpers6 from "@canopy-iiif/app/lib/components/navigation.js";
3051
3092
  function normalizeManifestId(raw) {
3052
3093
  if (!raw) return "";
@@ -3063,25 +3104,52 @@ function normalizeManifestId(raw) {
3063
3104
  return String(raw || "").trim();
3064
3105
  }
3065
3106
  }
3066
- var PageContextFallback = React28.createContext(null);
3107
+ var PageContextFallback = React29.createContext(null);
3067
3108
  function useReferencedManifestMap() {
3068
3109
  var _a, _b;
3069
3110
  const PageContext = ((_b = (_a = navigationHelpers6) == null ? void 0 : _a.getPageContext) == null ? void 0 : _b.call(_a)) || PageContextFallback;
3070
- const pageContext = React28.useContext(PageContext);
3111
+ const pageContext = React29.useContext(PageContext);
3071
3112
  const referencedItems = pageContext && pageContext.page && Array.isArray(pageContext.page.referencedItems) ? pageContext.page.referencedItems : [];
3072
- return React28.useMemo(() => {
3113
+ return React29.useMemo(() => {
3073
3114
  const map = /* @__PURE__ */ new Map();
3074
3115
  referencedItems.forEach((item) => {
3075
3116
  if (!item) return;
3076
3117
  const id = item.id || item.href;
3077
3118
  if (!id) return;
3078
3119
  const normalized = normalizeManifestId(id);
3079
- map.set(normalized, item);
3120
+ if (normalized) map.set(normalized, item);
3080
3121
  map.set(String(id), item);
3081
3122
  });
3082
3123
  return map;
3083
3124
  }, [referencedItems]);
3084
3125
  }
3126
+ function resolveManifestReferences(ids, manifestMap) {
3127
+ if (!Array.isArray(ids) || !ids.length || !manifestMap) return [];
3128
+ const seen = /* @__PURE__ */ new Set();
3129
+ const out = [];
3130
+ ids.forEach((value) => {
3131
+ if (!value) return;
3132
+ const normalized = normalizeManifestId(value);
3133
+ if (!normalized || seen.has(normalized)) return;
3134
+ seen.add(normalized);
3135
+ const record = manifestMap.get(normalized) || manifestMap.get(String(value));
3136
+ if (!record) return;
3137
+ out.push({
3138
+ id: record.id || record.href || value,
3139
+ href: record.href || null,
3140
+ title: record.title || record.label || record.href || value,
3141
+ summary: record.summary || "",
3142
+ thumbnail: record.thumbnail || null,
3143
+ thumbnailWidth: record.thumbnailWidth,
3144
+ thumbnailHeight: record.thumbnailHeight,
3145
+ type: record.type || "work",
3146
+ metadata: Array.isArray(record.metadata) && record.metadata.length ? record.metadata : record.summary ? [record.summary] : []
3147
+ });
3148
+ });
3149
+ return out;
3150
+ }
3151
+
3152
+ // ui/src/content/timeline/MdxTimeline.jsx
3085
3153
  function normalizeResource(resource, index) {
3086
3154
  if (!resource) return null;
3087
3155
  const href = resource.href || resource.id || "";
@@ -3097,7 +3165,7 @@ function normalizeResource(resource, index) {
3097
3165
  }
3098
3166
  function normalizePoint(child, index, options) {
3099
3167
  var _a, _b, _c, _d, _e, _f;
3100
- if (!React28.isValidElement(child)) return null;
3168
+ if (!React30.isValidElement(child)) return null;
3101
3169
  if (child.type !== TimelinePoint && ((_a = child.type) == null ? void 0 : _a.displayName) !== "TimelinePoint")
3102
3170
  return null;
3103
3171
  const props = child.props || {};
@@ -3113,7 +3181,7 @@ function normalizePoint(child, index, options) {
3113
3181
  try {
3114
3182
  if (props.children) {
3115
3183
  detailsHtml = ReactDOMServer.renderToStaticMarkup(
3116
- React28.createElement(React28.Fragment, null, props.children)
3184
+ React30.createElement(React30.Fragment, null, props.children)
3117
3185
  );
3118
3186
  }
3119
3187
  } catch (_) {
@@ -3138,31 +3206,6 @@ function normalizePoint(child, index, options) {
3138
3206
  manifests
3139
3207
  };
3140
3208
  }
3141
- function resolveManifestReferences(ids, manifestMap) {
3142
- if (!Array.isArray(ids) || !ids.length || !manifestMap) return [];
3143
- const seen = /* @__PURE__ */ new Set();
3144
- const out = [];
3145
- ids.forEach((value) => {
3146
- if (!value) return;
3147
- const normalized = normalizeManifestId(value);
3148
- if (!normalized || seen.has(normalized)) return;
3149
- seen.add(normalized);
3150
- const record = manifestMap.get(normalized);
3151
- if (!record) return;
3152
- out.push({
3153
- id: record.id || value,
3154
- href: record.href || null,
3155
- title: record.title || record.href || value,
3156
- summary: record.summary || "",
3157
- thumbnail: record.thumbnail || null,
3158
- thumbnailWidth: record.thumbnailWidth,
3159
- thumbnailHeight: record.thumbnailHeight,
3160
- type: record.type || "work",
3161
- metadata: Array.isArray(record.metadata) && record.metadata.length ? record.metadata : record.summary ? [record.summary] : []
3162
- });
3163
- });
3164
- return out;
3165
- }
3166
3209
  function serializeProps(props, payload, locale) {
3167
3210
  const clone = {};
3168
3211
  Object.keys(props || {}).forEach((key) => {
@@ -3187,7 +3230,7 @@ function MdxTimeline({ children, ...rest }) {
3187
3230
  const localeObj = createLocale(localeValue);
3188
3231
  const localeBase = typeof localeObj === "string" ? localeObj : localeObj.baseName || "en-US";
3189
3232
  const manifestMap = useReferencedManifestMap();
3190
- const childArray = React28.Children.toArray(children);
3233
+ const childArray = React30.Children.toArray(children);
3191
3234
  const points = childArray.map(
3192
3235
  (child, index) => normalizePoint(child, index, {
3193
3236
  range: rest.range || {},
@@ -3203,7 +3246,7 @@ function MdxTimeline({ children, ...rest }) {
3203
3246
  steps: rest.steps != null ? rest.steps : null
3204
3247
  };
3205
3248
  const json = serializeForScript(serializeProps(rest, payload, localeBase));
3206
- return /* @__PURE__ */ React28.createElement("div", { "data-canopy-timeline": "1" }, /* @__PURE__ */ React28.createElement(Timeline, { ...rest, __canopyTimeline: payload }), /* @__PURE__ */ React28.createElement(
3249
+ return /* @__PURE__ */ React30.createElement("div", { "data-canopy-timeline": "1" }, /* @__PURE__ */ React30.createElement(Timeline, { ...rest, __canopyTimeline: payload }), /* @__PURE__ */ React30.createElement(
3207
3250
  "script",
3208
3251
  {
3209
3252
  type: "application/json",
@@ -3213,7 +3256,7 @@ function MdxTimeline({ children, ...rest }) {
3213
3256
  }
3214
3257
 
3215
3258
  // ui/src/content/map/MdxMap.jsx
3216
- import React29 from "react";
3259
+ import React31 from "react";
3217
3260
  import ReactDOMServer2 from "react-dom/server";
3218
3261
 
3219
3262
  // ui/src/content/map/MapPoint.jsx
@@ -3240,15 +3283,15 @@ function renderDetailsHtml(children) {
3240
3283
  if (!children) return "";
3241
3284
  try {
3242
3285
  return ReactDOMServer2.renderToStaticMarkup(
3243
- React29.createElement(React29.Fragment, null, children)
3286
+ React31.createElement(React31.Fragment, null, children)
3244
3287
  );
3245
3288
  } catch (_) {
3246
3289
  return "";
3247
3290
  }
3248
3291
  }
3249
- function normalizeCustomPoint(child, index) {
3292
+ function normalizeCustomPoint(child, index, manifestMap) {
3250
3293
  var _a;
3251
- if (!React29.isValidElement(child)) return null;
3294
+ if (!React31.isValidElement(child)) return null;
3252
3295
  if (child.type !== MapPoint && ((_a = child.type) == null ? void 0 : _a.displayName) !== "MapPoint") return null;
3253
3296
  const coords = normalizeCoordinates(child.props || {});
3254
3297
  if (!coords) return null;
@@ -3257,26 +3300,43 @@ function normalizeCustomPoint(child, index) {
3257
3300
  const title = props.title || props.label || `Point ${index + 1}`;
3258
3301
  const summary = props.summary || props.description || "";
3259
3302
  const href = props.href || props.link || "";
3260
- const thumbnail = props.thumbnail || props.image || "";
3261
- const thumbnailWidth = normalizeNumber(props.thumbnailWidth || props.width);
3262
- const thumbnailHeight = normalizeNumber(props.thumbnailHeight || props.height);
3303
+ let thumbnail = props.thumbnail || props.image || "";
3304
+ let thumbnailWidth = normalizeNumber(props.thumbnailWidth);
3305
+ let thumbnailHeight = normalizeNumber(props.thumbnailHeight);
3263
3306
  const detailsHtml = renderDetailsHtml(props.children);
3307
+ const manifestValues = Array.isArray(props.referencedManifests) ? props.referencedManifests : props.manifest ? [props.manifest] : Array.isArray(props.manifests) ? props.manifests : [];
3308
+ const manifests = resolveManifestReferences(manifestValues, manifestMap);
3309
+ if (!thumbnail && manifests.length) {
3310
+ const manifestWithThumb = manifests.find(
3311
+ (manifest) => manifest && manifest.thumbnail
3312
+ );
3313
+ if (manifestWithThumb) {
3314
+ thumbnail = manifestWithThumb.thumbnail || "";
3315
+ if (thumbnail && thumbnailWidth == null && typeof manifestWithThumb.thumbnailWidth === "number") {
3316
+ thumbnailWidth = manifestWithThumb.thumbnailWidth;
3317
+ }
3318
+ if (thumbnail && thumbnailHeight == null && typeof manifestWithThumb.thumbnailHeight === "number") {
3319
+ thumbnailHeight = manifestWithThumb.thumbnailHeight;
3320
+ }
3321
+ }
3322
+ }
3264
3323
  return {
3265
3324
  id,
3266
3325
  title,
3267
3326
  summary,
3268
3327
  href,
3269
3328
  thumbnail,
3270
- thumbnailWidth: Number.isFinite(thumbnailWidth) ? thumbnailWidth : void 0,
3271
- thumbnailHeight: Number.isFinite(thumbnailHeight) ? thumbnailHeight : void 0,
3329
+ thumbnailWidth,
3330
+ thumbnailHeight,
3272
3331
  lat: coords.lat,
3273
3332
  lng: coords.lng,
3274
3333
  detailsHtml,
3334
+ manifests,
3275
3335
  type: "custom"
3276
3336
  };
3277
3337
  }
3278
- function normalizeCustomPoints(children) {
3279
- return React29.Children.toArray(children).map((child, index) => normalizeCustomPoint(child, index)).filter(Boolean);
3338
+ function normalizeCustomPoints(children, manifestMap) {
3339
+ return React31.Children.toArray(children).map((child, index) => normalizeCustomPoint(child, index, manifestMap)).filter(Boolean);
3280
3340
  }
3281
3341
  function normalizeHeight(value) {
3282
3342
  if (value == null) return "600px";
@@ -3382,7 +3442,8 @@ function getDatasetInfo() {
3382
3442
  }
3383
3443
  }
3384
3444
  function MdxMap({ children, ...rest }) {
3385
- const customPoints = normalizeCustomPoints(children);
3445
+ const manifestMap = useReferencedManifestMap();
3446
+ const customPoints = normalizeCustomPoints(children, manifestMap);
3386
3447
  const datasetInfo = getDatasetInfo();
3387
3448
  const navDataset = {
3388
3449
  hasFeatures: Boolean(datasetInfo && datasetInfo.hasFeatures),
@@ -3413,11 +3474,11 @@ function MdxMap({ children, ...rest }) {
3413
3474
  if (placeholderClass) placeholderProps.className = placeholderClass;
3414
3475
  if (rest.id) placeholderProps.id = rest.id;
3415
3476
  if (rest.style) placeholderProps.style = rest.style;
3416
- return /* @__PURE__ */ React29.createElement("div", { "data-canopy-map": "1" }, /* @__PURE__ */ React29.createElement("div", { ...placeholderProps, "aria-live": "polite" }, /* @__PURE__ */ React29.createElement("div", { className: "canopy-map__status" }, "Loading map\u2026")), /* @__PURE__ */ React29.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3477
+ return /* @__PURE__ */ React31.createElement("div", { "data-canopy-map": "1" }, /* @__PURE__ */ React31.createElement("div", { ...placeholderProps, "aria-live": "polite" }, /* @__PURE__ */ React31.createElement("div", { className: "canopy-map__status" }, "Loading map\u2026")), /* @__PURE__ */ React31.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3417
3478
  }
3418
3479
 
3419
3480
  // ui/src/search/MdxSearchResults.jsx
3420
- import React30 from "react";
3481
+ import React32 from "react";
3421
3482
  function MdxSearchResults(props) {
3422
3483
  let json = "{}";
3423
3484
  try {
@@ -3425,11 +3486,11 @@ function MdxSearchResults(props) {
3425
3486
  } catch (_) {
3426
3487
  json = "{}";
3427
3488
  }
3428
- return /* @__PURE__ */ React30.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React30.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3489
+ return /* @__PURE__ */ React32.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React32.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3429
3490
  }
3430
3491
 
3431
3492
  // ui/src/search/SearchSummary.jsx
3432
- import React31 from "react";
3493
+ import React33 from "react";
3433
3494
  function SearchSummary(props) {
3434
3495
  let json = "{}";
3435
3496
  try {
@@ -3437,11 +3498,11 @@ function SearchSummary(props) {
3437
3498
  } catch (_) {
3438
3499
  json = "{}";
3439
3500
  }
3440
- return /* @__PURE__ */ React31.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React31.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3501
+ return /* @__PURE__ */ React33.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React33.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3441
3502
  }
3442
3503
 
3443
3504
  // ui/src/search/MdxSearchTabs.jsx
3444
- import React32 from "react";
3505
+ import React34 from "react";
3445
3506
  function MdxSearchTabs(props) {
3446
3507
  let json = "{}";
3447
3508
  try {
@@ -3449,11 +3510,11 @@ function MdxSearchTabs(props) {
3449
3510
  } catch (_) {
3450
3511
  json = "{}";
3451
3512
  }
3452
- return /* @__PURE__ */ React32.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React32.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3513
+ return /* @__PURE__ */ React34.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React34.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3453
3514
  }
3454
3515
 
3455
3516
  // ui/src/search/MdxSearch.jsx
3456
- import React33 from "react";
3517
+ import React35 from "react";
3457
3518
  function MdxSearch(props = {}) {
3458
3519
  const {
3459
3520
  layout,
@@ -3471,11 +3532,11 @@ function MdxSearch(props = {}) {
3471
3532
  resultsPayload.layout = layout;
3472
3533
  }
3473
3534
  const classes = ["canopy-search", className].filter(Boolean).join(" ");
3474
- return /* @__PURE__ */ React33.createElement("section", { className: classes, "data-canopy-search": "1" }, showTabs ? /* @__PURE__ */ React33.createElement(MdxSearchTabs, { ...tabsProps }) : null, showSummary ? /* @__PURE__ */ React33.createElement(SearchSummary, { ...summaryProps }) : null, showResults ? /* @__PURE__ */ React33.createElement(MdxSearchResults, { ...resultsPayload }) : null, children || null);
3535
+ return /* @__PURE__ */ React35.createElement("section", { className: classes, "data-canopy-search": "1" }, showTabs ? /* @__PURE__ */ React35.createElement(MdxSearchTabs, { ...tabsProps }) : null, showSummary ? /* @__PURE__ */ React35.createElement(SearchSummary, { ...summaryProps }) : null, showResults ? /* @__PURE__ */ React35.createElement(MdxSearchResults, { ...resultsPayload }) : null, children || null);
3475
3536
  }
3476
3537
 
3477
3538
  // ui/src/search-form/MdxSearchFormModal.jsx
3478
- import React34 from "react";
3539
+ import React36 from "react";
3479
3540
  function MdxSearchFormModal(props = {}) {
3480
3541
  const {
3481
3542
  placeholder = "Search\u2026",
@@ -3491,12 +3552,12 @@ function MdxSearchFormModal(props = {}) {
3491
3552
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
3492
3553
  const resolvedSearchPath = resolveSearchPath(searchPath);
3493
3554
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
3494
- return /* @__PURE__ */ React34.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React34.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React34.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React34.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React34.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
3555
+ return /* @__PURE__ */ React36.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React36.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React36.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React36.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React36.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
3495
3556
  }
3496
3557
 
3497
3558
  // ui/src/iiif/ManifestPrimitives.jsx
3498
3559
  var import_slugify = __toESM(require_slugify());
3499
- import React35 from "react";
3560
+ import React37 from "react";
3500
3561
  import {
3501
3562
  Label as CloverLabel,
3502
3563
  Metadata as CloverMetadata,
@@ -3637,7 +3698,7 @@ function MetadataFacetLink(props) {
3637
3698
  const valueSlug = facetSlug ? toValueSlug(text) : "";
3638
3699
  const href = facetSlug && valueSlug ? buildFacetSearchHref(facetSlug, valueSlug) : "";
3639
3700
  if (!href) return text;
3640
- return /* @__PURE__ */ React35.createElement(
3701
+ return /* @__PURE__ */ React37.createElement(
3641
3702
  "a",
3642
3703
  {
3643
3704
  href,
@@ -3663,7 +3724,7 @@ function buildFacetCustomValueContent(items, manifest) {
3663
3724
  seen.add(normalized);
3664
3725
  custom.push({
3665
3726
  matchingLabel: item.label,
3666
- Content: /* @__PURE__ */ React35.createElement(MetadataFacetLink, { facetSlug: facet.slug })
3727
+ Content: /* @__PURE__ */ React37.createElement(MetadataFacetLink, { facetSlug: facet.slug })
3667
3728
  });
3668
3729
  }
3669
3730
  return custom;
@@ -3695,12 +3756,12 @@ function mergeCustomValueContent(userContent, autoContent) {
3695
3756
  function Label({ manifest, label, ...rest }) {
3696
3757
  const intl = label || manifest && manifest.label;
3697
3758
  if (!hasInternationalValue(intl)) return null;
3698
- return /* @__PURE__ */ React35.createElement(CloverLabel, { label: intl, ...rest });
3759
+ return /* @__PURE__ */ React37.createElement(CloverLabel, { label: intl, ...rest });
3699
3760
  }
3700
3761
  function Summary({ manifest, summary, ...rest }) {
3701
3762
  const intl = summary || manifest && manifest.summary;
3702
3763
  if (!hasInternationalValue(intl)) return null;
3703
- return /* @__PURE__ */ React35.createElement(CloverSummary, { summary: intl, ...rest });
3764
+ return /* @__PURE__ */ React37.createElement(CloverSummary, { summary: intl, ...rest });
3704
3765
  }
3705
3766
  function Metadata({ manifest, metadata, customValueContent, ...rest }) {
3706
3767
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
@@ -3710,7 +3771,7 @@ function Metadata({ manifest, metadata, customValueContent, ...rest }) {
3710
3771
  customValueContent,
3711
3772
  autoCustomContent
3712
3773
  );
3713
- return /* @__PURE__ */ React35.createElement(
3774
+ return /* @__PURE__ */ React37.createElement(
3714
3775
  CloverMetadata,
3715
3776
  {
3716
3777
  metadata: items,
@@ -3724,11 +3785,11 @@ function RequiredStatement({ manifest, requiredStatement, ...rest }) {
3724
3785
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
3725
3786
  return null;
3726
3787
  }
3727
- return /* @__PURE__ */ React35.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
3788
+ return /* @__PURE__ */ React37.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
3728
3789
  }
3729
3790
 
3730
3791
  // ui/src/docs/CodeBlock.jsx
3731
- import React36 from "react";
3792
+ import React38 from "react";
3732
3793
  function parseHighlightAttr(attr) {
3733
3794
  if (!attr) return /* @__PURE__ */ new Set();
3734
3795
  const cleaned = String(attr || "").trim();
@@ -3774,10 +3835,10 @@ var highlightBaseStyle = {
3774
3835
  };
3775
3836
  function DocsCodeBlock(props = {}) {
3776
3837
  const { children, ...rest } = props;
3777
- const childArray = React36.Children.toArray(children);
3778
- const codeElement = childArray.find((el) => React36.isValidElement(el));
3838
+ const childArray = React38.Children.toArray(children);
3839
+ const codeElement = childArray.find((el) => React38.isValidElement(el));
3779
3840
  if (!codeElement || !codeElement.props) {
3780
- return React36.createElement("pre", props);
3841
+ return React38.createElement("pre", props);
3781
3842
  }
3782
3843
  const {
3783
3844
  className = "",
@@ -3792,8 +3853,8 @@ function DocsCodeBlock(props = {}) {
3792
3853
  const highlightSet = parseHighlightAttr(highlightAttr);
3793
3854
  const copyAttr = codeProps["data-copy"];
3794
3855
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
3795
- const [copied, setCopied] = React36.useState(false);
3796
- const handleCopy = React36.useCallback(async () => {
3856
+ const [copied, setCopied] = React38.useState(false);
3857
+ const handleCopy = React38.useCallback(async () => {
3797
3858
  const text = rawCode;
3798
3859
  try {
3799
3860
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -3858,20 +3919,20 @@ function DocsCodeBlock(props = {}) {
3858
3919
  const highlight = highlightSet.has(lineNumber);
3859
3920
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
3860
3921
  const displayLine = line === "" ? " " : line;
3861
- return React36.createElement(
3922
+ return React38.createElement(
3862
3923
  "span",
3863
3924
  { key: lineNumber, style },
3864
- React36.createElement("span", { style: lineContentStyle }, displayLine)
3925
+ React38.createElement("span", { style: lineContentStyle }, displayLine)
3865
3926
  );
3866
3927
  });
3867
- return React36.createElement(
3928
+ return React38.createElement(
3868
3929
  "div",
3869
3930
  { style: containerStyle },
3870
- showHeader ? React36.createElement(
3931
+ showHeader ? React38.createElement(
3871
3932
  "div",
3872
3933
  { style: headerStyle },
3873
- React36.createElement("span", null, showFilename ? filename : null),
3874
- enableCopy ? React36.createElement(
3934
+ React38.createElement("span", null, showFilename ? filename : null),
3935
+ enableCopy ? React38.createElement(
3875
3936
  "button",
3876
3937
  {
3877
3938
  type: "button",
@@ -3890,29 +3951,29 @@ function DocsCodeBlock(props = {}) {
3890
3951
  copied ? "Copied" : "Copy"
3891
3952
  ) : null
3892
3953
  ) : null,
3893
- React36.createElement(
3954
+ React38.createElement(
3894
3955
  "pre",
3895
3956
  { ...preRest, className: preClassName, style: mergedPreStyle },
3896
- React36.createElement("code", { style: codeStyle }, lineElements)
3957
+ React38.createElement("code", { style: codeStyle }, lineElements)
3897
3958
  )
3898
3959
  );
3899
3960
  }
3900
3961
 
3901
3962
  // ui/src/docs/MarkdownTable.jsx
3902
- import React37 from "react";
3963
+ import React39 from "react";
3903
3964
  function MarkdownTable({ className = "", ...rest }) {
3904
3965
  const merged = ["markdown-table", className].filter(Boolean).join(" ");
3905
- return /* @__PURE__ */ React37.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React37.createElement("table", { className: merged, ...rest }));
3966
+ return /* @__PURE__ */ React39.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React39.createElement("table", { className: merged, ...rest }));
3906
3967
  }
3907
3968
 
3908
3969
  // ui/src/docs/Diagram.jsx
3909
- import React38 from "react";
3970
+ import React40 from "react";
3910
3971
  function CanopyDiagram() {
3911
- return /* @__PURE__ */ React38.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React38.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React38.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React38.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Collection A"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React38.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React38.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Collection B"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React38.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React38.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React38.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React38.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React38.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React38.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Automated content"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React38.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React38.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React38.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React38.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Search index"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React38.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React38.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React38.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React38.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React38.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React38.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React38.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Work pages"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React38.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React38.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React38.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React38.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React38.createElement("article", null, /* @__PURE__ */ React38.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React38.createElement("ul", null, /* @__PURE__ */ React38.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React38.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React38.createElement("li", null, "Optional annotation dataset"))))));
3972
+ return /* @__PURE__ */ React40.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React40.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React40.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React40.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Collection A"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React40.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React40.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Collection B"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React40.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React40.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React40.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React40.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React40.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React40.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Automated content"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React40.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React40.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React40.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React40.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Search index"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React40.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React40.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React40.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React40.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React40.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React40.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React40.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Work pages"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React40.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React40.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React40.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React40.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React40.createElement("article", null, /* @__PURE__ */ React40.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React40.createElement("ul", null, /* @__PURE__ */ React40.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React40.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React40.createElement("li", null, "Optional annotation dataset"))))));
3912
3973
  }
3913
3974
 
3914
3975
  // ui/src/docs/ThemeShowcase.jsx
3915
- import React39 from "react";
3976
+ import React41 from "react";
3916
3977
 
3917
3978
  // ../../node_modules/@radix-ui/colors/index.mjs
3918
3979
  var colors_exports = {};
@@ -7745,21 +7806,21 @@ var ACCENT_COLOR_NAMES = [
7745
7806
  "sky"
7746
7807
  ];
7747
7808
  var GRAY_COLOR_NAMES = ["gray", "mauve", "slate", "sage", "olive", "sand"];
7748
- var Section = ({ title, description, children }) => /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React39.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React39.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
7749
- var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React39.createElement("strong", null, label)), /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React39.createElement(
7809
+ var Section = ({ title, description, children }) => /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React41.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React41.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
7810
+ var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React41.createElement("strong", null, label)), /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React41.createElement(
7750
7811
  "div",
7751
7812
  {
7752
7813
  key: `${label}-${stop}`,
7753
7814
  className: "canopy-theme-showcase__scale-stop"
7754
7815
  },
7755
- /* @__PURE__ */ React39.createElement(
7816
+ /* @__PURE__ */ React41.createElement(
7756
7817
  "span",
7757
7818
  {
7758
7819
  className: "canopy-theme-showcase__scale-chip",
7759
7820
  style: { backgroundColor: `var(${prefix}-${stop})` }
7760
7821
  }
7761
7822
  ),
7762
- /* @__PURE__ */ React39.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
7823
+ /* @__PURE__ */ React41.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
7763
7824
  ))));
7764
7825
  function ThemeShowcase() {
7765
7826
  const accentColors = ACCENT_COLOR_NAMES;
@@ -7770,15 +7831,15 @@ function ThemeShowcase() {
7770
7831
  if (!scale) return null;
7771
7832
  return scale[`${name}9`] || Object.values(scale)[8];
7772
7833
  };
7773
- const ColorsLabeled = ({ colors }) => /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
7834
+ const ColorsLabeled = ({ colors }) => /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
7774
7835
  const colorValue = getRadixSwatch(name);
7775
- return /* @__PURE__ */ React39.createElement("div", { key: name, className: "canopy-theme-showcase__swatch" }, /* @__PURE__ */ React39.createElement(
7836
+ return /* @__PURE__ */ React41.createElement("div", { key: name, className: "canopy-theme-showcase__swatch" }, /* @__PURE__ */ React41.createElement(
7776
7837
  "div",
7777
7838
  {
7778
7839
  className: "canopy-theme-showcase__swatch-chip",
7779
7840
  style: { background: colorValue || "var(--color-gray-200)" }
7780
7841
  }
7781
- ), /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase__swatch-label" }, name));
7842
+ ), /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase__swatch-label" }, name));
7782
7843
  }));
7783
7844
  const styles = `
7784
7845
  .canopy-theme-showcase__section {
@@ -7862,13 +7923,13 @@ function ThemeShowcase() {
7862
7923
  font-weight: 300;
7863
7924
  }
7864
7925
  `;
7865
- return /* @__PURE__ */ React39.createElement("div", { className: "canopy-theme-showcase" }, /* @__PURE__ */ React39.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React39.createElement(
7926
+ return /* @__PURE__ */ React41.createElement("div", { className: "canopy-theme-showcase" }, /* @__PURE__ */ React41.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React41.createElement(
7866
7927
  Section,
7867
7928
  {
7868
7929
  title: "Color scales",
7869
7930
  description: "Accent and gray ramps from the active theme."
7870
7931
  },
7871
- /* @__PURE__ */ React39.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React39.createElement(
7932
+ /* @__PURE__ */ React41.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React41.createElement(
7872
7933
  ColorScaleRow,
7873
7934
  {
7874
7935
  key: scale.label,
@@ -7876,20 +7937,20 @@ function ThemeShowcase() {
7876
7937
  prefix: scale.prefix
7877
7938
  }
7878
7939
  )))
7879
- ), /* @__PURE__ */ React39.createElement(
7940
+ ), /* @__PURE__ */ React41.createElement(
7880
7941
  Section,
7881
7942
  {
7882
7943
  title: "Accent color palette options",
7883
7944
  description: "Primary color steps used for buttons, links, and highlights."
7884
7945
  },
7885
- /* @__PURE__ */ React39.createElement(ColorsLabeled, { colors: accentColors })
7886
- ), /* @__PURE__ */ React39.createElement(
7946
+ /* @__PURE__ */ React41.createElement(ColorsLabeled, { colors: accentColors })
7947
+ ), /* @__PURE__ */ React41.createElement(
7887
7948
  Section,
7888
7949
  {
7889
7950
  title: "Gray color palette options",
7890
7951
  description: "Neutral color steps used for backgrounds, borders, and text."
7891
7952
  },
7892
- /* @__PURE__ */ React39.createElement(ColorsLabeled, { colors: grayColors })
7953
+ /* @__PURE__ */ React41.createElement(ColorsLabeled, { colors: grayColors })
7893
7954
  ));
7894
7955
  }
7895
7956
  export {