@canopy-iiif/app 0.9.14 → 0.9.15

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.
@@ -153,8 +153,45 @@ var Scroll = (props) => {
153
153
  return /* @__PURE__ */ React4.createElement(CloverScroll, { ...props });
154
154
  };
155
155
 
156
+ // ui/src/iiif/Image.jsx
157
+ import React5, { useEffect as useEffect4, useState as useState4 } from "react";
158
+ var Image = (props) => {
159
+ const [CloverImage, setCloverImage] = useState4(null);
160
+ useEffect4(() => {
161
+ let mounted = true;
162
+ const canUseDom = typeof window !== "undefined" && typeof document !== "undefined";
163
+ if (canUseDom) {
164
+ import("@samvera/clover-iiif/image").then((mod) => {
165
+ if (!mounted) return;
166
+ const Comp = mod && (mod.default || mod.Image || mod);
167
+ setCloverImage(() => Comp);
168
+ }).catch(() => {
169
+ });
170
+ }
171
+ return () => {
172
+ mounted = false;
173
+ };
174
+ }, []);
175
+ if (!CloverImage) {
176
+ let json = "{}";
177
+ try {
178
+ json = JSON.stringify(props || {});
179
+ } catch (_) {
180
+ json = "{}";
181
+ }
182
+ return /* @__PURE__ */ React5.createElement("div", { "data-canopy-image": "1", className: "not-prose" }, /* @__PURE__ */ React5.createElement(
183
+ "script",
184
+ {
185
+ type: "application/json",
186
+ dangerouslySetInnerHTML: { __html: json }
187
+ }
188
+ ));
189
+ }
190
+ return /* @__PURE__ */ React5.createElement(CloverImage, { ...props });
191
+ };
192
+
156
193
  // ui/src/iiif/MdxRelatedItems.jsx
