@canopy-iiif/app 0.9.14 → 0.10.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.
@@ -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
  );
@@ -828,21 +885,17 @@ function Layout({
828
885
  className
829
886
  ].filter(Boolean).join(" ");
830
887
  const leftAsideClassName = [
831
- "mt-8 md:mt-0 md:order-1 md:sticky md:top-24 md:max-h-[calc(100vh-6rem)] md:overflow-y-auto text-sm text-slate-600",
888
+ "mt-8 md:mt-0 md:order-1 md:sticky md:top-24 md:max-h-[calc(100vh-6rem)] md:overflow-y-auto text-slate-600",
832
889
  sidebarClassName
833
890
  ].filter(Boolean).join(" ");
834
891
  const contentOrderClass = showLeftColumn ? "md:order-2" : hasContentNavigation ? "md:order-1" : "";
835
- const contentClassNames = [
836
- "space-y-6",
837
- contentOrderClass,
838
- contentClassName
839
- ].filter(Boolean).join(" ");
892
+ const contentClassNames = ["space-y-6", contentOrderClass, contentClassName].filter(Boolean).join(" ");
840
893
  const contentNavigationAsideClassName = [
841
- "hidden md:block md:order-3 mt-8 md:mt-0 md:sticky md:top-24 md:max-h-[calc(100vh-6rem)] md:overflow-y-auto text-sm text-slate-600",
894
+ "hidden md:block md:order-3 mt-8 md:mt-0 md:sticky md:top-24 md:max-h-[calc(100vh-6rem)] md:overflow-y-auto text-slate-600",
842
895
  contentNavigationClassName
843
896
  ].filter(Boolean).join(" ");
844
897
  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(
898
+ 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
899
  ContentNavigation,
847
900
  {
848
901
  items: headingTree,
@@ -854,17 +907,17 @@ function Layout({
854
907
  }
855
908
 
856
909
  // ui/src/layout/CanopyHeader.jsx
857
- import React18 from "react";
910
+ import React19 from "react";
858
911
 
859
912
  // ui/src/search/SearchPanel.jsx
860
- import React15 from "react";
913
+ import React16 from "react";
861
914
 
862
915
  // 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" }));
916
+ import React13 from "react";
917
+ 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
918
 
866
919
  // ui/src/search/SearchPanelForm.jsx
867
- import React13 from "react";
920
+ import React14 from "react";
868
921
  function readBasePath() {
869
922
  const normalize = (val) => {
870
923
  const raw = typeof val === "string" ? val.trim() : "";
@@ -927,18 +980,18 @@ function SearchPanelForm(props = {}) {
927
980
  clearLabel = "Clear search"
928
981
  } = props || {};
929
982
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
930
- const action = React13.useMemo(
983
+ const action = React14.useMemo(
931
984
  () => resolveSearchPath(searchPath),
932
985
  [searchPath]
933
986
  );
934
- const autoId = typeof React13.useId === "function" ? React13.useId() : void 0;
935
- const [fallbackId] = React13.useState(
987
+ const autoId = typeof React14.useId === "function" ? React14.useId() : void 0;
988
+ const [fallbackId] = React14.useState(
936
989
  () => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
937
990
  );
938
991
  const inputId = inputIdProp || autoId || fallbackId;
939
- const inputRef = React13.useRef(null);
940
- const [hasValue, setHasValue] = React13.useState(false);
941
- const focusInput = React13.useCallback(() => {
992
+ const inputRef = React14.useRef(null);
993
+ const [hasValue, setHasValue] = React14.useState(false);
994
+ const focusInput = React14.useCallback(() => {
942
995
  const el = inputRef.current;
943
996
  if (!el) return;
944
997
  if (document.activeElement === el) return;
@@ -951,7 +1004,7 @@ function SearchPanelForm(props = {}) {
951
1004
  }
952
1005
  }
953
1006
  }, []);
954
- const handlePointerDown = React13.useCallback(
1007
+ const handlePointerDown = React14.useCallback(
955
1008
  (event) => {
956
1009
  const target = event.target;
957
1010
  if (target && typeof target.closest === "function") {
@@ -963,23 +1016,23 @@ function SearchPanelForm(props = {}) {
963
1016
  },
964
1017
  [focusInput]
965
1018
  );
966
- React13.useEffect(() => {
1019
+ React14.useEffect(() => {
967
1020
  const el = inputRef.current;
968
1021
  if (!el) return;
969
1022
  if (el.value && el.value.trim()) {
970
1023
  setHasValue(true);
971
1024
  }
972
1025
  }, []);
973
- const handleInputChange = React13.useCallback((event) => {
1026
+ const handleInputChange = React14.useCallback((event) => {
974
1027
  var _a;
975
1028
  const nextHasValue = Boolean(
976
1029
  ((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
977
1030
  );
978
1031
  setHasValue(nextHasValue);
979
1032
  }, []);
980
- const handleClear = React13.useCallback((event) => {
1033
+ const handleClear = React14.useCallback((event) => {
981
1034
  }, []);
982
- const handleClearKey = React13.useCallback(
1035
+ const handleClearKey = React14.useCallback(
983
1036
  (event) => {
984
1037
  if (event.key === "Enter" || event.key === " ") {
985
1038
  event.preventDefault();
@@ -988,7 +1041,7 @@ function SearchPanelForm(props = {}) {
988
1041
  },
989
1042
  [handleClear]
990
1043
  );
991
- return /* @__PURE__ */ React13.createElement(
1044
+ return /* @__PURE__ */ React14.createElement(
992
1045
  "form",
993
1046
  {
994
1047
  action,
@@ -1000,7 +1053,7 @@ function SearchPanelForm(props = {}) {
1000
1053
  onPointerDown: handlePointerDown,
1001
1054
  "data-has-value": hasValue ? "1" : "0"
1002
1055
  },
1003
- /* @__PURE__ */ React13.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React13.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React13.createElement(
1056
+ /* @__PURE__ */ React14.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React14.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React14.createElement(
1004
1057
  "input",
1005
1058
  {
1006
1059
  id: inputId,
@@ -1016,7 +1069,7 @@ function SearchPanelForm(props = {}) {
1016
1069
  onInput: handleInputChange
1017
1070
  }
1018
1071
  )),
1019
- hasValue ? /* @__PURE__ */ React13.createElement(
1072
+ hasValue ? /* @__PURE__ */ React14.createElement(
1020
1073
  "button",
1021
1074
  {
1022
1075
  type: "button",
@@ -1029,32 +1082,31 @@ function SearchPanelForm(props = {}) {
1029
1082
  },
1030
1083
  "\xD7"
1031
1084
  ) : null,
1032
- /* @__PURE__ */ React13.createElement(
1085
+ /* @__PURE__ */ React14.createElement(
1033
1086
  "button",
1034
1087
  {
1035
1088
  type: "submit",
1036
1089
  "data-canopy-search-form-trigger": "submit",
1037
1090
  className: "canopy-search-form__submit"
1038
1091
  },
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"))
1092
+ text
1041
1093
  )
1042
1094
  );
1043
1095
  }
1044
1096
 
1045
1097
  // ui/src/search/SearchPanelTeaserResults.jsx
1046
- import React14 from "react";
1098
+ import React15 from "react";
1047
1099
  function SearchPanelTeaserResults(props = {}) {
1048
1100
  const { style, className } = props || {};
1049
1101
  const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
1050
- return /* @__PURE__ */ React14.createElement(
1102
+ return /* @__PURE__ */ React15.createElement(
1051
1103
  "div",
1052
1104
  {
1053
1105
  "data-canopy-search-form-panel": true,
1054
1106
  className: classes || void 0,
1055
1107
  style
1056
1108
  },
1057
- /* @__PURE__ */ React14.createElement("div", { id: "cplist" })
1109
+ /* @__PURE__ */ React15.createElement("div", { id: "cplist" })
1058
1110
  );
1059
1111
  }
1060
1112
 
@@ -1075,11 +1127,11 @@ function SearchPanel(props = {}) {
1075
1127
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
1076
1128
  const resolvedSearchPath = resolveSearchPath(searchPath);
1077
1129
  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) } }));
1130
+ 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
1131
  }
1080
1132
 
1081
1133
  // ui/src/layout/CanopyBrand.jsx
1082
- import React16 from "react";
1134
+ import React17 from "react";
1083
1135
  function CanopyBrand(props = {}) {
1084
1136
  const {
1085
1137
  labelId,
@@ -1090,11 +1142,11 @@ function CanopyBrand(props = {}) {
1090
1142
  } = props || {};
1091
1143
  const spanProps = labelId ? { id: labelId } : {};
1092
1144
  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));
1145
+ return /* @__PURE__ */ React17.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React17.createElement(Logo, null) : null, /* @__PURE__ */ React17.createElement("span", { ...spanProps }, label));
1094
1146
  }
1095
1147
 
1096
1148
  // ui/src/layout/CanopyModal.jsx
1097
- import React17 from "react";
1149
+ import React18 from "react";
1098
1150
  function CanopyModal(props = {}) {
1099
1151
  const {
1100
1152
  id,
@@ -1145,7 +1197,7 @@ function CanopyModal(props = {}) {
1145
1197
  if (padded) bodyClasses.push("canopy-modal__body--padded");
1146
1198
  if (bodyClassName) bodyClasses.push(bodyClassName);
1147
1199
  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(
1200
+ return /* @__PURE__ */ React18.createElement("div", { ...modalProps }, /* @__PURE__ */ React18.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React18.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React18.createElement(
1149
1201
  "svg",
1150
1202
  {
1151
1203
  xmlns: "http://www.w3.org/2000/svg",
@@ -1155,8 +1207,8 @@ function CanopyModal(props = {}) {
1155
1207
  strokeWidth: "1.5",
1156
1208
  className: "canopy-modal__close-icon"
1157
1209
  },
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(
1210
+ /* @__PURE__ */ React18.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
1211
+ ), /* @__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
1212
  CanopyBrand,
1161
1213
  {
1162
1214
  labelId: resolvedLabelId,
@@ -1360,7 +1412,7 @@ function HeaderScript() {
1360
1412
  });
1361
1413
  })();
1362
1414
  `;
1363
- return /* @__PURE__ */ React18.createElement(
1415
+ return /* @__PURE__ */ React19.createElement(
1364
1416
  "script",
1365
1417
  {
1366
1418
  dangerouslySetInnerHTML: {
@@ -1371,7 +1423,9 @@ function HeaderScript() {
1371
1423
  }
1372
1424
  function ensureArray(navLinks) {
1373
1425
  if (!Array.isArray(navLinks)) return [];
1374
- return navLinks.filter((link) => link && typeof link === "object" && typeof link.href === "string");
1426
+ return navLinks.filter(
1427
+ (link) => link && typeof link === "object" && typeof link.href === "string"
1428
+ );
1375
1429
  }
1376
1430
  function CanopyHeader(props = {}) {
1377
1431
  const {
@@ -1384,59 +1438,98 @@ function CanopyHeader(props = {}) {
1384
1438
  logo: SiteLogo
1385
1439
  } = props;
1386
1440
  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",
1441
+ return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement(
1442
+ "header",
1397
1443
  {
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"
1444
+ className: "canopy-header",
1445
+ "data-mobile-nav": "closed",
1446
+ "data-mobile-search": "closed"
1404
1447
  },
1405
- /* @__PURE__ */ React18.createElement(
1406
- "svg",
1448
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React19.createElement(
1449
+ CanopyBrand,
1407
1450
  {
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"
1451
+ label: title,
1452
+ href: brandHref,
1453
+ className: "canopy-header__brand-link",
1454
+ Logo: SiteLogo
1455
+ }
1456
+ )),
1457
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React19.createElement(
1458
+ SearchPanel,
1459
+ {
1460
+ label: searchLabel,
1461
+ hotkey: searchHotkey,
1462
+ placeholder: searchPlaceholder
1463
+ }
1464
+ )),
1465
+ /* @__PURE__ */ React19.createElement(
1466
+ "nav",
1467
+ {
1468
+ className: "canopy-nav-links canopy-header__desktop-nav",
1469
+ "aria-label": "Primary navigation"
1414
1470
  },
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",
1471
+ navLinks.map((link) => /* @__PURE__ */ React19.createElement("a", { key: link.href, href: link.href }, link.label || link.href))
1472
+ ),
1473
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__actions" }, /* @__PURE__ */ React19.createElement(
1474
+ "button",
1429
1475
  {
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"
1476
+ type: "button",
1477
+ className: "canopy-header__icon-button canopy-header__search-trigger",
1478
+ "aria-label": "Open search",
1479
+ "aria-controls": "canopy-modal-search",
1480
+ "aria-expanded": "false",
1481
+ "data-canopy-header-toggle": "search"
1436
1482
  },
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(
1483
+ /* @__PURE__ */ React19.createElement(
1484
+ "svg",
1485
+ {
1486
+ xmlns: "http://www.w3.org/2000/svg",
1487
+ viewBox: "0 0 24 24",
1488
+ fill: "none",
1489
+ stroke: "currentColor",
1490
+ strokeWidth: "1.5",
1491
+ className: "canopy-header__search-icon"
1492
+ },
1493
+ /* @__PURE__ */ React19.createElement(
1494
+ "path",
1495
+ {
1496
+ strokeLinecap: "round",
1497
+ strokeLinejoin: "round",
1498
+ 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"
1499
+ }
1500
+ )
1501
+ )
1502
+ ), /* @__PURE__ */ React19.createElement(
1503
+ "button",
1504
+ {
1505
+ type: "button",
1506
+ className: "canopy-header__icon-button canopy-header__menu",
1507
+ "aria-label": "Open navigation",
1508
+ "aria-controls": "canopy-modal-nav",
1509
+ "aria-expanded": "false",
1510
+ "data-canopy-header-toggle": "nav"
1511
+ },
1512
+ /* @__PURE__ */ React19.createElement(
1513
+ "svg",
1514
+ {
1515
+ xmlns: "http://www.w3.org/2000/svg",
1516
+ fill: "none",
1517
+ viewBox: "0 0 24 24",
1518
+ strokeWidth: "1.5",
1519
+ stroke: "currentColor",
1520
+ className: "canopy-header__menu-icon"
1521
+ },
1522
+ /* @__PURE__ */ React19.createElement(
1523
+ "path",
1524
+ {
1525
+ strokeLinecap: "round",
1526
+ strokeLinejoin: "round",
1527
+ d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
1528
+ }
1529
+ )
1530
+ )
1531
+ ))
1532
+ ), /* @__PURE__ */ React19.createElement(
1440
1533
  CanopyModal,
1441
1534
  {
1442
1535
  id: "canopy-modal-nav",
@@ -1448,8 +1541,15 @@ function CanopyHeader(props = {}) {
1448
1541
  closeLabel: "Close navigation",
1449
1542
  closeDataAttr: "nav"
1450
1543
  },
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(
1544
+ /* @__PURE__ */ React19.createElement(
1545
+ "nav",
1546
+ {
1547
+ className: "canopy-nav-links canopy-modal__nav",
1548
+ "aria-label": "Primary navigation"
1549
+ },
1550
+ navLinks.map((link) => /* @__PURE__ */ React19.createElement("a", { key: link.href, href: link.href }, link.label || link.href))
1551
+ )
1552
+ ), /* @__PURE__ */ React19.createElement(
1453
1553
  CanopyModal,
1454
1554
  {
1455
1555
  id: "canopy-modal-search",
@@ -1462,26 +1562,33 @@ function CanopyHeader(props = {}) {
1462
1562
  closeDataAttr: "search",
1463
1563
  bodyClassName: "canopy-modal__body--search"
1464
1564
  },
1465
- /* @__PURE__ */ React18.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })
1466
- ), /* @__PURE__ */ React18.createElement(HeaderScript, null));
1565
+ /* @__PURE__ */ React19.createElement(
1566
+ SearchPanel,
1567
+ {
1568
+ label: searchLabel,
1569
+ hotkey: searchHotkey,
1570
+ placeholder: searchPlaceholder
1571
+ }
1572
+ )
1573
+ ), /* @__PURE__ */ React19.createElement(HeaderScript, null));
1467
1574
  }
1468
1575
 
1469
1576
  // ui/src/layout/CanopyFooter.jsx
1470
- import React19 from "react";
1577
+ import React20 from "react";
1471
1578
  function CanopyFooter({ className = "", children }) {
1472
1579
  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));
1580
+ return /* @__PURE__ */ React20.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React20.createElement("div", { className: "canopy-footer__inner" }, children));
1474
1581
  }
1475
1582
 
1476
1583
  // ui/src/layout/Container.jsx
1477
- import React20 from "react";
1584
+ import React21 from "react";
1478
1585
  function Container({ className = "", children, ...rest }) {
1479
1586
  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);
1587
+ return /* @__PURE__ */ React21.createElement("div", { className: classes, ...rest }, children);
1481
1588
  }
1482
1589
 
1483
1590
  // ui/src/search/MdxSearchResults.jsx
1484
- import React21 from "react";
1591
+ import React22 from "react";
1485
1592
  function MdxSearchResults(props) {
1486
1593
  let json = "{}";
1487
1594
  try {
@@ -1489,11 +1596,11 @@ function MdxSearchResults(props) {
1489
1596
  } catch (_) {
1490
1597
  json = "{}";
1491
1598
  }
1492
- return /* @__PURE__ */ React21.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React21.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1599
+ return /* @__PURE__ */ React22.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React22.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1493
1600
  }
1494
1601
 
1495
1602
  // ui/src/search/SearchSummary.jsx
1496
- import React22 from "react";
1603
+ import React23 from "react";
1497
1604
  function SearchSummary(props) {
1498
1605
  let json = "{}";
1499
1606
  try {
@@ -1501,11 +1608,11 @@ function SearchSummary(props) {
1501
1608
  } catch (_) {
1502
1609
  json = "{}";
1503
1610
  }
1504
- return /* @__PURE__ */ React22.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React22.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1611
+ return /* @__PURE__ */ React23.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React23.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1505
1612
  }
1506
1613
 
1507
1614
  // ui/src/search/MdxSearchTabs.jsx
1508
- import React23 from "react";
1615
+ import React24 from "react";
1509
1616
  function MdxSearchTabs(props) {
1510
1617
  let json = "{}";
1511
1618
  try {
@@ -1513,11 +1620,11 @@ function MdxSearchTabs(props) {
1513
1620
  } catch (_) {
1514
1621
  json = "{}";
1515
1622
  }
1516
- return /* @__PURE__ */ React23.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React23.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1623
+ return /* @__PURE__ */ React24.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React24.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
1517
1624
  }
1518
1625
 
1519
1626
  // ui/src/search-form/MdxSearchFormModal.jsx
1520
- import React24 from "react";
1627
+ import React25 from "react";
1521
1628
  function MdxSearchFormModal(props = {}) {
1522
1629
  const {
1523
1630
  placeholder = "Search\u2026",
@@ -1533,11 +1640,11 @@ function MdxSearchFormModal(props = {}) {
1533
1640
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
1534
1641
  const resolvedSearchPath = resolveSearchPath(searchPath);
1535
1642
  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) } }));
1643
+ 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
1644
  }
1538
1645
 
1539
1646
  // ui/src/iiif/ManifestPrimitives.jsx
1540
- import React25 from "react";
1647
+ import React26 from "react";
1541
1648
  import {
1542
1649
  Label as CloverLabel,
1543
1650
  Metadata as CloverMetadata,
@@ -1562,28 +1669,28 @@ function ensureMetadata(items) {
1562
1669
  function Label({ manifest, label, ...rest }) {
1563
1670
  const intl = label || manifest && manifest.label;
1564
1671
  if (!hasInternationalValue(intl)) return null;
1565
- return /* @__PURE__ */ React25.createElement(CloverLabel, { label: intl, ...rest });
1672
+ return /* @__PURE__ */ React26.createElement(CloverLabel, { label: intl, ...rest });
1566
1673
  }
1567
1674
  function Summary({ manifest, summary, ...rest }) {
1568
1675
  const intl = summary || manifest && manifest.summary;
1569
1676
  if (!hasInternationalValue(intl)) return null;
1570
- return /* @__PURE__ */ React25.createElement(CloverSummary, { summary: intl, ...rest });
1677
+ return /* @__PURE__ */ React26.createElement(CloverSummary, { summary: intl, ...rest });
1571
1678
  }
1572
1679
  function Metadata({ manifest, metadata, ...rest }) {
1573
1680
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
1574
1681
  if (!items.length) return null;
1575
- return /* @__PURE__ */ React25.createElement(CloverMetadata, { metadata: items, ...rest });
1682
+ return /* @__PURE__ */ React26.createElement(CloverMetadata, { metadata: items, ...rest });
1576
1683
  }
1577
1684
  function RequiredStatement({ manifest, requiredStatement, ...rest }) {
1578
1685
  const stmt = requiredStatement || manifest && manifest.requiredStatement;
1579
1686
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
1580
1687
  return null;
1581
1688
  }
1582
- return /* @__PURE__ */ React25.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
1689
+ return /* @__PURE__ */ React26.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
1583
1690
  }
1584
1691
 
1585
1692
  // ui/src/docs/CodeBlock.jsx
1586
- import React26 from "react";
1693
+ import React27 from "react";
1587
1694
  function parseHighlightAttr(attr) {
1588
1695
  if (!attr) return /* @__PURE__ */ new Set();
1589
1696
  const cleaned = String(attr || "").trim();
@@ -1629,10 +1736,10 @@ var highlightBaseStyle = {
1629
1736
  };
1630
1737
  function DocsCodeBlock(props = {}) {
1631
1738
  const { children, ...rest } = props;
1632
- const childArray = React26.Children.toArray(children);
1633
- const codeElement = childArray.find((el) => React26.isValidElement(el));
1739
+ const childArray = React27.Children.toArray(children);
1740
+ const codeElement = childArray.find((el) => React27.isValidElement(el));
1634
1741
  if (!codeElement || !codeElement.props) {
1635
- return React26.createElement("pre", props);
1742
+ return React27.createElement("pre", props);
1636
1743
  }
1637
1744
  const {
1638
1745
  className = "",
@@ -1647,8 +1754,8 @@ function DocsCodeBlock(props = {}) {
1647
1754
  const highlightSet = parseHighlightAttr(highlightAttr);
1648
1755
  const copyAttr = codeProps["data-copy"];
1649
1756
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
1650
- const [copied, setCopied] = React26.useState(false);
1651
- const handleCopy = React26.useCallback(async () => {
1757
+ const [copied, setCopied] = React27.useState(false);
1758
+ const handleCopy = React27.useCallback(async () => {
1652
1759
  const text = rawCode;
1653
1760
  try {
1654
1761
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -1712,20 +1819,20 @@ function DocsCodeBlock(props = {}) {
1712
1819
  const highlight = highlightSet.has(lineNumber);
1713
1820
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
1714
1821
  const displayLine = line === "" ? " " : line;
1715
- return React26.createElement(
1822
+ return React27.createElement(
1716
1823
  "span",
1717
1824
  { key: lineNumber, style },
1718
- React26.createElement("span", { style: lineContentStyle }, displayLine)
1825
+ React27.createElement("span", { style: lineContentStyle }, displayLine)
1719
1826
  );
1720
1827
  });
1721
- return React26.createElement(
1828
+ return React27.createElement(
1722
1829
  "div",
1723
1830
  { style: containerStyle },
1724
- React26.createElement(
1831
+ React27.createElement(
1725
1832
  "div",
1726
1833
  { style: headerStyle },
1727
- React26.createElement("span", null, showFilename ? filename : null),
1728
- enableCopy ? React26.createElement(
1834
+ React27.createElement("span", null, showFilename ? filename : null),
1835
+ enableCopy ? React27.createElement(
1729
1836
  "button",
1730
1837
  {
1731
1838
  type: "button",
@@ -1744,19 +1851,19 @@ function DocsCodeBlock(props = {}) {
1744
1851
  copied ? "Copied" : "Copy"
1745
1852
  ) : null
1746
1853
  ),
1747
- React26.createElement(
1854
+ React27.createElement(
1748
1855
  "pre",
1749
1856
  { ...preRest, className: preClassName, style: mergedPreStyle },
1750
- React26.createElement("code", { style: codeStyle }, lineElements)
1857
+ React27.createElement("code", { style: codeStyle }, lineElements)
1751
1858
  )
1752
1859
  );
1753
1860
  }
1754
1861
 
1755
1862
  // ui/src/docs/MarkdownTable.jsx
1756
- import React27 from "react";
1863
+ import React28 from "react";
1757
1864
  function MarkdownTable({ className = "", ...rest }) {
1758
1865
  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 }));
1866
+ return /* @__PURE__ */ React28.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React28.createElement("table", { className: merged, ...rest }));
1760
1867
  }
1761
1868
  export {
1762
1869
  Button,
@@ -1769,6 +1876,7 @@ export {
1769
1876
  DocsCodeBlock,
1770
1877
  MarkdownTable as DocsMarkdownTable,
1771
1878
  HelloWorld,
1879
+ Image,
1772
1880
  interstitials_exports as Interstitials,
1773
1881
  Label,
1774
1882
  Layout,