@canopy-iiif/app 1.6.21 → 1.7.0

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.
@@ -4267,11 +4267,25 @@ TimelinePoint.displayName = "TimelinePoint";
4267
4267
 
4268
4268
  // ui/src/utils/manifestReferences.js
4269
4269
  import React33 from "react";
4270
- import navigationHelpers6 from "@canopy-iiif/app/lib/components/navigation.js";
4271
- import referencedHelpers from "@canopy-iiif/app/lib/components/referenced.js";
4272
- var referencedModule = referencedHelpers && typeof referencedHelpers === "object" ? referencedHelpers : null;
4273
- var buildReferencedItems = referencedModule && typeof referencedModule.buildReferencedItems === "function" ? referencedModule.buildReferencedItems : null;
4274
- var normalizeReferencedManifestList = referencedModule && typeof referencedModule.normalizeReferencedManifestList === "function" ? referencedModule.normalizeReferencedManifestList : null;
4270
+ var CONTEXT_KEY2 = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
4271
+ function getGlobalRoot() {
4272
+ if (typeof globalThis !== "undefined") return globalThis;
4273
+ if (typeof global !== "undefined") return global;
4274
+ if (typeof window !== "undefined") return window;
4275
+ return {};
4276
+ }
4277
+ function getPageContext2() {
4278
+ const root = getGlobalRoot();
4279
+ if (root && root[CONTEXT_KEY2]) return root[CONTEXT_KEY2];
4280
+ const ctx = React33.createContext({
4281
+ navigation: null,
4282
+ page: null,
4283
+ site: null,
4284
+ primaryNavigation: []
4285
+ });
4286
+ if (root) root[CONTEXT_KEY2] = ctx;
4287
+ return ctx;
4288
+ }
4275
4289
  function normalizeManifestId(raw) {
4276
4290
  if (!raw) return "";
4277
4291
  try {
@@ -4288,9 +4302,23 @@ function normalizeManifestId(raw) {
4288
4302
  }
4289
4303
  }
4290
4304
  var PageContextFallback = React33.createContext(null);
4305
+ var referencedModule = null;
4306
+ function getReferencedModule() {
4307
+ if (typeof window !== "undefined") return null;
4308
+ if (referencedModule) return referencedModule;
4309
+ try {
4310
+ const globalReq = typeof globalThis !== "undefined" && globalThis.__canopyRequire || null;
4311
+ if (typeof globalReq === "function") {
4312
+ referencedModule = globalReq("../../../lib/components/referenced.js");
4313
+ return referencedModule;
4314
+ }
4315
+ } catch (_) {
4316
+ referencedModule = null;
4317
+ }
4318
+ return referencedModule;
4319
+ }
4291
4320
  function useReferencedManifestMap() {
4292
- var _a, _b;
4293
- const PageContext = ((_b = (_a = navigationHelpers6) == null ? void 0 : _a.getPageContext) == null ? void 0 : _b.call(_a)) || PageContextFallback;
4321
+ const PageContext = getPageContext2() || PageContextFallback;
4294
4322
  const pageContext = React33.useContext(PageContext);
4295
4323
  const referencedItems = pageContext && pageContext.page && Array.isArray(pageContext.page.referencedItems) ? pageContext.page.referencedItems : [];
4296
4324
  return React33.useMemo(() => {
@@ -4353,32 +4381,37 @@ function resolveReferencedManifests(ids, manifestMap) {
4353
4381
  });
4354
4382
  }
4355
4383
  const missing = order.filter((key) => key && !manifestRecords.has(key));
4356
- if (missing.length && buildReferencedItems) {
4357
- let fallbackItems = [];
4358
- try {
4359
- const normalizedMissing = normalizeReferencedManifestList ? normalizeReferencedManifestList(missing) : missing;
4360
- if (normalizedMissing && normalizedMissing.length) {
4361
- fallbackItems = buildReferencedItems(normalizedMissing) || [];
4384
+ if (missing.length) {
4385
+ const referencedHelpers = getReferencedModule();
4386
+ const buildReferencedItems = referencedHelpers == null ? void 0 : referencedHelpers.buildReferencedItems;
4387
+ const normalizeList = referencedHelpers == null ? void 0 : referencedHelpers.normalizeReferencedManifestList;
4388
+ if (typeof buildReferencedItems === "function") {
4389
+ let fallbackItems = [];
4390
+ try {
4391
+ const normalizedMissing = typeof normalizeList === "function" ? normalizeList(missing) : missing;
4392
+ if (normalizedMissing && normalizedMissing.length) {
4393
+ fallbackItems = buildReferencedItems(normalizedMissing) || [];
4394
+ }
4395
+ } catch (_) {
4396
+ fallbackItems = [];
4362
4397
  }
4363
- } catch (_) {
4364
- fallbackItems = [];
4365
- }
4366
- fallbackItems.forEach((item) => {
4367
- if (!item) return;
4368
- const fallbackKey = normalizeManifestId(item.id || item.href);
4369
- if (!fallbackKey || manifestRecords.has(fallbackKey)) return;
4370
- manifestRecords.set(fallbackKey, {
4371
- id: item.id || item.href || fallbackKey,
4372
- href: item.href || null,
4373
- title: item.title || item.href || "",
4374
- summary: item.summary || "",
4375
- thumbnail: item.thumbnail || null,
4376
- thumbnailWidth: item.thumbnailWidth,
4377
- thumbnailHeight: item.thumbnailHeight,
4378
- type: item.type || "work",
4379
- metadata: Array.isArray(item.metadata) && item.metadata.length ? item.metadata : item.summary ? [item.summary] : []
4398
+ fallbackItems.forEach((item) => {
4399
+ if (!item) return;
4400
+ const fallbackKey = normalizeManifestId(item.id || item.href);
4401
+ if (!fallbackKey || manifestRecords.has(fallbackKey)) return;
4402
+ manifestRecords.set(fallbackKey, {
4403
+ id: item.id || item.href || fallbackKey,
4404
+ href: item.href || null,
4405
+ title: item.title || item.href || "",
4406
+ summary: item.summary || "",
4407
+ thumbnail: item.thumbnail || null,
4408
+ thumbnailWidth: item.thumbnailWidth,
4409
+ thumbnailHeight: item.thumbnailHeight,
4410
+ type: item.type || "work",
4411
+ metadata: Array.isArray(item.metadata) && item.metadata.length ? item.metadata : item.summary ? [item.summary] : []
4412
+ });
4380
4413
  });
4381
- });
4414
+ }
4382
4415
  }
4383
4416
  return order.map((key) => manifestRecords.get(key)).filter(Boolean);
4384
4417
  }
@@ -4711,8 +4744,477 @@ function MdxMap({ children, ...rest }) {
4711
4744
  return /* @__PURE__ */ React35.createElement("div", { "data-canopy-map": "1" }, /* @__PURE__ */ React35.createElement("div", { ...placeholderProps, "aria-live": "polite" }, /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__status" }, "Loading map\u2026")), /* @__PURE__ */ React35.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
4712
4745
  }
4713
4746
 
4714
- // ui/src/search/MdxSearchResults.jsx
4747
+ // ui/src/content/gallery/Gallery.jsx
4715
4748
  import React36 from "react";
4749
+ var INLINE_SCRIPT2 = `(() => {
4750
+ if (typeof window === 'undefined') return;
4751
+ if (window.__canopyGalleryBound) return;
4752
+ window.__canopyGalleryBound = true;
4753
+ const focusableSelector =
4754
+ 'a[href],area[href],button:not([disabled]),input:not([disabled]):not([type="hidden"]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
4755
+ const raf =
4756
+ (window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) ||
4757
+ function (cb) {
4758
+ return window.setTimeout(cb, 0);
4759
+ };
4760
+ let activeModal = null;
4761
+
4762
+ function isVisible(node) {
4763
+ return !!(node && (node.offsetWidth || node.offsetHeight || node.getClientRects().length));
4764
+ }
4765
+
4766
+ function getFocusable(modal) {
4767
+ if (!modal) return [];
4768
+ return Array.prototype.slice
4769
+ .call(modal.querySelectorAll(focusableSelector))
4770
+ .filter((node) => !node.hasAttribute('disabled') && node.getAttribute('aria-hidden') !== 'true' && isVisible(node));
4771
+ }
4772
+
4773
+ function escapeSelector(value) {
4774
+ if (window.CSS && typeof window.CSS.escape === 'function') {
4775
+ return window.CSS.escape(value);
4776
+ }
4777
+ return value.replace(/"/g, '\\"');
4778
+ }
4779
+
4780
+ function findTrigger(modal) {
4781
+ if (!modal || !modal.id) return null;
4782
+ try {
4783
+ return document.querySelector('[data-canopy-gallery-trigger="' + escapeSelector(modal.id) + '"]');
4784
+ } catch (_) {
4785
+ return null;
4786
+ }
4787
+ }
4788
+
4789
+ function focusInitial(modal) {
4790
+ if (!modal) return;
4791
+ const focusables = getFocusable(modal);
4792
+ const target = focusables[0] || modal;
4793
+ raf(() => {
4794
+ try {
4795
+ target.focus({preventScroll: true});
4796
+ } catch (_) {
4797
+ try {
4798
+ target.focus();
4799
+ } catch (err) {}
4800
+ }
4801
+ });
4802
+ }
4803
+
4804
+ function lockScroll() {
4805
+ document.body && document.body.setAttribute('data-canopy-gallery-locked', '1');
4806
+ }
4807
+
4808
+ function unlockScroll() {
4809
+ document.body && document.body.removeAttribute('data-canopy-gallery-locked');
4810
+ }
4811
+
4812
+ function handleKeydown(event) {
4813
+ if (!activeModal) return;
4814
+ if (event.key === 'Escape' || event.key === 'Esc') {
4815
+ event.preventDefault();
4816
+ const closeId = activeModal.getAttribute('data-canopy-gallery-close');
4817
+ if (closeId) {
4818
+ const targetHash = '#' + closeId;
4819
+ if (window.location.hash !== targetHash) {
4820
+ window.location.hash = targetHash;
4821
+ } else {
4822
+ window.location.hash = targetHash;
4823
+ }
4824
+ } else {
4825
+ window.location.hash = '';
4826
+ }
4827
+ return;
4828
+ }
4829
+ if (event.key !== 'Tab') return;
4830
+ const focusables = getFocusable(activeModal);
4831
+ if (!focusables.length) {
4832
+ event.preventDefault();
4833
+ activeModal.focus();
4834
+ return;
4835
+ }
4836
+ const first = focusables[0];
4837
+ const last = focusables[focusables.length - 1];
4838
+ const current = document.activeElement;
4839
+ if (event.shiftKey) {
4840
+ if (current === first || !activeModal.contains(current)) {
4841
+ event.preventDefault();
4842
+ last.focus();
4843
+ }
4844
+ return;
4845
+ }
4846
+ if (current === last || !activeModal.contains(current)) {
4847
+ event.preventDefault();
4848
+ first.focus();
4849
+ }
4850
+ }
4851
+
4852
+ function setActiveModal(modal) {
4853
+ if (modal) {
4854
+ if (!activeModal) {
4855
+ lockScroll();
4856
+ document.addEventListener('keydown', handleKeydown, true);
4857
+ } else if (activeModal !== modal) {
4858
+ activeModal.removeAttribute('data-canopy-gallery-active');
4859
+ }
4860
+ activeModal = modal;
4861
+ modal.setAttribute('data-canopy-gallery-active', '1');
4862
+ focusInitial(modal);
4863
+ return;
4864
+ }
4865
+ if (!activeModal) return;
4866
+ const previous = activeModal;
4867
+ activeModal = null;
4868
+ previous.removeAttribute('data-canopy-gallery-active');
4869
+ unlockScroll();
4870
+ document.removeEventListener('keydown', handleKeydown, true);
4871
+ const closeTarget = previous.getAttribute('data-canopy-gallery-close');
4872
+ if (closeTarget) {
4873
+ const closeHash = '#' + closeTarget;
4874
+ if (window.location.hash === closeHash) {
4875
+ try {
4876
+ if (window.history && typeof window.history.replaceState === 'function') {
4877
+ window.history.replaceState('', document.title, window.location.pathname + window.location.search);
4878
+ } else {
4879
+ window.location.hash = '';
4880
+ }
4881
+ } catch (_) {}
4882
+ }
4883
+ }
4884
+ const trigger = findTrigger(previous);
4885
+ if (trigger && typeof trigger.focus === 'function') {
4886
+ raf(() => {
4887
+ try {
4888
+ trigger.focus({preventScroll: true});
4889
+ } catch (_) {
4890
+ try {
4891
+ trigger.focus();
4892
+ } catch (err) {}
4893
+ }
4894
+ });
4895
+ }
4896
+ }
4897
+
4898
+ function modalFromHash() {
4899
+ const id = window.location.hash.replace(/^#/, '');
4900
+ if (!id) return null;
4901
+ const candidate = document.getElementById(id);
4902
+ if (!candidate) return null;
4903
+ return candidate.hasAttribute('data-canopy-gallery-modal') ? candidate : null;
4904
+ }
4905
+
4906
+ function syncFromHash() {
4907
+ const modal = modalFromHash();
4908
+ if (modal) {
4909
+ setActiveModal(modal);
4910
+ } else {
4911
+ setActiveModal(null);
4912
+ }
4913
+ }
4914
+
4915
+ window.addEventListener('hashchange', syncFromHash);
4916
+ window.addEventListener('pageshow', syncFromHash);
4917
+ syncFromHash();
4918
+ })()`;
4919
+ var galleryInstanceCounter = 0;
4920
+ function nextGalleryInstanceId() {
4921
+ galleryInstanceCounter += 1;
4922
+ return `canopy-gallery-${galleryInstanceCounter}`;
4923
+ }
4924
+ function slugify2(value, fallback) {
4925
+ if (!value) return fallback;
4926
+ const normalized = String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").trim();
4927
+ if (!normalized) return fallback;
4928
+ return normalized;
4929
+ }
4930
+ function ensureArray2(value) {
4931
+ if (!value && value !== 0) return [];
4932
+ return Array.isArray(value) ? value : [value];
4933
+ }
4934
+ function normalizeOrder(value) {
4935
+ const normalized = String(value || "default").toLowerCase();
4936
+ return normalized === "random" ? "random" : "default";
4937
+ }
4938
+ function shuffleItems(list) {
4939
+ const copy = list.slice();
4940
+ for (let i = copy.length - 1; i > 0; i -= 1) {
4941
+ const j = Math.floor(Math.random() * (i + 1));
4942
+ const temp = copy[i];
4943
+ copy[i] = copy[j];
4944
+ copy[j] = temp;
4945
+ }
4946
+ return copy;
4947
+ }
4948
+ function renderMetaList(meta, className) {
4949
+ const entries = ensureArray2(meta).filter((entry) => entry || entry === 0);
4950
+ if (!entries.length) return null;
4951
+ return /* @__PURE__ */ React36.createElement("ul", { className, role: "list" }, entries.map((entry, index) => /* @__PURE__ */ React36.createElement("li", { key: `meta-${index}` }, entry)));
4952
+ }
4953
+ function renderPreview(props = {}) {
4954
+ const source = typeof props.media === "string" && props.media || props.thumbnail || props.src || props.image && props.image.src || props.image;
4955
+ if (source) {
4956
+ const alt = props.thumbnailAlt || props.imageAlt || props.alt || props.title || "";
4957
+ const width = props.thumbnailWidth || props.imageWidth || props.width;
4958
+ const height = props.thumbnailHeight || props.imageHeight || props.height;
4959
+ return /* @__PURE__ */ React36.createElement(
4960
+ "img",
4961
+ {
4962
+ src: source,
4963
+ alt,
4964
+ width,
4965
+ height,
4966
+ loading: "lazy"
4967
+ }
4968
+ );
4969
+ }
4970
+ return /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__placeholder", "aria-hidden": "true" });
4971
+ }
4972
+ function normalizeItem(child, index, galleryId, manifestMap) {
4973
+ var _a;
4974
+ if (!React36.isValidElement(child)) return null;
4975
+ if (child.type !== GalleryItem && ((_a = child.type) == null ? void 0 : _a.displayName) !== "GalleryItem")
4976
+ return null;
4977
+ const props = child.props || {};
4978
+ const manifestValues = Array.isArray(props.referencedManifests) ? props.referencedManifests : props.manifest ? [props.manifest] : Array.isArray(props.manifests) ? props.manifests : [];
4979
+ const manifests = resolveReferencedManifests(manifestValues, manifestMap);
4980
+ const normalizedProps = { ...props };
4981
+ if (!normalizedProps.title) {
4982
+ const manifestWithTitle = manifests.find(
4983
+ (manifest) => manifest && manifest.title
4984
+ );
4985
+ if (manifestWithTitle && manifestWithTitle.title) {
4986
+ normalizedProps.title = manifestWithTitle.title;
4987
+ }
4988
+ }
4989
+ if (!normalizedProps.summary || !normalizedProps.description) {
4990
+ const manifestWithSummary = manifests.find(
4991
+ (manifest) => manifest && manifest.summary
4992
+ );
4993
+ if (manifestWithSummary && manifestWithSummary.summary) {
4994
+ if (!normalizedProps.summary) {
4995
+ normalizedProps.summary = manifestWithSummary.summary;
4996
+ }
4997
+ if (!normalizedProps.description) {
4998
+ normalizedProps.description = manifestWithSummary.summary;
4999
+ }
5000
+ }
5001
+ }
5002
+ if (!normalizedProps.thumbnail && !normalizedProps.media && !normalizedProps.image) {
5003
+ const manifestWithThumb = manifests.find(
5004
+ (manifest) => manifest && manifest.thumbnail
5005
+ );
5006
+ if (manifestWithThumb && manifestWithThumb.thumbnail) {
5007
+ normalizedProps.thumbnail = manifestWithThumb.thumbnail;
5008
+ if (manifestWithThumb.thumbnailWidth != null && normalizedProps.thumbnailWidth == null && normalizedProps.imageWidth == null && normalizedProps.width == null) {
5009
+ normalizedProps.thumbnailWidth = manifestWithThumb.thumbnailWidth;
5010
+ }
5011
+ if (manifestWithThumb.thumbnailHeight != null && normalizedProps.thumbnailHeight == null && normalizedProps.imageHeight == null && normalizedProps.height == null) {
5012
+ normalizedProps.thumbnailHeight = manifestWithThumb.thumbnailHeight;
5013
+ }
5014
+ }
5015
+ }
5016
+ const rawSlug = normalizedProps.slug || normalizedProps.id || normalizedProps.title || `item-${index + 1}`;
5017
+ const slug = slugify2(rawSlug, `item-${index + 1}`);
5018
+ const modalId = `${galleryId}-modal-${slug}-${index + 1}`;
5019
+ const triggerLabel = normalizedProps.triggerLabel || normalizedProps.buttonLabel || normalizedProps.linkLabel || `Open popup for ${normalizedProps.title || `item ${index + 1}`}`;
5020
+ const modalTitleId = `${modalId}-title`;
5021
+ const needsDescriptionId = normalizedProps.popupDescription || normalizedProps.modalDescription || normalizedProps.description || normalizedProps.summary;
5022
+ const modalDescriptionId = needsDescriptionId ? `${modalId}-description` : null;
5023
+ const manifestLinks = manifests.map((manifest) => {
5024
+ if (!manifest) return null;
5025
+ const href = manifest.href || manifest.id || manifest.slug;
5026
+ if (!href) return null;
5027
+ return {
5028
+ id: manifest.id || manifest.slug || href,
5029
+ href,
5030
+ title: manifest.title || manifest.label || href
5031
+ };
5032
+ }).filter(Boolean);
5033
+ return {
5034
+ index,
5035
+ key: child.key != null ? child.key : `${galleryId}-item-${index}`,
5036
+ props: normalizedProps,
5037
+ modalId,
5038
+ slug,
5039
+ modalTitleId,
5040
+ modalDescriptionId,
5041
+ triggerLabel,
5042
+ manifests: manifestLinks
5043
+ };
5044
+ }
5045
+ function buildCaptionContent(itemProps) {
5046
+ if (itemProps.caption) return itemProps.caption;
5047
+ const kicker = itemProps.kicker || itemProps.label || itemProps.eyebrow;
5048
+ const summary = itemProps.summary || itemProps.description;
5049
+ return /* @__PURE__ */ React36.createElement(React36.Fragment, null, kicker ? /* @__PURE__ */ React36.createElement("span", { className: "canopy-gallery__kicker" }, kicker) : null, itemProps.title ? /* @__PURE__ */ React36.createElement("span", { className: "canopy-gallery__title-text" }, itemProps.title) : null, summary ? /* @__PURE__ */ React36.createElement("span", { className: "canopy-gallery__summary" }, summary) : null, renderMetaList(
5050
+ itemProps.meta,
5051
+ "canopy-gallery__meta canopy-gallery__meta--caption"
5052
+ ));
5053
+ }
5054
+ function GalleryModal({ item, total, closeTargetId, prevTarget, nextTarget }) {
5055
+ const {
5056
+ props,
5057
+ modalId,
5058
+ modalTitleId,
5059
+ modalDescriptionId,
5060
+ triggerLabel,
5061
+ index,
5062
+ manifests
5063
+ } = item;
5064
+ const kicker = props.kicker || props.label || props.eyebrow;
5065
+ const summary = props.popupDescription || props.modalDescription || props.description || props.summary || null;
5066
+ const modalTitle = props.popupTitle || props.modalTitle || props.title || `Item ${index + 1}`;
5067
+ const preview = renderPreview(props);
5068
+ return /* @__PURE__ */ React36.createElement(
5069
+ "div",
5070
+ {
5071
+ id: modalId,
5072
+ className: "canopy-gallery__modal",
5073
+ role: "dialog",
5074
+ "aria-modal": "true",
5075
+ "aria-labelledby": modalTitleId,
5076
+ "aria-describedby": modalDescriptionId || void 0,
5077
+ tabIndex: -1,
5078
+ "data-canopy-gallery-modal": "true",
5079
+ "data-canopy-gallery-close": closeTargetId
5080
+ },
5081
+ /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__modal-scrim" }, /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__modal-panel" }, /* @__PURE__ */ React36.createElement("header", { className: "canopy-gallery__modal-header" }, preview ? /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__modal-thumb", "aria-hidden": "true" }, preview) : null, /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__modal-text" }, kicker ? /* @__PURE__ */ React36.createElement("p", { className: "canopy-gallery__modal-kicker" }, kicker) : null, /* @__PURE__ */ React36.createElement("h3", { id: modalTitleId, className: "canopy-gallery__modal-title" }, modalTitle), summary ? /* @__PURE__ */ React36.createElement(
5082
+ "p",
5083
+ {
5084
+ id: modalDescriptionId || void 0,
5085
+ className: "canopy-gallery__modal-summary"
5086
+ },
5087
+ summary
5088
+ ) : null, renderMetaList(
5089
+ props.meta,
5090
+ "canopy-gallery__meta canopy-gallery__meta--modal"
5091
+ ))), /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__modal-body" }, props.children, manifests && manifests.length ? /* @__PURE__ */ React36.createElement("section", { className: "canopy-gallery__referenced" }, /* @__PURE__ */ React36.createElement("h4", null, "Referenced works"), /* @__PURE__ */ React36.createElement("ul", { role: "list" }, manifests.map((manifest) => /* @__PURE__ */ React36.createElement("li", { key: manifest.id || manifest.href }, /* @__PURE__ */ React36.createElement("a", { href: manifest.href }, manifest.title || manifest.href))))) : null), /* @__PURE__ */ React36.createElement("footer", { className: "canopy-gallery__modal-footer" }, /* @__PURE__ */ React36.createElement(
5092
+ "a",
5093
+ {
5094
+ className: "canopy-gallery__modal-close",
5095
+ href: `#${closeTargetId}`,
5096
+ "aria-label": `Close popup for ${modalTitle}`
5097
+ },
5098
+ "Close"
5099
+ ), total > 1 ? /* @__PURE__ */ React36.createElement(
5100
+ "nav",
5101
+ {
5102
+ className: "canopy-gallery__modal-nav",
5103
+ "aria-label": "Gallery popup navigation"
5104
+ },
5105
+ /* @__PURE__ */ React36.createElement(
5106
+ "a",
5107
+ {
5108
+ className: "canopy-gallery__modal-nav-link canopy-gallery__modal-nav-link--prev",
5109
+ href: `#${prevTarget}`,
5110
+ "aria-label": `Show previous popup before ${modalTitle}`
5111
+ },
5112
+ "Prev"
5113
+ ),
5114
+ /* @__PURE__ */ React36.createElement(
5115
+ "a",
5116
+ {
5117
+ className: "canopy-gallery__modal-nav-link canopy-gallery__modal-nav-link--next",
5118
+ href: `#${nextTarget}`,
5119
+ "aria-label": `Show next popup after ${modalTitle}`
5120
+ },
5121
+ "Next"
5122
+ )
5123
+ ) : null)))
5124
+ );
5125
+ }
5126
+ function GalleryFigure({ item }) {
5127
+ const { props, modalId, triggerLabel } = item;
5128
+ return /* @__PURE__ */ React36.createElement(
5129
+ "figure",
5130
+ {
5131
+ className: "canopy-gallery__item",
5132
+ "data-gallery-item-index": item.index
5133
+ },
5134
+ /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__media" }, renderPreview(props)),
5135
+ /* @__PURE__ */ React36.createElement("figcaption", { className: "canopy-gallery__caption" }, buildCaptionContent(props)),
5136
+ /* @__PURE__ */ React36.createElement(
5137
+ "a",
5138
+ {
5139
+ className: "canopy-gallery__trigger",
5140
+ href: `#${modalId}`,
5141
+ "aria-haspopup": "dialog",
5142
+ "aria-controls": modalId,
5143
+ "aria-label": triggerLabel,
5144
+ "data-canopy-gallery-trigger": modalId
5145
+ },
5146
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-gallery__trigger-label" }, triggerLabel)
5147
+ )
5148
+ );
5149
+ }
5150
+ function GalleryItem() {
5151
+ return null;
5152
+ }
5153
+ GalleryItem.displayName = "GalleryItem";
5154
+ function normalizePopupSize(value) {
5155
+ const normalized = String(value || "full").toLowerCase();
5156
+ return normalized === "medium" ? "medium" : "full";
5157
+ }
5158
+ function Gallery({
5159
+ children,
5160
+ id,
5161
+ title,
5162
+ description,
5163
+ popupSize = "full",
5164
+ order = "default",
5165
+ className = "",
5166
+ style = {}
5167
+ }) {
5168
+ const manifestMap = useReferencedManifestMap();
5169
+ const galleryId = id ? String(id) : nextGalleryInstanceId();
5170
+ const HeadingTag = "h3";
5171
+ const closeTargetId = `${galleryId}-close`;
5172
+ const childArray = React36.Children.toArray(children);
5173
+ const items = childArray.map((child, index) => normalizeItem(child, index, galleryId, manifestMap)).filter(Boolean);
5174
+ if (!items.length) return null;
5175
+ const popupMode = normalizePopupSize(popupSize);
5176
+ const orderMode = normalizeOrder(order);
5177
+ const orderedItems = orderMode === "random" ? shuffleItems(items) : items;
5178
+ const rootClassName = [
5179
+ "canopy-gallery",
5180
+ popupMode === "medium" ? "canopy-gallery--popup-medium" : "canopy-gallery--popup-full",
5181
+ className
5182
+ ].filter(Boolean).join(" ");
5183
+ return /* @__PURE__ */ React36.createElement("section", { className: rootClassName, style, "data-canopy-gallery": "true" }, /* @__PURE__ */ React36.createElement(
5184
+ "div",
5185
+ {
5186
+ id: closeTargetId,
5187
+ className: "canopy-gallery__close-anchor",
5188
+ "aria-hidden": "true",
5189
+ tabIndex: -1
5190
+ }
5191
+ ), (title || description) && /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__header" }, title ? /* @__PURE__ */ React36.createElement(HeadingTag, { className: "canopy-gallery__heading" }, title) : null, description ? /* @__PURE__ */ React36.createElement("p", { className: "canopy-gallery__description" }, description) : null), /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__grid" }, orderedItems.map((item) => /* @__PURE__ */ React36.createElement(GalleryFigure, { key: item.key, item }))), /* @__PURE__ */ React36.createElement("div", { className: "canopy-gallery__modals" }, orderedItems.map((item, index) => {
5192
+ const total = orderedItems.length;
5193
+ const prevIndex = (index - 1 + total) % total;
5194
+ const nextIndex = (index + 1) % total;
5195
+ return /* @__PURE__ */ React36.createElement(
5196
+ GalleryModal,
5197
+ {
5198
+ key: `${item.modalId}-modal`,
5199
+ item,
5200
+ total,
5201
+ closeTargetId,
5202
+ prevTarget: orderedItems[prevIndex].modalId,
5203
+ nextTarget: orderedItems[nextIndex].modalId
5204
+ }
5205
+ );
5206
+ })), /* @__PURE__ */ React36.createElement(
5207
+ "script",
5208
+ {
5209
+ "data-canopy-gallery-script": "true",
5210
+ dangerouslySetInnerHTML: { __html: INLINE_SCRIPT2 }
5211
+ }
5212
+ ));
5213
+ }
5214
+ Gallery.Item = GalleryItem;
5215
+
5216
+ // ui/src/search/MdxSearchResults.jsx
5217
+ import React37 from "react";
4716
5218
  function MdxSearchResults(props) {
4717
5219
  let json = "{}";
4718
5220
  try {
@@ -4720,11 +5222,11 @@ function MdxSearchResults(props) {
4720
5222
  } catch (_) {
4721
5223
  json = "{}";
4722
5224
  }
4723
- return /* @__PURE__ */ React36.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React36.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
5225
+ return /* @__PURE__ */ React37.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React37.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
4724
5226
  }
4725
5227
 
4726
5228
  // ui/src/search/SearchSummary.jsx
4727
- import React37 from "react";
5229
+ import React38 from "react";
4728
5230
  function SearchSummary(props) {
4729
5231
  let json = "{}";
4730
5232
  try {
@@ -4732,11 +5234,11 @@ function SearchSummary(props) {
4732
5234
  } catch (_) {
4733
5235
  json = "{}";
4734
5236
  }
4735
- return /* @__PURE__ */ React37.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React37.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
5237
+ return /* @__PURE__ */ React38.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React38.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
4736
5238
  }
4737
5239
 
4738
5240
  // ui/src/search/MdxSearchTabs.jsx
4739
- import React38 from "react";
5241
+ import React39 from "react";
4740
5242
  function MdxSearchTabs(props) {
4741
5243
  let json = "{}";
4742
5244
  try {
@@ -4744,11 +5246,11 @@ function MdxSearchTabs(props) {
4744
5246
  } catch (_) {
4745
5247
  json = "{}";
4746
5248
  }
4747
- return /* @__PURE__ */ React38.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React38.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
5249
+ return /* @__PURE__ */ React39.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React39.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
4748
5250
  }
4749
5251
 
4750
5252
  // ui/src/search/MdxSearch.jsx
4751
- import React39 from "react";
5253
+ import React40 from "react";
4752
5254
  function MdxSearch(props = {}) {
4753
5255
  const {
4754
5256
  layout,
@@ -4766,11 +5268,11 @@ function MdxSearch(props = {}) {
4766
5268
  resultsPayload.layout = layout;
4767
5269
  }
4768
5270
  const classes = ["canopy-search", className].filter(Boolean).join(" ");
4769
- return /* @__PURE__ */ React39.createElement("section", { className: classes, "data-canopy-search": "1" }, showTabs ? /* @__PURE__ */ React39.createElement(MdxSearchTabs, { ...tabsProps }) : null, showSummary ? /* @__PURE__ */ React39.createElement(SearchSummary, { ...summaryProps }) : null, showResults ? /* @__PURE__ */ React39.createElement(MdxSearchResults, { ...resultsPayload }) : null, children || null);
5271
+ return /* @__PURE__ */ React40.createElement("section", { className: classes, "data-canopy-search": "1" }, showTabs ? /* @__PURE__ */ React40.createElement(MdxSearchTabs, { ...tabsProps }) : null, showSummary ? /* @__PURE__ */ React40.createElement(SearchSummary, { ...summaryProps }) : null, showResults ? /* @__PURE__ */ React40.createElement(MdxSearchResults, { ...resultsPayload }) : null, children || null);
4770
5272
  }
4771
5273
 
4772
5274
  // ui/src/search-form/MdxSearchFormModal.jsx
4773
- import React40 from "react";
5275
+ import React41 from "react";
4774
5276
  function MdxSearchFormModal(props = {}) {
4775
5277
  const {
4776
5278
  placeholder = "Search\u2026",
@@ -4786,12 +5288,12 @@ function MdxSearchFormModal(props = {}) {
4786
5288
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
4787
5289
  const resolvedSearchPath = resolveSearchPath(searchPath);
4788
5290
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
4789
- return /* @__PURE__ */ React40.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React40.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React40.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React40.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React40.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
5291
+ return /* @__PURE__ */ React41.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React41.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React41.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React41.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React41.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
4790
5292
  }
4791
5293
 
4792
5294
  // ui/src/iiif/ManifestPrimitives.jsx
4793
5295
  var import_slugify2 = __toESM(require_slugify());
4794
- import React41 from "react";
5296
+ import React42 from "react";
4795
5297
  import {
4796
5298
  Label as CloverLabel,
4797
5299
  Metadata as CloverMetadata,
@@ -4932,7 +5434,7 @@ function MetadataFacetLink(props) {
4932
5434
  const valueSlug = facetSlug ? toValueSlug(text) : "";
4933
5435
  const href = facetSlug && valueSlug ? buildFacetSearchHref(facetSlug, valueSlug) : "";
4934
5436
  if (!href) return text;
4935
- return /* @__PURE__ */ React41.createElement(
5437
+ return /* @__PURE__ */ React42.createElement(
4936
5438
  "a",
4937
5439
  {
4938
5440
  href,
@@ -4958,7 +5460,7 @@ function buildFacetCustomValueContent(items, manifest) {
4958
5460
  seen.add(normalized);
4959
5461
  custom.push({
4960
5462
  matchingLabel: item.label,
4961
- Content: /* @__PURE__ */ React41.createElement(MetadataFacetLink, { facetSlug: facet.slug })
5463
+ Content: /* @__PURE__ */ React42.createElement(MetadataFacetLink, { facetSlug: facet.slug })
4962
5464
  });
4963
5465
  }
4964
5466
  return custom;
@@ -4990,12 +5492,12 @@ function mergeCustomValueContent(userContent, autoContent) {
4990
5492
  function Label({ manifest, label, ...rest }) {
4991
5493
  const intl = label || manifest && manifest.label;
4992
5494
  if (!hasInternationalValue(intl)) return null;
4993
- return /* @__PURE__ */ React41.createElement(CloverLabel, { label: intl, ...rest });
5495
+ return /* @__PURE__ */ React42.createElement(CloverLabel, { label: intl, ...rest });
4994
5496
  }
4995
5497
  function Summary({ manifest, summary, ...rest }) {
4996
5498
  const intl = summary || manifest && manifest.summary;
4997
5499
  if (!hasInternationalValue(intl)) return null;
4998
- return /* @__PURE__ */ React41.createElement(CloverSummary, { summary: intl, ...rest });
5500
+ return /* @__PURE__ */ React42.createElement(CloverSummary, { summary: intl, ...rest });
4999
5501
  }
5000
5502
  function Metadata({ manifest, metadata, customValueContent, ...rest }) {
5001
5503
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
@@ -5005,7 +5507,7 @@ function Metadata({ manifest, metadata, customValueContent, ...rest }) {
5005
5507
  customValueContent,
5006
5508
  autoCustomContent
5007
5509
  );
5008
- return /* @__PURE__ */ React41.createElement(
5510
+ return /* @__PURE__ */ React42.createElement(
5009
5511
  CloverMetadata,
5010
5512
  {
5011
5513
  metadata: items,
@@ -5019,17 +5521,17 @@ function RequiredStatement({ manifest, requiredStatement, ...rest }) {
5019
5521
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
5020
5522
  return null;
5021
5523
  }
5022
- return /* @__PURE__ */ React41.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
5524
+ return /* @__PURE__ */ React42.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
5023
5525
  }
5024
5526
 
5025
5527
  // ui/src/iiif/Properties/Id.jsx
5026
- import React42 from "react";
5528
+ import React43 from "react";
5027
5529
  function Id({ title = "IIIF Manifest", id, ...props }) {
5028
- return /* @__PURE__ */ React42.createElement("dl", null, /* @__PURE__ */ React42.createElement("dt", null, title), /* @__PURE__ */ React42.createElement("dd", null, /* @__PURE__ */ React42.createElement("a", { href: id }, id)));
5530
+ return /* @__PURE__ */ React43.createElement("dl", null, /* @__PURE__ */ React43.createElement("dt", null, title), /* @__PURE__ */ React43.createElement("dd", null, /* @__PURE__ */ React43.createElement("a", { href: id }, id)));
5029
5531
  }
5030
5532
 
5031
5533
  // ui/src/docs/CodeBlock.jsx
5032
- import React43 from "react";
5534
+ import React44 from "react";
5033
5535
  function parseHighlightAttr(attr) {
5034
5536
  if (!attr) return /* @__PURE__ */ new Set();
5035
5537
  const cleaned = String(attr || "").trim();
@@ -5075,10 +5577,10 @@ var highlightBaseStyle = {
5075
5577
  };
5076
5578
  function DocsCodeBlock(props = {}) {
5077
5579
  const { children, ...rest } = props;
5078
- const childArray = React43.Children.toArray(children);
5079
- const codeElement = childArray.find((el) => React43.isValidElement(el));
5580
+ const childArray = React44.Children.toArray(children);
5581
+ const codeElement = childArray.find((el) => React44.isValidElement(el));
5080
5582
  if (!codeElement || !codeElement.props) {
5081
- return React43.createElement("pre", props);
5583
+ return React44.createElement("pre", props);
5082
5584
  }
5083
5585
  const {
5084
5586
  className = "",
@@ -5093,9 +5595,9 @@ function DocsCodeBlock(props = {}) {
5093
5595
  const highlightSet = parseHighlightAttr(highlightAttr);
5094
5596
  const copyAttr = codeProps["data-copy"];
5095
5597
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
5096
- const [copied, setCopied] = React43.useState(false);
5097
- const buttonRef = React43.useRef(null);
5098
- const handleCopy = React43.useCallback(async () => {
5598
+ const [copied, setCopied] = React44.useState(false);
5599
+ const buttonRef = React44.useRef(null);
5600
+ const handleCopy = React44.useCallback(async () => {
5099
5601
  const text = trimmedCode;
5100
5602
  try {
5101
5603
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -5117,12 +5619,12 @@ function DocsCodeBlock(props = {}) {
5117
5619
  setCopied(false);
5118
5620
  }
5119
5621
  }, [trimmedCode]);
5120
- React43.useEffect(() => {
5622
+ React44.useEffect(() => {
5121
5623
  if (buttonRef.current) {
5122
5624
  buttonRef.current.setAttribute("data-docs-copy-hydrated", "true");
5123
5625
  }
5124
5626
  }, []);
5125
- React43.useEffect(() => {
5627
+ React44.useEffect(() => {
5126
5628
  if (!buttonRef.current) return;
5127
5629
  if (copied) buttonRef.current.setAttribute("data-docs-copy-active", "true");
5128
5630
  else buttonRef.current.removeAttribute("data-docs-copy-active");
@@ -5185,27 +5687,27 @@ function DocsCodeBlock(props = {}) {
5185
5687
  const highlight = highlightSet.has(lineNumber);
5186
5688
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
5187
5689
  const displayLine = line === "" ? " " : line;
5188
- return React43.createElement(
5690
+ return React44.createElement(
5189
5691
  "span",
5190
5692
  {
5191
5693
  key: lineNumber,
5192
5694
  style,
5193
5695
  "data-docs-code-line": line
5194
5696
  },
5195
- React43.createElement("span", { style: lineContentStyle }, displayLine)
5697
+ React44.createElement("span", { style: lineContentStyle }, displayLine)
5196
5698
  );
5197
5699
  });
5198
- return React43.createElement(
5700
+ return React44.createElement(
5199
5701
  "div",
5200
5702
  {
5201
5703
  style: containerStyle,
5202
5704
  "data-docs-code-block": "true"
5203
5705
  },
5204
- showHeader ? React43.createElement(
5706
+ showHeader ? React44.createElement(
5205
5707
  "div",
5206
5708
  { style: headerStyle },
5207
- React43.createElement("span", null, showFilename ? filename : null),
5208
- enableCopy ? React43.createElement(
5709
+ React44.createElement("span", null, showFilename ? filename : null),
5710
+ enableCopy ? React44.createElement(
5209
5711
  "button",
5210
5712
  {
5211
5713
  ref: buttonRef,
@@ -5216,8 +5718,8 @@ function DocsCodeBlock(props = {}) {
5216
5718
  "data-docs-copy-button": "true",
5217
5719
  style: buttonStyle
5218
5720
  },
5219
- React43.createElement("span", null, "Copy"),
5220
- React43.createElement(
5721
+ React44.createElement("span", null, "Copy"),
5722
+ React44.createElement(
5221
5723
  "span",
5222
5724
  {
5223
5725
  "aria-hidden": "true",
@@ -5227,7 +5729,7 @@ function DocsCodeBlock(props = {}) {
5227
5729
  )
5228
5730
  ) : null
5229
5731
  ) : null,
5230
- enableCopy ? React43.createElement("textarea", {
5732
+ enableCopy ? React44.createElement("textarea", {
5231
5733
  "data-docs-copy-source": "true",
5232
5734
  tabIndex: -1,
5233
5735
  readOnly: true,
@@ -5242,29 +5744,29 @@ function DocsCodeBlock(props = {}) {
5242
5744
  pointerEvents: "none"
5243
5745
  }
5244
5746
  }) : null,
5245
- React43.createElement(
5747
+ React44.createElement(
5246
5748
  "pre",
5247
5749
  { ...preRest, className: preClassName, style: mergedPreStyle },
5248
- React43.createElement("code", { style: codeStyle }, lineElements)
5750
+ React44.createElement("code", { style: codeStyle }, lineElements)
5249
5751
  )
5250
5752
  );
5251
5753
  }
5252
5754
 
5253
5755
  // ui/src/docs/MarkdownTable.jsx
5254
- import React44 from "react";
5756
+ import React45 from "react";
5255
5757
  function MarkdownTable({ className = "", ...rest }) {
5256
5758
  const merged = ["markdown-table", className].filter(Boolean).join(" ");
5257
- return /* @__PURE__ */ React44.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React44.createElement("table", { className: merged, ...rest }));
5759
+ return /* @__PURE__ */ React45.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React45.createElement("table", { className: merged, ...rest }));
5258
5760
  }
5259
5761
 
5260
5762
  // ui/src/docs/Diagram.jsx
5261
- import React45 from "react";
5763
+ import React46 from "react";
5262
5764
  function CanopyDiagram() {
5263
- return /* @__PURE__ */ React45.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React45.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React45.createElement("h3", null, "IIIF Providers"), /* @__PURE__ */ React45.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 45 manifests while 5 manifests are directly retrieved as-is via IIIF endpoints."), /* @__PURE__ */ React45.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Collection A"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "30 Manifests"), /* @__PURE__ */ React45.createElement("li", null, /* @__PURE__ */ React45.createElement("em", null, "Manuscripts")))), /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Collection B"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "15 Manifests"), /* @__PURE__ */ React45.createElement("li", null, /* @__PURE__ */ React45.createElement("em", null, "Portraits")))), /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Manifests (direct)"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "5 Manifests"), /* @__PURE__ */ React45.createElement("li", null, /* @__PURE__ */ React45.createElement("em", null, "Scrapbooks")))))), /* @__PURE__ */ React45.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React45.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React45.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React45.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React45.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React45.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy retrieves collections and syncs all manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React45.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Automated content"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "50 manifests \u2192 50 work pages"), /* @__PURE__ */ React45.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React45.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React45.createElement("li", null, "Author narratives"), /* @__PURE__ */ React45.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Search index"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React45.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React45.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React45.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React45.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React45.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React45.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React45.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React45.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__ */ React45.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Work pages"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "50 generated HTML pages"), /* @__PURE__ */ React45.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React45.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React45.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React45.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React45.createElement("article", null, /* @__PURE__ */ React45.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React45.createElement("ul", null, /* @__PURE__ */ React45.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React45.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React45.createElement("li", null, "Optional annotation dataset"))))));
5765
+ return /* @__PURE__ */ React46.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React46.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React46.createElement("h3", null, "IIIF Providers"), /* @__PURE__ */ React46.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 45 manifests while 5 manifests are directly retrieved as-is via IIIF endpoints."), /* @__PURE__ */ React46.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Collection A"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "30 Manifests"), /* @__PURE__ */ React46.createElement("li", null, /* @__PURE__ */ React46.createElement("em", null, "Manuscripts")))), /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Collection B"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "15 Manifests"), /* @__PURE__ */ React46.createElement("li", null, /* @__PURE__ */ React46.createElement("em", null, "Portraits")))), /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Manifests (direct)"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "5 Manifests"), /* @__PURE__ */ React46.createElement("li", null, /* @__PURE__ */ React46.createElement("em", null, "Scrapbooks")))))), /* @__PURE__ */ React46.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React46.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React46.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React46.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React46.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React46.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy retrieves collections and syncs all manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React46.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Automated content"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "50 manifests \u2192 50 work pages"), /* @__PURE__ */ React46.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React46.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React46.createElement("li", null, "Author narratives"), /* @__PURE__ */ React46.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Search index"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React46.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React46.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React46.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React46.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React46.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React46.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React46.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React46.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__ */ React46.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Work pages"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "50 generated HTML pages"), /* @__PURE__ */ React46.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React46.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React46.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React46.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React46.createElement("article", null, /* @__PURE__ */ React46.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React46.createElement("ul", null, /* @__PURE__ */ React46.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React46.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React46.createElement("li", null, "Optional annotation dataset"))))));
5264
5766
  }
5265
5767
 
5266
5768
  // ui/src/docs/ThemeShowcase.jsx
5267
- import React46 from "react";
5769
+ import React47 from "react";
5268
5770
 
5269
5771
  // ../../node_modules/@radix-ui/colors/index.mjs
5270
5772
  var colors_exports = {};
@@ -9116,21 +9618,21 @@ var STEP_MAP = {
9116
9618
  800: 11,
9117
9619
  900: 12
9118
9620
  };
9119
- var Section = ({ title, description, children }) => /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React46.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React46.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
9120
- var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React46.createElement("strong", null, label)), /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React46.createElement(
9621
+ var Section = ({ title, description, children }) => /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React47.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React47.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
9622
+ var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React47.createElement("strong", null, label)), /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React47.createElement(
9121
9623
  "div",
9122
9624
  {
9123
9625
  key: `${label}-${stop}`,
9124
9626
  className: "canopy-theme-showcase__scale-stop"
9125
9627
  },
9126
- /* @__PURE__ */ React46.createElement(
9628
+ /* @__PURE__ */ React47.createElement(
9127
9629
  "span",
9128
9630
  {
9129
9631
  className: "canopy-theme-showcase__scale-chip",
9130
9632
  style: { backgroundColor: `var(${prefix}-${stop})` }
9131
9633
  }
9132
9634
  ),
9133
- /* @__PURE__ */ React46.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
9635
+ /* @__PURE__ */ React47.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
9134
9636
  ))));
9135
9637
  var AVAILABLE = new Set(
9136
9638
  Object.keys(colors_exports).filter(
@@ -9195,9 +9697,9 @@ var PREVIEW_DATA = buildPreviewData();
9195
9697
  function encodeJson(value) {
9196
9698
  return JSON.stringify(value).replace(/</g, "\\u003c");
9197
9699
  }
9198
- var ColorsLabeled = ({ colors, type, getRadixSwatch }) => /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
9700
+ var ColorsLabeled = ({ colors, type, getRadixSwatch }) => /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
9199
9701
  const colorValue = getRadixSwatch(name);
9200
- return /* @__PURE__ */ React46.createElement(
9702
+ return /* @__PURE__ */ React47.createElement(
9201
9703
  "button",
9202
9704
  {
9203
9705
  key: `${type}-${name}`,
@@ -9208,14 +9710,14 @@ var ColorsLabeled = ({ colors, type, getRadixSwatch }) => /* @__PURE__ */ React4
9208
9710
  "data-theme-swatch-value": name,
9209
9711
  "aria-pressed": "false"
9210
9712
  },
9211
- /* @__PURE__ */ React46.createElement(
9713
+ /* @__PURE__ */ React47.createElement(
9212
9714
  "span",
9213
9715
  {
9214
9716
  className: "canopy-theme-showcase__swatch-chip",
9215
9717
  style: { background: colorValue || "var(--color-gray-200)" }
9216
9718
  }
9217
9719
  ),
9218
- /* @__PURE__ */ React46.createElement("span", { className: "canopy-theme-showcase__swatch-label" }, name)
9720
+ /* @__PURE__ */ React47.createElement("span", { className: "canopy-theme-showcase__swatch-label" }, name)
9219
9721
  );
9220
9722
  }));
9221
9723
  function ThemeShowcase() {
@@ -9375,7 +9877,7 @@ function ThemeShowcase() {
9375
9877
  .canopy-theme-showcase__swatch-controls { display: none; }
9376
9878
  .canopy-theme-showcase__clear-button { display: none; }
9377
9879
  `;
9378
- return /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase", "data-theme-showcase": true }, /* @__PURE__ */ React46.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React46.createElement(
9880
+ return /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase", "data-theme-showcase": true }, /* @__PURE__ */ React47.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React47.createElement(
9379
9881
  "div",
9380
9882
  {
9381
9883
  style: {
@@ -9386,18 +9888,18 @@ function ThemeShowcase() {
9386
9888
  marginBottom: "1rem"
9387
9889
  }
9388
9890
  },
9389
- /* @__PURE__ */ React46.createElement(
9891
+ /* @__PURE__ */ React47.createElement(
9390
9892
  Section,
9391
9893
  {
9392
9894
  title: "Appearance",
9393
9895
  description: "Pick the base light or dark mode for the theme preview."
9394
9896
  },
9395
- /* @__PURE__ */ React46.createElement("div", { className: "canopy-theme-showcase__appearance-buttons" }, ["light", "dark"].map((mode) => {
9897
+ /* @__PURE__ */ React47.createElement("div", { className: "canopy-theme-showcase__appearance-buttons" }, ["light", "dark"].map((mode) => {
9396
9898
  const label = `${mode.charAt(0).toUpperCase()}${mode.slice(1)}`;
9397
9899
  const baseClass = "canopy-theme-showcase__appearance-button";
9398
9900
  const isDefault = mode === DEFAULTS.appearance;
9399
9901
  const className = isDefault ? `${baseClass} is-active` : baseClass;
9400
- return /* @__PURE__ */ React46.createElement(
9902
+ return /* @__PURE__ */ React47.createElement(
9401
9903
  "button",
9402
9904
  {
9403
9905
  key: mode,
@@ -9409,7 +9911,7 @@ function ThemeShowcase() {
9409
9911
  );
9410
9912
  }))
9411
9913
  ),
9412
- /* @__PURE__ */ React46.createElement(
9914
+ /* @__PURE__ */ React47.createElement(
9413
9915
  "button",
9414
9916
  {
9415
9917
  type: "button",
@@ -9418,13 +9920,13 @@ function ThemeShowcase() {
9418
9920
  },
9419
9921
  "Reset"
9420
9922
  )
9421
- ), /* @__PURE__ */ React46.createElement(
9923
+ ), /* @__PURE__ */ React47.createElement(
9422
9924
  Section,
9423
9925
  {
9424
9926
  title: "Color scales",
9425
9927
  description: "Accent and gray ramps from the active theme."
9426
9928
  },
9427
- /* @__PURE__ */ React46.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React46.createElement(
9929
+ /* @__PURE__ */ React47.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React47.createElement(
9428
9930
  ColorScaleRow,
9429
9931
  {
9430
9932
  key: scale.label,
@@ -9432,13 +9934,13 @@ function ThemeShowcase() {
9432
9934
  prefix: scale.prefix
9433
9935
  }
9434
9936
  )))
9435
- ), /* @__PURE__ */ React46.createElement(
9937
+ ), /* @__PURE__ */ React47.createElement(
9436
9938
  Section,
9437
9939
  {
9438
9940
  title: "Accent color palette options",
9439
9941
  description: "Click a swatch to temporarily override the accent palette."
9440
9942
  },
9441
- /* @__PURE__ */ React46.createElement(
9943
+ /* @__PURE__ */ React47.createElement(
9442
9944
  ColorsLabeled,
9443
9945
  {
9444
9946
  colors: accentColors,
@@ -9446,13 +9948,13 @@ function ThemeShowcase() {
9446
9948
  getRadixSwatch
9447
9949
  }
9448
9950
  )
9449
- ), /* @__PURE__ */ React46.createElement(
9951
+ ), /* @__PURE__ */ React47.createElement(
9450
9952
  Section,
9451
9953
  {
9452
9954
  title: "Gray color palette options",
9453
9955
  description: "Click a swatch to preview the neutral ramp for surfaces and text."
9454
9956
  },
9455
- /* @__PURE__ */ React46.createElement(
9957
+ /* @__PURE__ */ React47.createElement(
9456
9958
  ColorsLabeled,
9457
9959
  {
9458
9960
  colors: grayColors,
@@ -9460,7 +9962,7 @@ function ThemeShowcase() {
9460
9962
  getRadixSwatch
9461
9963
  }
9462
9964
  )
9463
- ), /* @__PURE__ */ React46.createElement(
9965
+ ), /* @__PURE__ */ React47.createElement(
9464
9966
  "script",
9465
9967
  {
9466
9968
  type: "application/json",
@@ -9482,6 +9984,8 @@ export {
9482
9984
  Container,
9483
9985
  DocsCodeBlock,
9484
9986
  MarkdownTable as DocsMarkdownTable,
9987
+ Gallery,
9988
+ GalleryItem,
9485
9989
  GoogleAnalytics,
9486
9990
  HelloWorld,
9487
9991
  Id,