157
- import React5 from "react";
194
+ import React6 from "react";
158
195
  function MdxRelatedItems(props) {
159
196
  let json = "{}";
160
197
  try {
@@ -162,7 +199,7 @@ function MdxRelatedItems(props) {
162
199
  } catch (_) {
163
200
  json = "{}";
164
201
  }
165
- return /* @__PURE__ */ React5.createElement("div", { "data-canopy-related-items": "1", className: "not-prose" }, /* @__PURE__ */ React5.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
202
+ return /* @__PURE__ */ React6.createElement("div", { "data-canopy-related-items": "1", className: "not-prose" }, /* @__PURE__ */ React6.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
166
203
  }
167
204
 
168
205
  // ui/src/interstitials/index.js
@@ -173,7 +210,7 @@ __export(interstitials_exports, {
173
210
  });
174
211
 
175
212
  // ui/src/interstitials/Hero.jsx
176
- import React8 from "react";
213
+ import React9 from "react";
177
214
  import helpers from "@canopy-iiif/app/lib/components/featured.js";
178
215
 
179
216
  // ui/src/interstitials/hero-utils.js
@@ -184,7 +221,7 @@ function computeHeroHeightStyle(height) {
184
221
  }
185
222
 
186
223
  // ui/src/layout/Button.jsx
187
- import React6 from "react";
224
+ import React7 from "react";
188
225
  var VARIANTS = /* @__PURE__ */ new Set(["primary", "secondary"]);
189
226
  function Button({
190
227
  label,
@@ -205,7 +242,7 @@ function Button({
205
242
  `canopy-button--${resolvedVariant}`,
206
243
  className
207
244
  ].filter(Boolean).join(" ");
208
- return /* @__PURE__ */ React6.createElement(
245
+ return /* @__PURE__ */ React7.createElement(
209
246
  "a",
210
247
  {
211
248
  href,
@@ -219,13 +256,14 @@ function Button({
219
256
  }
220
257
 
221
258
  // ui/src/layout/ButtonWrapper.jsx
222
- import React7 from "react";
259
+ import React8 from "react";
223
260
  function ButtonWrapper({ className = "", children, ...rest }) {
224
261
  const classes = ["canopy-button-group", className].filter(Boolean).join(" ");
225
- return /* @__PURE__ */ React7.createElement("div", { className: classes, ...rest }, children);
262
+ return /* @__PURE__ */ React8.createElement("div", { className: classes, ...rest }, children);
226
263
  }
227
264
 
228
265
  // ui/src/interstitials/Hero.jsx
266
+ var HERO_DEFAULT_SIZES_ATTR = "(min-width: 1024px) 1280px, 100vw";
229
267
  var basePath = (() => {
230
268
  try {
231
269
  const raw = typeof process !== "undefined" && process && process.env ? String(process.env.CANOPY_BASE_PATH || "") : "";
@@ -300,6 +338,7 @@ function Hero({
300
338
  item,
301
339
  index,
302
340
  random = true,
341
+ transition = "fade",
303
342
  headline,
304
343
  description,
305
344
  links = [],
@@ -366,6 +405,15 @@ function Hero({
366
405
  backgroundClassName,
367
406
  className
368
407
  ].filter(Boolean).join(" ");
408
+ const normalizedTransition = (() => {
409
+ try {
410
+ const raw = transition == null ? "" : String(transition);
411
+ const normalized = raw.trim().toLowerCase();
412
+ return normalized === "slide" ? "slide" : "fade";
413
+ } catch (_) {
414
+ return "fade";
415
+ }
416
+ })();
369
417
  const renderSlide = (slide, idx, { showVeil = true, captionVariant = "overlay" } = {}) => {
370
418
  const safeHref = applyBasePath(slide.href || "#");
371
419
  const isStaticCaption = captionVariant === "static";
@@ -374,45 +422,53 @@ function Hero({
374
422
  showVeil ? "" : "canopy-interstitial__pane--flat",
375
423
  isStaticCaption ? "canopy-interstitial__pane--static" : ""
376
424
  ].filter(Boolean).join(" ");
425
+ const buildImageProps = (className2) => {
426
+ if (!slide.thumbnail) return null;
427
+ const props = {
428
+ src: slide.thumbnail,
429
+ alt: "",
430
+ "aria-hidden": true,
431
+ className: className2,
432
+ loading: idx === 0 ? "eager" : "lazy"
433
+ };
434
+ if (slide.srcset) props.srcSet = slide.srcset;
435
+ if (slide.srcset)
436
+ props.sizes = slide.sizes || HERO_DEFAULT_SIZES_ATTR;
437
+ return props;
438
+ };
377
439
  if (isStaticCaption) {
378
- return /* @__PURE__ */ React8.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React8.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__media-frame" }, /* @__PURE__ */ React8.createElement(
440
+ return /* @__PURE__ */ React9.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React9.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__media-frame" }, /* @__PURE__ */ React9.createElement(
379
441
  "img",
380
442
  {
381
- src: slide.thumbnail,
382
- alt: "",
383
- "aria-hidden": "true",
384
- className: "canopy-interstitial__media canopy-interstitial__media--static",
385
- loading: idx === 0 ? "eager" : "lazy"
443
+ ...buildImageProps(
444
+ "canopy-interstitial__media canopy-interstitial__media--static"
445
+ )
386
446
  }
387
- )) : null, slide.title ? /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__caption canopy-interstitial__caption--static" }, /* @__PURE__ */ React8.createElement("a", { href: safeHref, className: "canopy-interstitial__caption-link" }, slide.title)) : null));
447
+ )) : null, slide.title ? /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__caption canopy-interstitial__caption--static" }, /* @__PURE__ */ React9.createElement("a", { href: safeHref, className: "canopy-interstitial__caption-link" }, slide.title)) : null));
388
448
  }
389
- return /* @__PURE__ */ React8.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React8.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React8.createElement(
449
+ return /* @__PURE__ */ React9.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React9.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React9.createElement(
390
450
  "img",
391
451
  {
392
- src: slide.thumbnail,
393
- alt: "",
394
- "aria-hidden": "true",
395
- className: "canopy-interstitial__media",
396
- loading: idx === 0 ? "eager" : "lazy"
452
+ ...buildImageProps("canopy-interstitial__media")
397
453
  }
398
- ) : null, showVeil ? /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__veil", "aria-hidden": "true" }) : null, slide.title ? /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__caption" }, /* @__PURE__ */ React8.createElement("a", { href: safeHref, className: "canopy-interstitial__caption-link" }, slide.title)) : null));
454
+ ) : null, showVeil ? /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__veil", "aria-hidden": "true" }) : null, slide.title ? /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__caption" }, /* @__PURE__ */ React9.createElement("a", { href: safeHref, className: "canopy-interstitial__caption-link" }, slide.title)) : null));
399
455
  };
400
- const renderSlider = (options = {}) => /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__slider swiper" }, /* @__PURE__ */ React8.createElement("div", { className: "swiper-wrapper" }, orderedSlides.map((slide, idx) => renderSlide(slide, idx, options))), /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__nav" }, /* @__PURE__ */ React8.createElement(
456
+ const renderSlider = (options = {}) => /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__slider swiper" }, /* @__PURE__ */ React9.createElement("div", { className: "swiper-wrapper" }, orderedSlides.map((slide, idx) => renderSlide(slide, idx, options))), /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__nav" }, /* @__PURE__ */ React9.createElement(
401
457
  "button",
402
458
  {
403
459
  type: "button",
404
460
  "aria-label": "Previous slide",
405
461
  className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--prev swiper-button-prev"
406
462
  }
407
- ), /* @__PURE__ */ React8.createElement(
463
+ ), /* @__PURE__ */ React9.createElement(
408
464
  "button",
409
465
  {
410
466
  type: "button",
411
467
  "aria-label": "Next slide",
412
468
  className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--next swiper-button-next"
413
469
  }
414
- )), /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__pagination swiper-pagination" }));
415
- const overlayContent = /* @__PURE__ */ React8.createElement(React8.Fragment, null, overlayTitle ? /* @__PURE__ */ React8.createElement("h1", { className: "canopy-interstitial__headline" }, overlayTitle) : null, derivedDescription ? /* @__PURE__ */ React8.createElement("p", { className: "canopy-interstitial__description" }, derivedDescription) : null, overlayLinks.length ? /* @__PURE__ */ React8.createElement(ButtonWrapper, { className: "canopy-interstitial__actions" }, overlayLinks.map((link) => /* @__PURE__ */ React8.createElement(
470
+ )), /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__pagination swiper-pagination" }));
471
+ const overlayContent = /* @__PURE__ */ React9.createElement(React9.Fragment, null, overlayTitle ? /* @__PURE__ */ React9.createElement("h1", { className: "canopy-interstitial__headline" }, overlayTitle) : null, derivedDescription ? /* @__PURE__ */ React9.createElement("p", { className: "canopy-interstitial__description" }, derivedDescription) : null, overlayLinks.length ? /* @__PURE__ */ React9.createElement(ButtonWrapper, { className: "canopy-interstitial__actions" }, overlayLinks.map((link) => /* @__PURE__ */ React9.createElement(
416
472
  Button,
417
473
  {
418
474
  key: `${link.href}-${link.title}`,
@@ -423,20 +479,21 @@ function Hero({
423
479
  }
424
480
  ))) : null);
425
481
  const cleanedProps = sanitizeRest(rest);
426
- return /* @__PURE__ */ React8.createElement(
482
+ return /* @__PURE__ */ React9.createElement(
427
483
  "section",
428
484
  {
429
485
  className: containerClassName,
430
486
  "data-canopy-hero-slider": "1",
487
+ "data-transition": normalizedTransition,
431
488
  style: heroStyles,
432
489
  ...cleanedProps
433
490
  },
434
- /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React8.createElement("div", { className: "canopy-interstitial__media-group" }, renderSlider({ showVeil: false, captionVariant: "static" })))
491
+ /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React9.createElement("div", { className: "canopy-interstitial__media-group" }, renderSlider({ showVeil: false, captionVariant: "static" })))
435
492
  );
436
493
  }
437
494
 
438
495
  // ui/src/layout/SubNavigation.jsx
439
- import React9 from "react";
496
+ import React10 from "react";
440
497
  import navigationHelpers from "@canopy-iiif/app/lib/components/navigation.js";
441
498
  function resolveRelativeCandidate(page, current) {
442
499
  if (page && typeof page.relativePath === "string" && page.relativePath) return page.relativePath;
@@ -461,14 +518,14 @@ function renderNodes(nodes, parentKey = "node") {
461
518
  if (node.isActive) classes.push("is-active");
462
519
  const linkClass = classes.join(" ");
463
520
  const Tag = node.href ? "a" : "span";
464
- return /* @__PURE__ */ React9.createElement(
521
+ return /* @__PURE__ */ React10.createElement(
465
522
  "li",
466
523
  {
467
524
  key,
468
525
  className: "canopy-sub-navigation__item",
469
526
  "data-depth": depth
470
527
  },
471
- /* @__PURE__ */ React9.createElement(
528
+ /* @__PURE__ */ React10.createElement(
472
529
  Tag,
473
530
  {
474
531
  className: linkClass,
@@ -477,7 +534,7 @@ function renderNodes(nodes, parentKey = "node") {
477
534
  },
478
535
  node.title || node.slug
479
536
  ),
480
- showChildren ? /* @__PURE__ */ React9.createElement("ul", { className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested", role: "list" }, renderNodes(node.children, key)) : null
537
+ showChildren ? /* @__PURE__ */ React10.createElement("ul", { className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested", role: "list" }, renderNodes(node.children, key)) : null
481
538
  );
482
539
  });
483
540
  }
@@ -491,12 +548,12 @@ function SubNavigation({
491
548
  ariaLabel
492
549
  }) {
493
550
  const PageContext = navigationHelpers && navigationHelpers.getPageContext ? navigationHelpers.getPageContext() : null;
494
- const context = PageContext ? React9.useContext(PageContext) : null;
551
+ const context = PageContext ? React10.useContext(PageContext) : null;
495
552
  const contextNavigation = context && context.navigation ? context.navigation : null;
496
553
  const contextPage = context && context.page ? context.page : null;
497
554
  const effectiveNavigation = navigationProp || contextNavigation;
498
555
  const effectivePage = page || contextPage;
499
- const resolvedNavigation = React9.useMemo(() => {
556
+ const resolvedNavigation = React10.useMemo(() => {
500
557
  if (effectiveNavigation && effectiveNavigation.root) return effectiveNavigation;
501
558
  const candidate = resolveRelativeCandidate(effectivePage, current);
502
559
  if (!candidate) return effectiveNavigation || null;
@@ -520,15 +577,15 @@ function SubNavigation({
520
577
  if (!Object.prototype.hasOwnProperty.call(inlineStyle, "--sub-nav-indent")) {
521
578
  inlineStyle["--sub-nav-indent"] = "0.85rem";
522
579
  }
523
- return /* @__PURE__ */ React9.createElement("nav", { className: combinedClassName, style: inlineStyle, "aria-label": navLabel }, finalHeading ? /* @__PURE__ */ React9.createElement("div", { className: "canopy-sub-navigation__heading" }, finalHeading) : null, /* @__PURE__ */ React9.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, renderNodes([rootNode], rootNode.slug || "root")));
580
+ return /* @__PURE__ */ React10.createElement("nav", { className: combinedClassName, style: inlineStyle, "aria-label": navLabel }, finalHeading ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-sub-navigation__heading" }, finalHeading) : null, /* @__PURE__ */ React10.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, renderNodes([rootNode], rootNode.slug || "root")));
524
581
  }
525
582
 
526
583
  // ui/src/layout/Layout.jsx
527
- import React11 from "react";
584
+ import React12 from "react";
528
585
  import navigationHelpers2 from "@canopy-iiif/app/lib/components/navigation.js";
529
586
 
530
587
  // ui/src/layout/ContentNavigation.jsx
531
- import React10 from "react";
588
+ import React11 from "react";
532
589
  var SCROLL_OFFSET_REM = 1.618;
533
590
  function depthIndex(depth) {
534
591
  return Math.max(0, Math.min(5, (depth || 1) - 1));
@@ -543,12 +600,12 @@ function ContentNavigation({
543
600
  ariaLabel
544
601
  }) {
545
602
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
546
- const savedDepthsRef = React10.useRef(null);
603
+ const savedDepthsRef = React11.useRef(null);
547
604
  if ((!items || !items.length) && !headingId) return null;
548
605
  const combinedClassName = ["canopy-sub-navigation canopy-content-navigation", className].filter(Boolean).join(" ");
549
606
  const effectiveHeading = heading || pageTitle || null;
550
607
  const navLabel = ariaLabel || (effectiveHeading ? `${effectiveHeading} navigation` : "Section navigation");
551
- const getSavedDepth = React10.useCallback(
608
+ const getSavedDepth = React11.useCallback(
552
609
  (id, fallback) => {
553
610
  if (!id) return fallback;
554
611
  if (!savedDepthsRef.current) savedDepthsRef.current = /* @__PURE__ */ new Map();
@@ -559,7 +616,7 @@ function ContentNavigation({
559
616
  },
560
617
  []
561
618
  );
562
- const headingEntries = React10.useMemo(() => {
619
+ const headingEntries = React11.useMemo(() => {
563
620
  const entries = [];
564
621
  const seen = /* @__PURE__ */ new Set();
565
622
  if (headingId) {
@@ -583,12 +640,12 @@ function ContentNavigation({
583
640
  return entries;
584
641
  }, [headingId, items, getSavedDepth]);
585
642
  const fallbackId = headingEntries.length ? headingEntries[0].id : headingId || null;
586
- const [activeId, setActiveId] = React10.useState(fallbackId);
587
- const activeIdRef = React10.useRef(activeId);
588
- React10.useEffect(() => {
643
+ const [activeId, setActiveId] = React11.useState(fallbackId);
644
+ const activeIdRef = React11.useRef(activeId);
645
+ React11.useEffect(() => {
589
646
  activeIdRef.current = activeId;
590
647
  }, [activeId]);
591
- React10.useEffect(() => {
648
+ React11.useEffect(() => {
592
649
  if (!headingEntries.length) return;
593
650
  if (!headingEntries.some((entry) => entry.id === activeIdRef.current)) {
594
651
  const next = headingEntries[0].id;
@@ -596,7 +653,7 @@ function ContentNavigation({
596
653
  setActiveId(next);
597
654
  }
598
655
  }, [headingEntries]);
599
- const computeOffsetPx = React10.useCallback(() => {
656
+ const computeOffsetPx = React11.useCallback(() => {
600
657
  if (!isBrowser) return 0;
601
658
  try {
602
659
  const root = document.documentElement;
@@ -606,8 +663,8 @@ function ContentNavigation({
606
663
  return 0;
607
664
  }
608
665
  }, [isBrowser]);
609
- const headingElementsRef = React10.useRef([]);
610
- const updateActiveFromElements = React10.useCallback(
666
+ const headingElementsRef = React11.useRef([]);
667
+ const updateActiveFromElements = React11.useCallback(
611
668
  (elements) => {
612
669
  if (!elements || !elements.length) return;
613
670
  const offset = computeOffsetPx();
@@ -627,7 +684,7 @@ function ContentNavigation({
627
684
  },
628
685
  [computeOffsetPx]
629
686
  );
630
- React10.useEffect(() => {
687
+ React11.useEffect(() => {
631
688
  if (!isBrowser) return void 0;
632
689
  const elements = headingEntries.map((entry) => {
633
690
  const element = document.getElementById(entry.id);
@@ -653,7 +710,7 @@ function ContentNavigation({
653
710
  window.removeEventListener("resize", handle);
654
711
  };
655
712
  }, [headingEntries, isBrowser, updateActiveFromElements]);
656
- const handleAnchorClick = React10.useCallback(
713
+ const handleAnchorClick = React11.useCallback(
657
714
  (event, targetId, options = {}) => {
658
715
  var _a;
659
716
  try {
@@ -684,7 +741,7 @@ function ContentNavigation({
684
741
  },
685
742
  [computeOffsetPx, headingEntries, headingId, isBrowser]
686
743
  );
687
- const renderNodes2 = React10.useCallback(
744
+ const renderNodes2 = React11.useCallback(
688
745
  (nodes) => {
689
746
  if (!nodes || !nodes.length) return null;
690
747
  return nodes.map((node) => {
@@ -693,7 +750,7 @@ function ContentNavigation({
693
750
  const depth = node.depth || node.level || getSavedDepth(id, 2);
694
751
  const idx = depthIndex(depth);
695
752
  const isActive = id && activeId === id;
696
- return /* @__PURE__ */ React10.createElement("li", { key: id || node.title, className: "canopy-sub-navigation__item", "data-depth": idx }, /* @__PURE__ */ React10.createElement(
753
+ return /* @__PURE__ */ React11.createElement("li", { key: id || node.title, className: "canopy-sub-navigation__item", "data-depth": idx }, /* @__PURE__ */ React11.createElement(
697
754
  "a",
698
755
  {
699
756
  className: `canopy-sub-navigation__link depth-${idx}${isActive ? " is-active" : ""}`,
@@ -702,7 +759,7 @@ function ContentNavigation({
702
759
  "aria-current": isActive ? "location" : void 0
703
760
  },
704
761
  node.title
705
- ), node.children && node.children.length ? /* @__PURE__ */ React10.createElement(
762
+ ), node.children && node.children.length ? /* @__PURE__ */ React11.createElement(
706
763
  "ul",
707
764
  {
708
765
  className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
@@ -715,7 +772,7 @@ function ContentNavigation({
715
772
  [handleAnchorClick, activeId, getSavedDepth]
716
773
  );
717
774
  const nestedItems = renderNodes2(items);
718
- const topLink = headingId ? /* @__PURE__ */ React10.createElement("li", { className: "canopy-sub-navigation__item", "data-depth": 0 }, /* @__PURE__ */ React10.createElement(
775
+ const topLink = headingId ? /* @__PURE__ */ React11.createElement("li", { className: "canopy-sub-navigation__item", "data-depth": 0 }, /* @__PURE__ */ React11.createElement(
719
776
  "a",
720
777
  {
721
778
  className: `canopy-sub-navigation__link depth-0${activeId === headingId ? " is-active" : ""}`,
@@ -724,7 +781,7 @@ function ContentNavigation({
724
781
  "aria-current": activeId === headingId ? "location" : void 0
725
782
  },
726
783
  effectiveHeading || pageTitle || headingId
727
- ), nestedItems ? /* @__PURE__ */ React10.createElement(
784
+ ), nestedItems ? /* @__PURE__ */ React11.createElement(
728
785
  "ul",
729
786
  {
730
787
  className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
@@ -732,7 +789,7 @@ function ContentNavigation({
732
789
  },
733
790
  nestedItems
734
791
  ) : null) : null;
735
- return /* @__PURE__ */ React10.createElement("nav", { className: combinedClassName, style, "aria-label": navLabel }, /* @__PURE__ */ React10.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, topLink || nestedItems));
792
+ return /* @__PURE__ */ React11.createElement("nav", { className: combinedClassName, style, "aria-label": navLabel }, /* @__PURE__ */ React11.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, topLink || nestedItems));
736
793
  }
737
794
 
738
795
  // ui/src/layout/Layout.jsx
@@ -764,10 +821,10 @@ function buildHeadingTree(headings) {
764
821
  }
765
822
  function buildNavigationAside(sidebar, className) {
766
823
  if (!sidebar) {
767
- return /* @__PURE__ */ React11.createElement(SubNavigation, { className });
824
+ return /* @__PURE__ */ React12.createElement(SubNavigation, { className });
768
825
  }
769
826
  if (typeof sidebar === "function") {
770
- return React11.createElement(sidebar);
827
+ return React12.createElement(sidebar);
771
828
  }
772
829
  return sidebar;
773
830
  }
@@ -784,26 +841,26 @@ function Layout({
784
841
  ...rest
785
842
  }) {
786
843
  const PageContext = navigationHelpers2 && typeof navigationHelpers2.getPageContext === "function" ? navigationHelpers2.getPageContext() : null;
787
- const context = PageContext ? React11.useContext(PageContext) : null;
788
- const pageHeadings = React11.useMemo(() => {
844
+ const context = PageContext ? React12.useContext(PageContext) : null;
845
+ const pageHeadings = React12.useMemo(() => {
789
846
  const headings = context && context.page ? context.page.headings : null;
790
847
  return Array.isArray(headings) ? headings : [];
791
848
  }, [context]);
792
- const contentHeading = React11.useMemo(() => {
849
+ const contentHeading = React12.useMemo(() => {
793
850
  const first = pageHeadings.find((heading) => {
794
851
  const depth = heading && (heading.depth || heading.level);
795
852
  return depth === 1;
796
853
  });
797
854
  return first && first.title ? first.title : null;
798
855
  }, [pageHeadings]);
799
- const headingAnchorId = React11.useMemo(() => {
856
+ const headingAnchorId = React12.useMemo(() => {
800
857
  const first = pageHeadings.find((heading) => {
801
858
  const depth = heading && (heading.depth || heading.level);
802
859
  return depth === 1;
803
860
  });
804
861
  return first && first.id ? first.id : null;
805
862
  }, [pageHeadings]);
806
- const headingTree = React11.useMemo(
863
+ const headingTree = React12.useMemo(
807
864
  () => buildHeadingTree(pageHeadings),
808
865
  [pageHeadings]
809
866
  );
@@ -842,7 +899,7 @@ function Layout({
842
899
  contentNavigationClassName
843
900
  ].filter(Boolean).join(" ");
844
901
  const sidebarNode = showLeftColumn ? buildNavigationAside(sidebar, sidebarClassName) : null;
845
- return /* @__PURE__ */ React11.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React11.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React11.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React11.createElement("aside", { className: contentNavigationAsideClassName }, /* @__PURE__ */ React11.createElement(
902
+ return /* @__PURE__ */ React12.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React12.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React12.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React12.createElement("aside", { className: contentNavigationAsideClassName }, /* @__PURE__ */ React12.createElement(
846
903
  ContentNavigation,
847
904
  {
848
905
  items: headingTree,
@@ -854,17 +911,17 @@ function Layout({
854
911
  }
855
912
 
856
913
  // ui/src/layout/CanopyHeader.jsx
857
- import React18 from "react";
914
+ import React19 from "react";
858
915
 
859
916
  // ui/src/search/SearchPanel.jsx
860
- import React15 from "react";
917
+ import React16 from "react";
861
918
 
862
919
  // ui/src/Icons.jsx
863
- import React12 from "react";
864
- var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React12.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React12.createElement("path", { d: "M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z" }));
920
+ import React13 from "react";
921
+ var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React13.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React13.createElement("path", { d: "M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z" }));
865
922
 
866
923
  // ui/src/search/SearchPanelForm.jsx
867
- import React13 from "react";
924
+ import React14 from "react";
868
925
  function readBasePath() {
869
926
  const normalize = (val) => {
870
927
  const raw = typeof val === "string" ? val.trim() : "";
@@ -927,18 +984,18 @@ function SearchPanelForm(props = {}) {
927
984
  clearLabel = "Clear search"
928
985
  } = props || {};
929
986
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
930
- const action = React13.useMemo(
987
+ const action = React14.useMemo(
931
988
  () => resolveSearchPath(searchPath),
932
989
  [searchPath]
933
990
  );
934
- const autoId = typeof React13.useId === "function" ? React13.useId() : void 0;
935
- const [fallbackId] = React13.useState(
991
+ const autoId = typeof React14.useId === "function" ? React14.useId() : void 0;
992
+ const [fallbackId] = React14.useState(
936
993
  () => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
937
994
  );
938
995
  const inputId = inputIdProp || autoId || fallbackId;
939
- const inputRef = React13.useRef(null);
940
- const [hasValue, setHasValue] = React13.useState(false);
941
- const focusInput = React13.useCallback(() => {
996
+ const inputRef = React14.useRef(null);
997
+ const [hasValue, setHasValue] = React14.useState(false);
998
+ const focusInput = React14.useCallback(() => {
942
999
  const el = inputRef.current;
943
1000
  if (!el) return;
944
1001
  if (document.activeElement === el) return;
@@ -951,7 +1008,7 @@ function SearchPanelForm(props = {}) {
951
1008
  }
952
1009
  }
953
1010
  }, []);
954
- const handlePointerDown = React13.useCallback(
1011
+ const handlePointerDown = React14.useCallback(
955
1012
  (event) => {
956
1013
  const target = event.target;
957
1014
  if (target && typeof target.closest === "function") {
@@ -963,23 +1020,23 @@ function SearchPanelForm(props = {}) {
963
1020
  },
964
1021
  [focusInput]
965
1022
  );
966
- React13.useEffect(() => {
1023
+ React14.useEffect(() => {
967
1024
  const el = inputRef.current;
968
1025
  if (!el) return;
969
1026
  if (el.value && el.value.trim()) {
970
1027
  setHasValue(true);
971
1028
  }
972
1029
  }, []);
973
- const handleInputChange = React13.useCallback((event) => {
1030
+ const handleInputChange = React14.useCallback((event) => {
974
1031
  var _a;
975
1032
  const nextHasValue = Boolean(
976
1033
  ((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
977
1034
  );
978
1035
  setHasValue(nextHasValue);
979
1036
  }, []);
980
- const handleClear = React13.useCallback((event) => {
1037
+ const handleClear = React14.useCallback((event) => {
981
1038
  }, []);
982
- const handleClearKey = React13.useCallback(
1039
+ const handleClearKey = React14.useCallback(
983
1040
  (event) => {
984
1041
  if (event.key === "Enter" || event.key === " ") {
985
1042
  event.preventDefault();
@@ -988,7 +1045,7 @@ function SearchPanelForm(props = {}) {
988
1045
  },
989
1046
  [handleClear]
990
1047
  );
991
- return /* @__PURE__ */ React13.createElement(
1048
+ return /* @__PURE__ */ React14.createElement(
992
1049
  "form",
993
1050
  {
994
1051
  action,
@@ -1000,7 +1057,7 @@ function SearchPanelForm(props = {}) {
1000
1057
  onPointerDown: handlePointerDown,
1001
1058
  "data-has-value": hasValue ? "1" : "0"
1002
1059
  },
1003
- /* @__PURE__ */ React13.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React13.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React13.createElement(
1060
+ /* @__PURE__ */ React14.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React14.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React14.createElement(
1004
1061
  "input",
1005
1062
  {
1006
1063
  id: inputId,
@@ -1016,7 +1073,7 @@ function SearchPanelForm(props = {}) {
1016
1073
  onInput: handleInputChange
1017
1074
  }
1018
1075
  )),
1019
- hasValue ? /* @__PURE__ */ React13.createElement(
1076
+ hasValue ? /* @__PURE__ */ React14.createElement(
1020
1077
  "button",
1021
1078
  {
1022
1079
  type: "button",
@@ -1029,32 +1086,31 @@ function SearchPanelForm(props = {}) {
1029
1086
  },
1030
1087
  "\xD7"
1031
1088
  ) : null,
1032
- /* @__PURE__ */ React13.createElement(
1089
+ /* @__PURE__ */ React14.createElement(
1033
1090
  "button",
1034
1091
  {
1035
1092
  type: "submit",
1036
1093
  "data-canopy-search-form-trigger": "submit",
1037
1094
  className: "canopy-search-form__submit"
1038
1095
  },
1039
- /* @__PURE__ */ React13.createElement("span", null, text),
1040
- /* @__PURE__ */ React13.createElement("span", { "aria-hidden": true, className: "canopy-search-form__shortcut" }, /* @__PURE__ */ React13.createElement("span", null, "\u2318"), /* @__PURE__ */ React13.createElement("span", null, "K"))
1096
+ text
1041
1097
  )
1042
1098
  );
1043
1099
  }
1044
1100
 
1045
1101
  // ui/src/search/SearchPanelTeaserResults.jsx
1046
- import React14 from "react";
1102
+ import React15 from "react";
1047
1103
  function SearchPanelTeaserResults(props = {}) {
1048
1104
  const { style, className } = props || {};
1049
1105
  const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
1050
- return /* @__PURE__ */ React14.createElement(
1106
+ return /* @__PURE__ */ React15.createElement(
1051
1107
  "div",
1052
1108
  {
1053
1109
  "data-canopy-search-form-panel": true,
1054
1110
  className: classes || void 0,
1055
1111
  style
1056
1112
  },
1057
- /* @__PURE__ */ React14.createElement("div", { id: "cplist" })
1113
+ /* @__PURE__ */ React15.createElement("div", { id: "cplist" })
1058
1114
  );
1059
1115
  }
1060
1116
 
@@ -1075,11 +1131,11 @@ function SearchPanel(props = {}) {
1075
1131
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
1076
1132
  const resolvedSearchPath = resolveSearchPath(searchPath);
1077
1133
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
1078
- return /* @__PURE__ */ React15.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React15.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React15.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React15.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React15.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
1134
+ return /* @__PURE__ */ React16.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React16.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React16.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React16.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React16.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
1079
1135
  }
1080
1136
 
1081
1137
  // ui/src/layout/CanopyBrand.jsx
1082
- import React16 from "react";
1138
+ import React17 from "react";
1083
1139
  function CanopyBrand(props = {}) {
1084
1140
  const {
1085
1141
  labelId,
@@ -1090,11 +1146,11 @@ function CanopyBrand(props = {}) {
1090
1146
  } = props || {};
1091
1147
  const spanProps = labelId ? { id: labelId } : {};
1092
1148
  const classes = ["canopy-logo", className].filter(Boolean).join(" ");
1093
- return /* @__PURE__ */ React16.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React16.createElement(Logo, null) : null, /* @__PURE__ */ React16.createElement("span", { ...spanProps }, label));
1149
+ return /* @__PURE__ */ React17.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React17.createElement(Logo, null) : null, /* @__PURE__ */ React17.createElement("span", { ...spanProps }, label));
1094
1150
  }
1095
1151
 
1096
1152
  // ui/src/layout/CanopyModal.jsx
1097
- import React17 from "react";
1153
+ import React18 from "react";
1098
1154
  function CanopyModal(props = {}) {
1099
1155
  const {
1100
1156
  id,
@@ -1145,7 +1201,7 @@ function CanopyModal(props = {}) {
1145
1201
  if (padded) bodyClasses.push("canopy-modal__body--padded");
1146
1202
  if (bodyClassName) bodyClasses.push(bodyClassName);
1147
1203
  const bodyClassNameValue = bodyClasses.join(" ");
1148
- return /* @__PURE__ */ React17.createElement("div", { ...modalProps }, /* @__PURE__ */ React17.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React17.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React17.createElement(
1204
+ return /* @__PURE__ */ React18.createElement("div", { ...modalProps }, /* @__PURE__ */ React18.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React18.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React18.createElement(
1149
1205
  "svg",
1150
1206
  {
1151
1207
  xmlns: "http://www.w3.org/2000/svg",
@@ -1155,8 +1211,8 @@ function CanopyModal(props = {}) {
1155
1211
  strokeWidth: "1.5",
1156
1212
  className: "canopy-modal__close-icon"
1157
1213
  },
1158
- /* @__PURE__ */ React17.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
1159
- ), /* @__PURE__ */ React17.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React17.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React17.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React17.createElement(
1214
+ /* @__PURE__ */ React18.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
1215
+ ), /* @__PURE__ */ React18.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React18.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React18.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React18.createElement(
1160
1216
  CanopyBrand,
1161
1217
  {
1162
1218
  labelId: resolvedLabelId,
@@ -1360,7 +1416,7 @@ function HeaderScript() {
1360
1416
  });
1361
1417
  })();
1362
1418
  `;
1363
- return /* @__PURE__ */ React18.createElement(
1419
+ return /* @__PURE__ */ React19.createElement(
1364
1420
  "script",
1365
1421
  {
1366
1422
  dangerouslySetInnerHTML: {
@@ -1371,7 +1427,9 @@ function HeaderScript() {
1371
1427
  }
1372
1428
  function ensureArray(navLinks) {
1373
1429
  if (!Array.isArray(navLinks)) return [];
1374
- return navLinks.filter((link) => link && typeof link === "object" && typeof link.href === "string");
1430
+ return navLinks.filter(
1431
+ (link) => link && typeof link === "object" && typeof link.href === "string"
1432
+ );
1375
1433
  }
1376
1434
  function CanopyHeader(props = {}) {
1377
1435
  const {
@@ -1384,59 +1442,98 @@ function CanopyHeader(props = {}) {
1384
1442
  logo: SiteLogo
1385
1443
  } = props;
1386
1444
  const navLinks = ensureArray(navLinksProp);
1387
- return /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement("header", { className: "canopy-header", "data-mobile-nav": "closed", "data-mobile-search": "closed" }, /* @__PURE__ */ React18.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React18.createElement(
1388
- CanopyBrand,
1389
- {
1390
- label: title,
1391
- href: brandHref,
1392
- className: "canopy-header__brand-link",
1393
- Logo: SiteLogo
1394
- }
1395
- )), /* @__PURE__ */ React18.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React18.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })), /* @__PURE__ */ React18.createElement("nav", { className: "canopy-nav-links canopy-header__desktop-nav", "aria-label": "Primary navigation" }, navLinks.map((link) => /* @__PURE__ */ React18.createElement("a", { key: link.href, href: link.href }, link.label || link.href))), /* @__PURE__ */ React18.createElement("div", { className: "canopy-header__actions" }, /* @__PURE__ */ React18.createElement(
1396
- "button",
1445
+ return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement(
1446
+ "header",
1397
1447
  {
1398
- type: "button",
1399
- className: "canopy-header__icon-button canopy-header__search-trigger",
1400
- "aria-label": "Open search",
1401
- "aria-controls": "canopy-modal-search",
1402
- "aria-expanded": "false",
1403
- "data-canopy-header-toggle": "search"
1448
+ className: "canopy-header",
1449
+ "data-mobile-nav": "closed",
1450
+ "data-mobile-search": "closed"
1404
1451
  },
1405
- /* @__PURE__ */ React18.createElement(
1406
- "svg",
1452
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React19.createElement(
1453
+ CanopyBrand,
1407
1454
  {
1408
- xmlns: "http://www.w3.org/2000/svg",
1409
- viewBox: "0 0 24 24",
1410
- fill: "none",
1411
- stroke: "currentColor",
1412
- strokeWidth: "1.5",
1413
- className: "canopy-header__search-icon"
1455
+ label: title,
1456
+ href: brandHref,
1457
+ className: "canopy-header__brand-link",
1458
+ Logo: SiteLogo
1459
+ }
1460
+ )),
1461
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React19.createElement(
1462
+ SearchPanel,
1463
+ {
1464
+ label: searchLabel,
1465
+ hotkey: searchHotkey,
1466
+ placeholder: searchPlaceholder
1467
+ }
1468
+ )),
1469
+ /* @__PURE__ */ React19.createElement(
1470
+ "nav",
1471
+ {
1472
+ className: "canopy-nav-links canopy-header__desktop-nav",
1473
+ "aria-label": "Primary navigation"
1414
1474
  },
1415
- /* @__PURE__ */ React18.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m21 21-3.8-3.8M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z" })
1416
- )
1417
- ), /* @__PURE__ */ React18.createElement(
1418
- "button",
1419
- {
1420
- type: "button",
1421
- className: "canopy-header__icon-button canopy-header__menu",
1422
- "aria-label": "Open navigation",
1423
- "aria-controls": "canopy-modal-nav",
1424
- "aria-expanded": "false",
1425
- "data-canopy-header-toggle": "nav"
1426
- },
1427
- /* @__PURE__ */ React18.createElement(
1428
- "svg",
1475
+ navLinks.map((link) => /* @__PURE__ */ React19.createElement("a", { key: link.href, href: link.href }, link.label || link.href))
1476
+ ),
1477
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__actions" }, /* @__PURE__ */ React19.createElement(
1478
+ "button",
1429
1479
  {
1430
- xmlns: "http://www.w3.org/2000/svg",
1431
- fill: "none",
1432
- viewBox: "0 0 24 24",
1433
- strokeWidth: "1.5",
1434
- stroke: "currentColor",
1435
- className: "canopy-header__menu-icon"
1480
+ type: "button",
1481
+ className: "canopy-header__icon-button canopy-header__search-trigger",
1482
+ "aria-label": "Open search",
1483
+ "aria-controls": "canopy-modal-search",
1484
+ "aria-expanded": "false",
1485
+ "data-canopy-header-toggle": "search"
1436
1486
  },
1437
- /* @__PURE__ */ React18.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" })
1438
- )
1439
- ))), /* @__PURE__ */ React18.createElement(
1487
+ /* @__PURE__ */ React19.createElement(
1488
+ "svg",
1489
+ {
1490
+ xmlns: "http://www.w3.org/2000/svg",
1491
+ viewBox: "0 0 24 24",
1492
+ fill: "none",
1493
+ stroke: "currentColor",
1494
+ strokeWidth: "1.5",
1495
+ className: "canopy-header__search-icon"
1496
+ },
1497
+ /* @__PURE__ */ React19.createElement(
1498
+ "path",
1499
+ {
1500
+ strokeLinecap: "round",
1501
+ strokeLinejoin: "round",
1502
+ d: "m21 21-3.8-3.8M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z"
1503
+ }
1504
+ )
1505
+ )
1506
+ ), /* @__PURE__ */ React19.createElement(
1507
+ "button",
1508
+ {
1509
+ type: "button",
1510
+ className: "canopy-header__icon-button canopy-header__menu",
1511
+ "aria-label": "Open navigation",
1512
+ "aria-controls": "canopy-modal-nav",
1513
+ "aria-expanded": "false",
1514
+ "data-canopy-header-toggle": "nav"
1515
+ },
1516
+ /* @__PURE__ */ React19.createElement(
1517
+ "svg",
1518
+ {
1519
+ xmlns: "http://www.w3.org/2000/svg",
1520
+ fill: "none",
1521
+ viewBox: "0 0 24 24",
1522
+ strokeWidth: "1.5",
1523
+ stroke: "currentColor",
1524
+ className: "canopy-header__menu-icon"
1525
+ },
1526
+ /* @__PURE__ */ React19.createElement(
1527
+ "path",
1528
+ {
1529
+ strokeLinecap: "round",
1530
+ strokeLinejoin: "round",
1531
+ d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
1532
+ }
1533
+ )
1534
+ )
1535
+ ))
1536
+ ), /* @__PURE__ */ React19.createElement(
1440
1537
  CanopyModal,
1441
1538
  {
1442
1539
  id: "canopy-modal-nav",
@@ -1448,8 +1545,15 @@ function CanopyHeader(props = {}) {
1448
1545
  closeLabel: "Close navigation",
1449
1546
  closeDataAttr: "nav"
1450
1547
  },
1451
- /* @__PURE__ */ React18.createElement("nav", { className: "canopy-nav-links canopy-modal__nav", "aria-label": "Primary navigation" }, navLinks.map((link) => /* @__PURE__ */ React18.createElement("a", { key: link.href, href: link.href }, link.label || link.href)))
1452
- ), /* @__PURE__ */ React18.createElement(
1548
+ /* @__PURE__ */ React19.createElement(
1549
+ "nav",
1550
+ {
1551
+ className: "canopy-nav-links canopy-modal__nav",
1552
+ "aria-label": "Primary navigation"
1553
+ },
1554
+ navLinks.map((link) => /* @__PURE__ */ React19.createElement("a", { key: link.href, href: link.href }, link.label || link.href))
1555
+ )
1556
+ ), /* @__PURE__ */ React19.createElement(
1453
1557
  CanopyModal,
1454
1558
  {
1455
1559
  id: "canopy-modal-search",
@@ -1462,26 +1566,33 @@ function CanopyHeader(props = {}) {
1462
1566
  closeDataAttr: "search",
1463
1567
  bodyClassName: "canopy-modal__body--search"
1464
1568
  },
1465
- /* @__PURE__ */ React18.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })
1466
- ), /* @__PURE__ */ React18.createElement(HeaderScript, null));
1569
+ /* @__PURE__ */ React19.createElement(
1570
+ SearchPanel,
1571
+ {
1572
+ label: searchLabel,
1573
+ hotkey: searchHotkey,
1574
+ placeholder: searchPlaceholder
1575
+ }
1576
+ )
1577
+ ), /* @__PURE__ */ React19.createElement(HeaderScript, null));
1467
1578
  }
1468
1579
 
1469
1580
  // ui/src/layout/CanopyFooter.jsx
1470
- import React19 from "react";
1581
+ import React20 from "react";
1471
1582
  function CanopyFooter({ className = "", children }) {
1472
1583
  const footerClassName = ["canopy-footer", className].filter(Boolean).join(" ");
1473
- return /* @__PURE__ */ React19.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React19.createElement("div", { className: "canopy-footer__inner" }, children));
1584
+ return /* @__PURE__ */ React20.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React20.createElement("div", { className: "canopy-footer__inner" }, children));
1474
1585
  }
1475
1586
 
1476
1587
  // ui/src/layout/Container.jsx
1477
- import React20 from "react";
1588
+ import React21 from "react";
1478
1589
  function Container({ className = "", children, ...rest }) {
1479
1590
  const classes = ["mx-auto", "max-w-content", "w-full", "px-6", className].filter(Boolean).join(" ");
1480
- return /* @__PURE__ */ React20.createElement("div", { className: classes, ...rest }, children);
1591
+ return /* @__PURE__ */ React21.createElement("div", { className: classes, ...rest }, children);
1481
1592
  }
1482
1593
 
1483
1594
  // ui/src/search/MdxSearchResults.jsx
1484
- import React21 from "react";
1595
+ import React22 from "react";
1485
1596
  function MdxSearchResults(props) {
1486
1597
  let json = "{}";
1487
1598
  try {
@@ -1489,11 +1600,11 @@ function MdxSearchResults(props) {
1489
1600
  } catch (_) {
1490
1601
  json = "{}";
1491
1602
  }
1492
- return /* @__PURE__ */ React21.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React21.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1603
+ return /* @__PURE__ */ React22.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React22.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1493
1604
  }
1494
1605
 
1495
1606
  // ui/src/search/SearchSummary.jsx
1496
- import React22 from "react";
1607
+ import React23 from "react";
1497
1608
  function SearchSummary(props) {
1498
1609
  let json = "{}";
1499
1610
  try {
@@ -1501,11 +1612,11 @@ function SearchSummary(props) {
1501
1612
  } catch (_) {
1502
1613
  json = "{}";
1503
1614
  }
1504
- return /* @__PURE__ */ React22.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React22.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1615
+ return /* @__PURE__ */ React23.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React23.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1505
1616
  }
1506
1617
 
1507
1618
  // ui/src/search/MdxSearchTabs.jsx
1508
- import React23 from "react";
1619
+ import React24 from "react";
1509
1620
  function MdxSearchTabs(props) {
1510
1621
  let json = "{}";
1511
1622
  try {
@@ -1513,11 +1624,11 @@ function MdxSearchTabs(props) {
1513
1624
  } catch (_) {
1514
1625
  json = "{}";
1515
1626
  }
1516
- return /* @__PURE__ */ React23.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React23.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1627
+ return /* @__PURE__ */ React24.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React24.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1517
1628
  }
1518
1629
 
1519
1630
  // ui/src/search-form/MdxSearchFormModal.jsx
1520
- import React24 from "react";
1631
+ import React25 from "react";
1521
1632
  function MdxSearchFormModal(props = {}) {
1522
1633
  const {
1523
1634
  placeholder = "Search\u2026",
@@ -1533,11 +1644,11 @@ function MdxSearchFormModal(props = {}) {
1533
1644
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
1534
1645
  const resolvedSearchPath = resolveSearchPath(searchPath);
1535
1646
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
1536
- return /* @__PURE__ */ React24.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React24.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React24.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React24.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React24.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
1647
+ return /* @__PURE__ */ React25.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React25.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React25.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React25.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React25.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
1537
1648
  }
1538
1649
 
1539
1650
  // ui/src/iiif/ManifestPrimitives.jsx
1540
- import React25 from "react";
1651
+ import React26 from "react";
1541
1652
  import {
1542
1653
  Label as CloverLabel,
1543
1654
  Metadata as CloverMetadata,
@@ -1562,28 +1673,28 @@ function ensureMetadata(items) {
1562
1673
  function Label({ manifest, label, ...rest }) {
1563
1674
  const intl = label || manifest && manifest.label;
1564
1675
  if (!hasInternationalValue(intl)) return null;
1565
- return /* @__PURE__ */ React25.createElement(CloverLabel, { label: intl, ...rest });
1676
+ return /* @__PURE__ */ React26.createElement(CloverLabel, { label: intl, ...rest });
1566
1677
  }
1567
1678
  function Summary({ manifest, summary, ...rest }) {
1568
1679
  const intl = summary || manifest && manifest.summary;
1569
1680
  if (!hasInternationalValue(intl)) return null;
1570
- return /* @__PURE__ */ React25.createElement(CloverSummary, { summary: intl, ...rest });
1681
+ return /* @__PURE__ */ React26.createElement(CloverSummary, { summary: intl, ...rest });
1571
1682
  }
1572
1683
  function Metadata({ manifest, metadata, ...rest }) {
1573
1684
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
1574
1685
  if (!items.length) return null;
1575
- return /* @__PURE__ */ React25.createElement(CloverMetadata, { metadata: items, ...rest });
1686
+ return /* @__PURE__ */ React26.createElement(CloverMetadata, { metadata: items, ...rest });
1576
1687
  }
1577
1688
  function RequiredStatement({ manifest, requiredStatement, ...rest }) {
1578
1689
  const stmt = requiredStatement || manifest && manifest.requiredStatement;
1579
1690
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
1580
1691
  return null;
1581
1692
  }
1582
- return /* @__PURE__ */ React25.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
1693
+ return /* @__PURE__ */ React26.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
1583
1694
  }
1584
1695
 
1585
1696
  // ui/src/docs/CodeBlock.jsx
1586
- import React26 from "react";
1697
+ import React27 from "react";
1587
1698
  function parseHighlightAttr(attr) {
1588
1699
  if (!attr) return /* @__PURE__ */ new Set();
1589
1700
  const cleaned = String(attr || "").trim();
@@ -1629,10 +1740,10 @@ var highlightBaseStyle = {
1629
1740
  };
1630
1741
  function DocsCodeBlock(props = {}) {
1631
1742
  const { children, ...rest } = props;
1632
- const childArray = React26.Children.toArray(children);
1633
- const codeElement = childArray.find((el) => React26.isValidElement(el));
1743
+ const childArray = React27.Children.toArray(children);
1744
+ const codeElement = childArray.find((el) => React27.isValidElement(el));
1634
1745
  if (!codeElement || !codeElement.props) {
1635
- return React26.createElement("pre", props);
1746
+ return React27.createElement("pre", props);
1636
1747
  }
1637
1748
  const {
1638
1749
  className = "",
@@ -1647,8 +1758,8 @@ function DocsCodeBlock(props = {}) {
1647
1758
  const highlightSet = parseHighlightAttr(highlightAttr);
1648
1759
  const copyAttr = codeProps["data-copy"];
1649
1760
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
1650
- const [copied, setCopied] = React26.useState(false);
1651
- const handleCopy = React26.useCallback(async () => {
1761
+ const [copied, setCopied] = React27.useState(false);
1762
+ const handleCopy = React27.useCallback(async () => {
1652
1763
  const text = rawCode;
1653
1764
  try {
1654
1765
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -1712,20 +1823,20 @@ function DocsCodeBlock(props = {}) {
1712
1823
  const highlight = highlightSet.has(lineNumber);
1713
1824
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
1714
1825
  const displayLine = line === "" ? " " : line;
1715
- return React26.createElement(
1826
+ return React27.createElement(
1716
1827
  "span",
1717
1828
  { key: lineNumber, style },
1718
- React26.createElement("span", { style: lineContentStyle }, displayLine)
1829
+ React27.createElement("span", { style: lineContentStyle }, displayLine)
1719
1830
  );
1720
1831
  });
1721
- return React26.createElement(
1832
+ return React27.createElement(
1722
1833
  "div",
1723
1834
  { style: containerStyle },
1724
- React26.createElement(
1835
+ React27.createElement(
1725
1836
  "div",
1726
1837
  { style: headerStyle },
1727
- React26.createElement("span", null, showFilename ? filename : null),
1728
- enableCopy ? React26.createElement(
1838
+ React27.createElement("span", null, showFilename ? filename : null),
1839
+ enableCopy ? React27.createElement(
1729
1840
  "button",
1730
1841
  {
1731
1842
  type: "button",
@@ -1744,19 +1855,19 @@ function DocsCodeBlock(props = {}) {
1744
1855
  copied ? "Copied" : "Copy"
1745
1856
  ) : null
1746
1857
  ),
1747
- React26.createElement(
1858
+ React27.createElement(
1748
1859
  "pre",
1749
1860
  { ...preRest, className: preClassName, style: mergedPreStyle },
1750
- React26.createElement("code", { style: codeStyle }, lineElements)
1861
+ React27.createElement("code", { style: codeStyle }, lineElements)
1751
1862
  )
1752
1863
  );
1753
1864
  }
1754
1865
 
1755
1866
  // ui/src/docs/MarkdownTable.jsx
1756
- import React27 from "react";
1867
+ import React28 from "react";
1757
1868
  function MarkdownTable({ className = "", ...rest }) {
1758
1869
  const merged = ["markdown-table", className].filter(Boolean).join(" ");
1759
- return /* @__PURE__ */ React27.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React27.createElement("table", { className: merged, ...rest }));
1870
+ return /* @__PURE__ */ React28.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React28.createElement("table", { className: merged, ...rest }));
1760
1871
  }
1761
1872
  export {
1762
1873
  Button,
@@ -1769,6 +1880,7 @@ export {
1769
1880
  DocsCodeBlock,
1770
1881
  MarkdownTable as DocsMarkdownTable,
1771
1882
  HelloWorld,
1883
+ Image,
1772
1884
  interstitials_exports as Interstitials,
1773
1885
  Label,
1774
1886
  Layout,