@canopy-iiif/app 0.9.13 → 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 React6 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
@@ -183,7 +220,50 @@ function computeHeroHeightStyle(height) {
183
220
  return { width: "100%", height: val };
184
221
  }
185
222
 
223
+ // ui/src/layout/Button.jsx
224
+ import React7 from "react";
225
+ var VARIANTS = /* @__PURE__ */ new Set(["primary", "secondary"]);
226
+ function Button({
227
+ label,
228
+ href = "#",
229
+ target,
230
+ rel,
231
+ variant = "primary",
232
+ className = "",
233
+ children,
234
+ ...rest
235
+ }) {
236
+ const resolvedVariant = VARIANTS.has(variant) ? variant : "primary";
237
+ const computedRel = target === "_blank" && !rel ? "noopener noreferrer" : rel;
238
+ const content = children != null ? children : label;
239
+ if (!content) return null;
240
+ const classes = [
241
+ "canopy-button",
242
+ `canopy-button--${resolvedVariant}`,
243
+ className
244
+ ].filter(Boolean).join(" ");
245
+ return /* @__PURE__ */ React7.createElement(
246
+ "a",
247
+ {
248
+ href,
249
+ className: classes,
250
+ target,
251
+ rel: computedRel,
252
+ ...rest
253
+ },
254
+ content
255
+ );
256
+ }
257
+
258
+ // ui/src/layout/ButtonWrapper.jsx
259
+ import React8 from "react";
260
+ function ButtonWrapper({ className = "", children, ...rest }) {
261
+ const classes = ["canopy-button-group", className].filter(Boolean).join(" ");
262
+ return /* @__PURE__ */ React8.createElement("div", { className: classes, ...rest }, children);
263
+ }
264
+
186
265
  // ui/src/interstitials/Hero.jsx
266
+ var HERO_DEFAULT_SIZES_ATTR = "(min-width: 1024px) 1280px, 100vw";
187
267
  var basePath = (() => {
188
268
  try {
189
269
  const raw = typeof process !== "undefined" && process && process.env ? String(process.env.CANOPY_BASE_PATH || "") : "";
@@ -225,7 +305,8 @@ function normalizeLinks(links) {
225
305
  const title = link.title ? String(link.title) : "";
226
306
  if (!href || !title) return null;
227
307
  const type = link.type === "secondary" ? "secondary" : "primary";
228
- return { href, title, type };
308
+ const target = link.target ? String(link.target) : void 0;
309
+ return { href, title, type, target };
229
310
  }).filter(Boolean);
230
311
  }
231
312
  function sanitizeRest(rest) {
@@ -257,6 +338,7 @@ function Hero({
257
338
  item,
258
339
  index,
259
340
  random = true,
341
+ transition = "fade",
260
342
  headline,
261
343
  description,
262
344
  links = [],
@@ -323,6 +405,15 @@ function Hero({
323
405
  backgroundClassName,
324
406
  className
325
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
+ })();
326
417
  const renderSlide = (slide, idx, { showVeil = true, captionVariant = "overlay" } = {}) => {
327
418
  const safeHref = applyBasePath(slide.href || "#");
328
419
  const isStaticCaption = captionVariant === "static";
@@ -331,68 +422,78 @@ function Hero({
331
422
  showVeil ? "" : "canopy-interstitial__pane--flat",
332
423
  isStaticCaption ? "canopy-interstitial__pane--static" : ""
333
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
+ };
334
439
  if (isStaticCaption) {
335
- return /* @__PURE__ */ React6.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React6.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__media-frame" }, /* @__PURE__ */ React6.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(
336
441
  "img",
337
442
  {
338
- src: slide.thumbnail,
339
- alt: "",
340
- "aria-hidden": "true",
341
- className: "canopy-interstitial__media canopy-interstitial__media--static",
342
- loading: idx === 0 ? "eager" : "lazy"
443
+ ...buildImageProps(
444
+ "canopy-interstitial__media canopy-interstitial__media--static"
445
+ )
343
446
  }
344
- )) : null, slide.title ? /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__caption canopy-interstitial__caption--static" }, /* @__PURE__ */ React6.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));
345
448
  }
346
- return /* @__PURE__ */ React6.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React6.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React6.createElement(
449
+ return /* @__PURE__ */ React9.createElement("div", { className: "swiper-slide", key: safeHref || idx }, /* @__PURE__ */ React9.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React9.createElement(
347
450
  "img",
348
451
  {
349
- src: slide.thumbnail,
350
- alt: "",
351
- "aria-hidden": "true",
352
- className: "canopy-interstitial__media",
353
- loading: idx === 0 ? "eager" : "lazy"
452
+ ...buildImageProps("canopy-interstitial__media")
354
453
  }
355
- ) : null, showVeil ? /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__veil", "aria-hidden": "true" }) : null, slide.title ? /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__caption" }, /* @__PURE__ */ React6.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));
356
455
  };
357
- const renderSlider = (options = {}) => /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__slider swiper" }, /* @__PURE__ */ React6.createElement("div", { className: "swiper-wrapper" }, orderedSlides.map((slide, idx) => renderSlide(slide, idx, options))), /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__nav" }, /* @__PURE__ */ React6.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(
358
457
  "button",
359
458
  {
360
459
  type: "button",
361
460
  "aria-label": "Previous slide",
362
461
  className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--prev swiper-button-prev"
363
462
  }
364
- ), /* @__PURE__ */ React6.createElement(
463
+ ), /* @__PURE__ */ React9.createElement(
365
464
  "button",
366
465
  {
367
466
  type: "button",
368
467
  "aria-label": "Next slide",
369
468
  className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--next swiper-button-next"
370
469
  }
371
- )), /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__pagination swiper-pagination" }));
372
- const overlayContent = /* @__PURE__ */ React6.createElement(React6.Fragment, null, overlayTitle ? /* @__PURE__ */ React6.createElement("h1", { className: "canopy-interstitial__headline" }, overlayTitle) : null, derivedDescription ? /* @__PURE__ */ React6.createElement("p", { className: "canopy-interstitial__description" }, derivedDescription) : null, overlayLinks.length ? /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__actions" }, overlayLinks.map((link) => /* @__PURE__ */ React6.createElement(
373
- "a",
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(
472
+ Button,
374
473
  {
375
474
  key: `${link.href}-${link.title}`,
376
475
  href: link.href,
377
- className: link.type === "secondary" ? "canopy-button canopy-button--secondary" : "canopy-button canopy-button--primary"
378
- },
379
- link.title
476
+ label: link.title,
477
+ variant: link.type,
478
+ target: link.target
479
+ }
380
480
  ))) : null);
381
481
  const cleanedProps = sanitizeRest(rest);
382
- return /* @__PURE__ */ React6.createElement(
482
+ return /* @__PURE__ */ React9.createElement(
383
483
  "section",
384
484
  {
385
485
  className: containerClassName,
386
486
  "data-canopy-hero-slider": "1",
487
+ "data-transition": normalizedTransition,
387
488
  style: heroStyles,
388
489
  ...cleanedProps
389
490
  },
390
- /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React6.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React6.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" })))
391
492
  );
392
493
  }
393
494
 
394
495
  // ui/src/layout/SubNavigation.jsx
395
- import React7 from "react";
496
+ import React10 from "react";
396
497
  import navigationHelpers from "@canopy-iiif/app/lib/components/navigation.js";
397
498
  function resolveRelativeCandidate(page, current) {
398
499
  if (page && typeof page.relativePath === "string" && page.relativePath) return page.relativePath;
@@ -417,14 +518,14 @@ function renderNodes(nodes, parentKey = "node") {
417
518
  if (node.isActive) classes.push("is-active");
418
519
  const linkClass = classes.join(" ");
419
520
  const Tag = node.href ? "a" : "span";
420
- return /* @__PURE__ */ React7.createElement(
521
+ return /* @__PURE__ */ React10.createElement(
421
522
  "li",
422
523
  {
423
524
  key,
424
525
  className: "canopy-sub-navigation__item",
425
526
  "data-depth": depth
426
527
  },
427
- /* @__PURE__ */ React7.createElement(
528
+ /* @__PURE__ */ React10.createElement(
428
529
  Tag,
429
530
  {
430
531
  className: linkClass,
@@ -433,7 +534,7 @@ function renderNodes(nodes, parentKey = "node") {
433
534
  },
434
535
  node.title || node.slug
435
536
  ),
436
- showChildren ? /* @__PURE__ */ React7.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
437
538
  );
438
539
  });
439
540
  }
@@ -447,12 +548,12 @@ function SubNavigation({
447
548
  ariaLabel
448
549
  }) {
449
550
  const PageContext = navigationHelpers && navigationHelpers.getPageContext ? navigationHelpers.getPageContext() : null;
450
- const context = PageContext ? React7.useContext(PageContext) : null;
551
+ const context = PageContext ? React10.useContext(PageContext) : null;
451
552
  const contextNavigation = context && context.navigation ? context.navigation : null;
452
553
  const contextPage = context && context.page ? context.page : null;
453
554
  const effectiveNavigation = navigationProp || contextNavigation;
454
555
  const effectivePage = page || contextPage;
455
- const resolvedNavigation = React7.useMemo(() => {
556
+ const resolvedNavigation = React10.useMemo(() => {
456
557
  if (effectiveNavigation && effectiveNavigation.root) return effectiveNavigation;
457
558
  const candidate = resolveRelativeCandidate(effectivePage, current);
458
559
  if (!candidate) return effectiveNavigation || null;
@@ -476,15 +577,15 @@ function SubNavigation({
476
577
  if (!Object.prototype.hasOwnProperty.call(inlineStyle, "--sub-nav-indent")) {
477
578
  inlineStyle["--sub-nav-indent"] = "0.85rem";
478
579
  }
479
- return /* @__PURE__ */ React7.createElement("nav", { className: combinedClassName, style: inlineStyle, "aria-label": navLabel }, finalHeading ? /* @__PURE__ */ React7.createElement("div", { className: "canopy-sub-navigation__heading" }, finalHeading) : null, /* @__PURE__ */ React7.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")));
480
581
  }
481
582
 
482
583
  // ui/src/layout/Layout.jsx
483
- import React9 from "react";
584
+ import React12 from "react";
484
585
  import navigationHelpers2 from "@canopy-iiif/app/lib/components/navigation.js";
485
586
 
486
587
  // ui/src/layout/ContentNavigation.jsx
487
- import React8 from "react";
588
+ import React11 from "react";
488
589
  var SCROLL_OFFSET_REM = 1.618;
489
590
  function depthIndex(depth) {
490
591
  return Math.max(0, Math.min(5, (depth || 1) - 1));
@@ -499,12 +600,12 @@ function ContentNavigation({
499
600
  ariaLabel
500
601
  }) {
501
602
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
502
- const savedDepthsRef = React8.useRef(null);
603
+ const savedDepthsRef = React11.useRef(null);
503
604
  if ((!items || !items.length) && !headingId) return null;
504
605
  const combinedClassName = ["canopy-sub-navigation canopy-content-navigation", className].filter(Boolean).join(" ");
505
606
  const effectiveHeading = heading || pageTitle || null;
506
607
  const navLabel = ariaLabel || (effectiveHeading ? `${effectiveHeading} navigation` : "Section navigation");
507
- const getSavedDepth = React8.useCallback(
608
+ const getSavedDepth = React11.useCallback(
508
609
  (id, fallback) => {
509
610
  if (!id) return fallback;
510
611
  if (!savedDepthsRef.current) savedDepthsRef.current = /* @__PURE__ */ new Map();
@@ -515,7 +616,7 @@ function ContentNavigation({
515
616
  },
516
617
  []
517
618
  );
518
- const headingEntries = React8.useMemo(() => {
619
+ const headingEntries = React11.useMemo(() => {
519
620
  const entries = [];
520
621
  const seen = /* @__PURE__ */ new Set();
521
622
  if (headingId) {
@@ -539,12 +640,12 @@ function ContentNavigation({
539
640
  return entries;
540
641
  }, [headingId, items, getSavedDepth]);
541
642
  const fallbackId = headingEntries.length ? headingEntries[0].id : headingId || null;
542
- const [activeId, setActiveId] = React8.useState(fallbackId);
543
- const activeIdRef = React8.useRef(activeId);
544
- React8.useEffect(() => {
643
+ const [activeId, setActiveId] = React11.useState(fallbackId);
644
+ const activeIdRef = React11.useRef(activeId);
645
+ React11.useEffect(() => {
545
646
  activeIdRef.current = activeId;
546
647
  }, [activeId]);
547
- React8.useEffect(() => {
648
+ React11.useEffect(() => {
548
649
  if (!headingEntries.length) return;
549
650
  if (!headingEntries.some((entry) => entry.id === activeIdRef.current)) {
550
651
  const next = headingEntries[0].id;
@@ -552,7 +653,7 @@ function ContentNavigation({
552
653
  setActiveId(next);
553
654
  }
554
655
  }, [headingEntries]);
555
- const computeOffsetPx = React8.useCallback(() => {
656
+ const computeOffsetPx = React11.useCallback(() => {
556
657
  if (!isBrowser) return 0;
557
658
  try {
558
659
  const root = document.documentElement;
@@ -562,8 +663,8 @@ function ContentNavigation({
562
663
  return 0;
563
664
  }
564
665
  }, [isBrowser]);
565
- const headingElementsRef = React8.useRef([]);
566
- const updateActiveFromElements = React8.useCallback(
666
+ const headingElementsRef = React11.useRef([]);
667
+ const updateActiveFromElements = React11.useCallback(
567
668
  (elements) => {
568
669
  if (!elements || !elements.length) return;
569
670
  const offset = computeOffsetPx();
@@ -583,7 +684,7 @@ function ContentNavigation({
583
684
  },
584
685
  [computeOffsetPx]
585
686
  );
586
- React8.useEffect(() => {
687
+ React11.useEffect(() => {
587
688
  if (!isBrowser) return void 0;
588
689
  const elements = headingEntries.map((entry) => {
589
690
  const element = document.getElementById(entry.id);
@@ -609,7 +710,7 @@ function ContentNavigation({
609
710
  window.removeEventListener("resize", handle);
610
711
  };
611
712
  }, [headingEntries, isBrowser, updateActiveFromElements]);
612
- const handleAnchorClick = React8.useCallback(
713
+ const handleAnchorClick = React11.useCallback(
613
714
  (event, targetId, options = {}) => {
614
715
  var _a;
615
716
  try {
@@ -640,7 +741,7 @@ function ContentNavigation({
640
741
  },
641
742
  [computeOffsetPx, headingEntries, headingId, isBrowser]
642
743
  );
643
- const renderNodes2 = React8.useCallback(
744
+ const renderNodes2 = React11.useCallback(
644
745
  (nodes) => {
645
746
  if (!nodes || !nodes.length) return null;
646
747
  return nodes.map((node) => {
@@ -649,7 +750,7 @@ function ContentNavigation({
649
750
  const depth = node.depth || node.level || getSavedDepth(id, 2);
650
751
  const idx = depthIndex(depth);
651
752
  const isActive = id && activeId === id;
652
- return /* @__PURE__ */ React8.createElement("li", { key: id || node.title, className: "canopy-sub-navigation__item", "data-depth": idx }, /* @__PURE__ */ React8.createElement(
753
+ return /* @__PURE__ */ React11.createElement("li", { key: id || node.title, className: "canopy-sub-navigation__item", "data-depth": idx }, /* @__PURE__ */ React11.createElement(
653
754
  "a",
654
755
  {
655
756
  className: `canopy-sub-navigation__link depth-${idx}${isActive ? " is-active" : ""}`,
@@ -658,7 +759,7 @@ function ContentNavigation({
658
759
  "aria-current": isActive ? "location" : void 0
659
760
  },
660
761
  node.title
661
- ), node.children && node.children.length ? /* @__PURE__ */ React8.createElement(
762
+ ), node.children && node.children.length ? /* @__PURE__ */ React11.createElement(
662
763
  "ul",
663
764
  {
664
765
  className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
@@ -671,7 +772,7 @@ function ContentNavigation({
671
772
  [handleAnchorClick, activeId, getSavedDepth]
672
773
  );
673
774
  const nestedItems = renderNodes2(items);
674
- const topLink = headingId ? /* @__PURE__ */ React8.createElement("li", { className: "canopy-sub-navigation__item", "data-depth": 0 }, /* @__PURE__ */ React8.createElement(
775
+ const topLink = headingId ? /* @__PURE__ */ React11.createElement("li", { className: "canopy-sub-navigation__item", "data-depth": 0 }, /* @__PURE__ */ React11.createElement(
675
776
  "a",
676
777
  {
677
778
  className: `canopy-sub-navigation__link depth-0${activeId === headingId ? " is-active" : ""}`,
@@ -680,7 +781,7 @@ function ContentNavigation({
680
781
  "aria-current": activeId === headingId ? "location" : void 0
681
782
  },
682
783
  effectiveHeading || pageTitle || headingId
683
- ), nestedItems ? /* @__PURE__ */ React8.createElement(
784
+ ), nestedItems ? /* @__PURE__ */ React11.createElement(
684
785
  "ul",
685
786
  {
686
787
  className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
@@ -688,7 +789,7 @@ function ContentNavigation({
688
789
  },
689
790
  nestedItems
690
791
  ) : null) : null;
691
- return /* @__PURE__ */ React8.createElement("nav", { className: combinedClassName, style, "aria-label": navLabel }, /* @__PURE__ */ React8.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));
692
793
  }
693
794
 
694
795
  // ui/src/layout/Layout.jsx
@@ -720,10 +821,10 @@ function buildHeadingTree(headings) {
720
821
  }
721
822
  function buildNavigationAside(sidebar, className) {
722
823
  if (!sidebar) {
723
- return /* @__PURE__ */ React9.createElement(SubNavigation, { className });
824
+ return /* @__PURE__ */ React12.createElement(SubNavigation, { className });
724
825
  }
725
826
  if (typeof sidebar === "function") {
726
- return React9.createElement(sidebar);
827
+ return React12.createElement(sidebar);
727
828
  }
728
829
  return sidebar;
729
830
  }
@@ -740,26 +841,26 @@ function Layout({
740
841
  ...rest
741
842
  }) {
742
843
  const PageContext = navigationHelpers2 && typeof navigationHelpers2.getPageContext === "function" ? navigationHelpers2.getPageContext() : null;
743
- const context = PageContext ? React9.useContext(PageContext) : null;
744
- const pageHeadings = React9.useMemo(() => {
844
+ const context = PageContext ? React12.useContext(PageContext) : null;
845
+ const pageHeadings = React12.useMemo(() => {
745
846
  const headings = context && context.page ? context.page.headings : null;
746
847
  return Array.isArray(headings) ? headings : [];
747
848
  }, [context]);
748
- const contentHeading = React9.useMemo(() => {
849
+ const contentHeading = React12.useMemo(() => {
749
850
  const first = pageHeadings.find((heading) => {
750
851
  const depth = heading && (heading.depth || heading.level);
751
852
  return depth === 1;
752
853
  });
753
854
  return first && first.title ? first.title : null;
754
855
  }, [pageHeadings]);
755
- const headingAnchorId = React9.useMemo(() => {
856
+ const headingAnchorId = React12.useMemo(() => {
756
857
  const first = pageHeadings.find((heading) => {
757
858
  const depth = heading && (heading.depth || heading.level);
758
859
  return depth === 1;
759
860
  });
760
861
  return first && first.id ? first.id : null;
761
862
  }, [pageHeadings]);
762
- const headingTree = React9.useMemo(
863
+ const headingTree = React12.useMemo(
763
864
  () => buildHeadingTree(pageHeadings),
764
865
  [pageHeadings]
765
866
  );
@@ -798,7 +899,7 @@ function Layout({
798
899
  contentNavigationClassName
799
900
  ].filter(Boolean).join(" ");
800
901
  const sidebarNode = showLeftColumn ? buildNavigationAside(sidebar, sidebarClassName) : null;
801
- return /* @__PURE__ */ React9.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React9.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React9.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React9.createElement("aside", { className: contentNavigationAsideClassName }, /* @__PURE__ */ React9.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(
802
903
  ContentNavigation,
803
904
  {
804
905
  items: headingTree,
@@ -810,17 +911,17 @@ function Layout({
810
911
  }
811
912
 
812
913
  // ui/src/layout/CanopyHeader.jsx
813
- import React16 from "react";
914
+ import React19 from "react";
814
915
 
815
916
  // ui/src/search/SearchPanel.jsx
816
- import React13 from "react";
917
+ import React16 from "react";
817
918
 
818
919
  // ui/src/Icons.jsx
819
- import React10 from "react";
820
- var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React10.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React10.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" }));
821
922
 
822
923
  // ui/src/search/SearchPanelForm.jsx
823
- import React11 from "react";
924
+ import React14 from "react";
824
925
  function readBasePath() {
825
926
  const normalize = (val) => {
826
927
  const raw = typeof val === "string" ? val.trim() : "";
@@ -883,18 +984,18 @@ function SearchPanelForm(props = {}) {
883
984
  clearLabel = "Clear search"
884
985
  } = props || {};
885
986
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
886
- const action = React11.useMemo(
987
+ const action = React14.useMemo(
887
988
  () => resolveSearchPath(searchPath),
888
989
  [searchPath]
889
990
  );
890
- const autoId = typeof React11.useId === "function" ? React11.useId() : void 0;
891
- const [fallbackId] = React11.useState(
991
+ const autoId = typeof React14.useId === "function" ? React14.useId() : void 0;
992
+ const [fallbackId] = React14.useState(
892
993
  () => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
893
994
  );
894
995
  const inputId = inputIdProp || autoId || fallbackId;
895
- const inputRef = React11.useRef(null);
896
- const [hasValue, setHasValue] = React11.useState(false);
897
- const focusInput = React11.useCallback(() => {
996
+ const inputRef = React14.useRef(null);
997
+ const [hasValue, setHasValue] = React14.useState(false);
998
+ const focusInput = React14.useCallback(() => {
898
999
  const el = inputRef.current;
899
1000
  if (!el) return;
900
1001
  if (document.activeElement === el) return;
@@ -907,7 +1008,7 @@ function SearchPanelForm(props = {}) {
907
1008
  }
908
1009
  }
909
1010
  }, []);
910
- const handlePointerDown = React11.useCallback(
1011
+ const handlePointerDown = React14.useCallback(
911
1012
  (event) => {
912
1013
  const target = event.target;
913
1014
  if (target && typeof target.closest === "function") {
@@ -919,23 +1020,23 @@ function SearchPanelForm(props = {}) {
919
1020
  },
920
1021
  [focusInput]
921
1022
  );
922
- React11.useEffect(() => {
1023
+ React14.useEffect(() => {
923
1024
  const el = inputRef.current;
924
1025
  if (!el) return;
925
1026
  if (el.value && el.value.trim()) {
926
1027
  setHasValue(true);
927
1028
  }
928
1029
  }, []);
929
- const handleInputChange = React11.useCallback((event) => {
1030
+ const handleInputChange = React14.useCallback((event) => {
930
1031
  var _a;
931
1032
  const nextHasValue = Boolean(
932
1033
  ((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
933
1034
  );
934
1035
  setHasValue(nextHasValue);
935
1036
  }, []);
936
- const handleClear = React11.useCallback((event) => {
1037
+ const handleClear = React14.useCallback((event) => {
937
1038
  }, []);
938
- const handleClearKey = React11.useCallback(
1039
+ const handleClearKey = React14.useCallback(
939
1040
  (event) => {
940
1041
  if (event.key === "Enter" || event.key === " ") {
941
1042
  event.preventDefault();
@@ -944,7 +1045,7 @@ function SearchPanelForm(props = {}) {
944
1045
  },
945
1046
  [handleClear]
946
1047
  );
947
- return /* @__PURE__ */ React11.createElement(
1048
+ return /* @__PURE__ */ React14.createElement(
948
1049
  "form",
949
1050
  {
950
1051
  action,
@@ -956,7 +1057,7 @@ function SearchPanelForm(props = {}) {
956
1057
  onPointerDown: handlePointerDown,
957
1058
  "data-has-value": hasValue ? "1" : "0"
958
1059
  },
959
- /* @__PURE__ */ React11.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React11.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React11.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(
960
1061
  "input",
961
1062
  {
962
1063
  id: inputId,
@@ -972,7 +1073,7 @@ function SearchPanelForm(props = {}) {
972
1073
  onInput: handleInputChange
973
1074
  }
974
1075
  )),
975
- hasValue ? /* @__PURE__ */ React11.createElement(
1076
+ hasValue ? /* @__PURE__ */ React14.createElement(
976
1077
  "button",
977
1078
  {
978
1079
  type: "button",
@@ -985,32 +1086,31 @@ function SearchPanelForm(props = {}) {
985
1086
  },
986
1087
  "\xD7"
987
1088
  ) : null,
988
- /* @__PURE__ */ React11.createElement(
1089
+ /* @__PURE__ */ React14.createElement(
989
1090
  "button",
990
1091
  {
991
1092
  type: "submit",
992
1093
  "data-canopy-search-form-trigger": "submit",
993
1094
  className: "canopy-search-form__submit"
994
1095
  },
995
- /* @__PURE__ */ React11.createElement("span", null, text),
996
- /* @__PURE__ */ React11.createElement("span", { "aria-hidden": true, className: "canopy-search-form__shortcut" }, /* @__PURE__ */ React11.createElement("span", null, "\u2318"), /* @__PURE__ */ React11.createElement("span", null, "K"))
1096
+ text
997
1097
  )
998
1098
  );
999
1099
  }
1000
1100
 
1001
1101
  // ui/src/search/SearchPanelTeaserResults.jsx
1002
- import React12 from "react";
1102
+ import React15 from "react";
1003
1103
  function SearchPanelTeaserResults(props = {}) {
1004
1104
  const { style, className } = props || {};
1005
1105
  const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
1006
- return /* @__PURE__ */ React12.createElement(
1106
+ return /* @__PURE__ */ React15.createElement(
1007
1107
  "div",
1008
1108
  {
1009
1109
  "data-canopy-search-form-panel": true,
1010
1110
  className: classes || void 0,
1011
1111
  style
1012
1112
  },
1013
- /* @__PURE__ */ React12.createElement("div", { id: "cplist" })
1113
+ /* @__PURE__ */ React15.createElement("div", { id: "cplist" })
1014
1114
  );
1015
1115
  }
1016
1116
 
@@ -1031,11 +1131,11 @@ function SearchPanel(props = {}) {
1031
1131
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
1032
1132
  const resolvedSearchPath = resolveSearchPath(searchPath);
1033
1133
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
1034
- return /* @__PURE__ */ React13.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React13.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React13.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React13.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React13.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) } }));
1035
1135
  }
1036
1136
 
1037
1137
  // ui/src/layout/CanopyBrand.jsx
1038
- import React14 from "react";
1138
+ import React17 from "react";
1039
1139
  function CanopyBrand(props = {}) {
1040
1140
  const {
1041
1141
  labelId,
@@ -1046,11 +1146,11 @@ function CanopyBrand(props = {}) {
1046
1146
  } = props || {};
1047
1147
  const spanProps = labelId ? { id: labelId } : {};
1048
1148
  const classes = ["canopy-logo", className].filter(Boolean).join(" ");
1049
- return /* @__PURE__ */ React14.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React14.createElement(Logo, null) : null, /* @__PURE__ */ React14.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));
1050
1150
  }
1051
1151
 
1052
1152
  // ui/src/layout/CanopyModal.jsx
1053
- import React15 from "react";
1153
+ import React18 from "react";
1054
1154
  function CanopyModal(props = {}) {
1055
1155
  const {
1056
1156
  id,
@@ -1101,7 +1201,7 @@ function CanopyModal(props = {}) {
1101
1201
  if (padded) bodyClasses.push("canopy-modal__body--padded");
1102
1202
  if (bodyClassName) bodyClasses.push(bodyClassName);
1103
1203
  const bodyClassNameValue = bodyClasses.join(" ");
1104
- return /* @__PURE__ */ React15.createElement("div", { ...modalProps }, /* @__PURE__ */ React15.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React15.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React15.createElement(
1204
+ return /* @__PURE__ */ React18.createElement("div", { ...modalProps }, /* @__PURE__ */ React18.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React18.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React18.createElement(
1105
1205
  "svg",
1106
1206
  {
1107
1207
  xmlns: "http://www.w3.org/2000/svg",
@@ -1111,8 +1211,8 @@ function CanopyModal(props = {}) {
1111
1211
  strokeWidth: "1.5",
1112
1212
  className: "canopy-modal__close-icon"
1113
1213
  },
1114
- /* @__PURE__ */ React15.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
1115
- ), /* @__PURE__ */ React15.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React15.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React15.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React15.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(
1116
1216
  CanopyBrand,
1117
1217
  {
1118
1218
  labelId: resolvedLabelId,
@@ -1316,7 +1416,7 @@ function HeaderScript() {
1316
1416
  });
1317
1417
  })();
1318
1418
  `;
1319
- return /* @__PURE__ */ React16.createElement(
1419
+ return /* @__PURE__ */ React19.createElement(
1320
1420
  "script",
1321
1421
  {
1322
1422
  dangerouslySetInnerHTML: {
@@ -1327,7 +1427,9 @@ function HeaderScript() {
1327
1427
  }
1328
1428
  function ensureArray(navLinks) {
1329
1429
  if (!Array.isArray(navLinks)) return [];
1330
- 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
+ );
1331
1433
  }
1332
1434
  function CanopyHeader(props = {}) {
1333
1435
  const {
@@ -1340,59 +1442,98 @@ function CanopyHeader(props = {}) {
1340
1442
  logo: SiteLogo
1341
1443
  } = props;
1342
1444
  const navLinks = ensureArray(navLinksProp);
1343
- return /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("header", { className: "canopy-header", "data-mobile-nav": "closed", "data-mobile-search": "closed" }, /* @__PURE__ */ React16.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React16.createElement(
1344
- CanopyBrand,
1445
+ return /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement(
1446
+ "header",
1345
1447
  {
1346
- label: title,
1347
- href: brandHref,
1348
- className: "canopy-header__brand-link",
1349
- Logo: SiteLogo
1350
- }
1351
- )), /* @__PURE__ */ React16.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React16.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })), /* @__PURE__ */ React16.createElement("nav", { className: "canopy-nav-links canopy-header__desktop-nav", "aria-label": "Primary navigation" }, navLinks.map((link) => /* @__PURE__ */ React16.createElement("a", { key: link.href, href: link.href }, link.label || link.href))), /* @__PURE__ */ React16.createElement("div", { className: "canopy-header__actions" }, /* @__PURE__ */ React16.createElement(
1352
- "button",
1353
- {
1354
- type: "button",
1355
- className: "canopy-header__icon-button canopy-header__search-trigger",
1356
- "aria-label": "Open search",
1357
- "aria-controls": "canopy-modal-search",
1358
- "aria-expanded": "false",
1359
- "data-canopy-header-toggle": "search"
1448
+ className: "canopy-header",
1449
+ "data-mobile-nav": "closed",
1450
+ "data-mobile-search": "closed"
1360
1451
  },
1361
- /* @__PURE__ */ React16.createElement(
1362
- "svg",
1452
+ /* @__PURE__ */ React19.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React19.createElement(
1453
+ CanopyBrand,
1363
1454
  {
1364
- xmlns: "http://www.w3.org/2000/svg",
1365
- viewBox: "0 0 24 24",
1366
- fill: "none",
1367
- stroke: "currentColor",
1368
- strokeWidth: "1.5",
1369
- 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"
1370
1474
  },
1371
- /* @__PURE__ */ React16.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" })
1372
- )
1373
- ), /* @__PURE__ */ React16.createElement(
1374
- "button",
1375
- {
1376
- type: "button",
1377
- className: "canopy-header__icon-button canopy-header__menu",
1378
- "aria-label": "Open navigation",
1379
- "aria-controls": "canopy-modal-nav",
1380
- "aria-expanded": "false",
1381
- "data-canopy-header-toggle": "nav"
1382
- },
1383
- /* @__PURE__ */ React16.createElement(
1384
- "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",
1385
1479
  {
1386
- xmlns: "http://www.w3.org/2000/svg",
1387
- fill: "none",
1388
- viewBox: "0 0 24 24",
1389
- strokeWidth: "1.5",
1390
- stroke: "currentColor",
1391
- 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"
1392
1486
  },
1393
- /* @__PURE__ */ React16.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" })
1394
- )
1395
- ))), /* @__PURE__ */ React16.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(
1396
1537
  CanopyModal,
1397
1538
  {
1398
1539
  id: "canopy-modal-nav",
@@ -1404,8 +1545,15 @@ function CanopyHeader(props = {}) {
1404
1545
  closeLabel: "Close navigation",
1405
1546
  closeDataAttr: "nav"
1406
1547
  },
1407
- /* @__PURE__ */ React16.createElement("nav", { className: "canopy-nav-links canopy-modal__nav", "aria-label": "Primary navigation" }, navLinks.map((link) => /* @__PURE__ */ React16.createElement("a", { key: link.href, href: link.href }, link.label || link.href)))
1408
- ), /* @__PURE__ */ React16.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(
1409
1557
  CanopyModal,
1410
1558
  {
1411
1559
  id: "canopy-modal-search",
@@ -1418,19 +1566,33 @@ function CanopyHeader(props = {}) {
1418
1566
  closeDataAttr: "search",
1419
1567
  bodyClassName: "canopy-modal__body--search"
1420
1568
  },
1421
- /* @__PURE__ */ React16.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })
1422
- ), /* @__PURE__ */ React16.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));
1423
1578
  }
1424
1579
 
1425
1580
  // ui/src/layout/CanopyFooter.jsx
1426
- import React17 from "react";
1581
+ import React20 from "react";
1427
1582
  function CanopyFooter({ className = "", children }) {
1428
1583
  const footerClassName = ["canopy-footer", className].filter(Boolean).join(" ");
1429
- return /* @__PURE__ */ React17.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React17.createElement("div", { className: "canopy-footer__inner" }, children));
1584
+ return /* @__PURE__ */ React20.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React20.createElement("div", { className: "canopy-footer__inner" }, children));
1585
+ }
1586
+
1587
+ // ui/src/layout/Container.jsx
1588
+ import React21 from "react";
1589
+ function Container({ className = "", children, ...rest }) {
1590
+ const classes = ["mx-auto", "max-w-content", "w-full", "px-6", className].filter(Boolean).join(" ");
1591
+ return /* @__PURE__ */ React21.createElement("div", { className: classes, ...rest }, children);
1430
1592
  }
1431
1593
 
1432
1594
  // ui/src/search/MdxSearchResults.jsx
1433
- import React18 from "react";
1595
+ import React22 from "react";
1434
1596
  function MdxSearchResults(props) {
1435
1597
  let json = "{}";
1436
1598
  try {
@@ -1438,11 +1600,11 @@ function MdxSearchResults(props) {
1438
1600
  } catch (_) {
1439
1601
  json = "{}";
1440
1602
  }
1441
- return /* @__PURE__ */ React18.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React18.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 } }));
1442
1604
  }
1443
1605
 
1444
1606
  // ui/src/search/SearchSummary.jsx
1445
- import React19 from "react";
1607
+ import React23 from "react";
1446
1608
  function SearchSummary(props) {
1447
1609
  let json = "{}";
1448
1610
  try {
@@ -1450,11 +1612,11 @@ function SearchSummary(props) {
1450
1612
  } catch (_) {
1451
1613
  json = "{}";
1452
1614
  }
1453
- return /* @__PURE__ */ React19.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React19.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 } }));
1454
1616
  }
1455
1617
 
1456
1618
  // ui/src/search/MdxSearchTabs.jsx
1457
- import React20 from "react";
1619
+ import React24 from "react";
1458
1620
  function MdxSearchTabs(props) {
1459
1621
  let json = "{}";
1460
1622
  try {
@@ -1462,11 +1624,11 @@ function MdxSearchTabs(props) {
1462
1624
  } catch (_) {
1463
1625
  json = "{}";
1464
1626
  }
1465
- return /* @__PURE__ */ React20.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React20.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 } }));
1466
1628
  }
1467
1629
 
1468
1630
  // ui/src/search-form/MdxSearchFormModal.jsx
1469
- import React21 from "react";
1631
+ import React25 from "react";
1470
1632
  function MdxSearchFormModal(props = {}) {
1471
1633
  const {
1472
1634
  placeholder = "Search\u2026",
@@ -1482,11 +1644,11 @@ function MdxSearchFormModal(props = {}) {
1482
1644
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
1483
1645
  const resolvedSearchPath = resolveSearchPath(searchPath);
1484
1646
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
1485
- return /* @__PURE__ */ React21.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React21.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React21.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React21.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React21.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) } }));
1486
1648
  }
1487
1649
 
1488
1650
  // ui/src/iiif/ManifestPrimitives.jsx
1489
- import React22 from "react";
1651
+ import React26 from "react";
1490
1652
  import {
1491
1653
  Label as CloverLabel,
1492
1654
  Metadata as CloverMetadata,
@@ -1511,28 +1673,28 @@ function ensureMetadata(items) {
1511
1673
  function Label({ manifest, label, ...rest }) {
1512
1674
  const intl = label || manifest && manifest.label;
1513
1675
  if (!hasInternationalValue(intl)) return null;
1514
- return /* @__PURE__ */ React22.createElement(CloverLabel, { label: intl, ...rest });
1676
+ return /* @__PURE__ */ React26.createElement(CloverLabel, { label: intl, ...rest });
1515
1677
  }
1516
1678
  function Summary({ manifest, summary, ...rest }) {
1517
1679
  const intl = summary || manifest && manifest.summary;
1518
1680
  if (!hasInternationalValue(intl)) return null;
1519
- return /* @__PURE__ */ React22.createElement(CloverSummary, { summary: intl, ...rest });
1681
+ return /* @__PURE__ */ React26.createElement(CloverSummary, { summary: intl, ...rest });
1520
1682
  }
1521
1683
  function Metadata({ manifest, metadata, ...rest }) {
1522
1684
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
1523
1685
  if (!items.length) return null;
1524
- return /* @__PURE__ */ React22.createElement(CloverMetadata, { metadata: items, ...rest });
1686
+ return /* @__PURE__ */ React26.createElement(CloverMetadata, { metadata: items, ...rest });
1525
1687
  }
1526
1688
  function RequiredStatement({ manifest, requiredStatement, ...rest }) {
1527
1689
  const stmt = requiredStatement || manifest && manifest.requiredStatement;
1528
1690
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
1529
1691
  return null;
1530
1692
  }
1531
- return /* @__PURE__ */ React22.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
1693
+ return /* @__PURE__ */ React26.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
1532
1694
  }
1533
1695
 
1534
1696
  // ui/src/docs/CodeBlock.jsx
1535
- import React23 from "react";
1697
+ import React27 from "react";
1536
1698
  function parseHighlightAttr(attr) {
1537
1699
  if (!attr) return /* @__PURE__ */ new Set();
1538
1700
  const cleaned = String(attr || "").trim();
@@ -1578,10 +1740,10 @@ var highlightBaseStyle = {
1578
1740
  };
1579
1741
  function DocsCodeBlock(props = {}) {
1580
1742
  const { children, ...rest } = props;
1581
- const childArray = React23.Children.toArray(children);
1582
- const codeElement = childArray.find((el) => React23.isValidElement(el));
1743
+ const childArray = React27.Children.toArray(children);
1744
+ const codeElement = childArray.find((el) => React27.isValidElement(el));
1583
1745
  if (!codeElement || !codeElement.props) {
1584
- return React23.createElement("pre", props);
1746
+ return React27.createElement("pre", props);
1585
1747
  }
1586
1748
  const {
1587
1749
  className = "",
@@ -1596,8 +1758,8 @@ function DocsCodeBlock(props = {}) {
1596
1758
  const highlightSet = parseHighlightAttr(highlightAttr);
1597
1759
  const copyAttr = codeProps["data-copy"];
1598
1760
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
1599
- const [copied, setCopied] = React23.useState(false);
1600
- const handleCopy = React23.useCallback(async () => {
1761
+ const [copied, setCopied] = React27.useState(false);
1762
+ const handleCopy = React27.useCallback(async () => {
1601
1763
  const text = rawCode;
1602
1764
  try {
1603
1765
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -1623,7 +1785,7 @@ function DocsCodeBlock(props = {}) {
1623
1785
  borderRadius: "12px",
1624
1786
  overflow: "hidden",
1625
1787
  margin: "1.5rem 0",
1626
- background: "var(--color-brand-100, #e0f2ff)",
1788
+ background: "var(--color-brand-100)",
1627
1789
  fontFamily: "var(--font-mono)",
1628
1790
  fontSize: "0.85rem"
1629
1791
  };
@@ -1633,13 +1795,14 @@ function DocsCodeBlock(props = {}) {
1633
1795
  justifyContent: "space-between",
1634
1796
  padding: "1rem 1.25rem",
1635
1797
  fontWeight: 700,
1636
- background: "var(--color-brand-200, #dbeafe)",
1798
+ background: "var(--color-brand-100)",
1799
+ borderBottom: "1px solid var(--color-brand-200)",
1637
1800
  color: "var(--color-brand-900)"
1638
1801
  };
1639
1802
  const preStyle = {
1640
1803
  margin: 0,
1641
- background: "var(--color-brand-100, #e0f2ff)",
1642
- color: "var(--color-brand-800, #1f2d5c)",
1804
+ background: "var(--color-brand-100)",
1805
+ color: "var(--color-brand-800)",
1643
1806
  lineHeight: 1.55,
1644
1807
  padding: "1rem 0",
1645
1808
  overflowX: "auto"
@@ -1660,20 +1823,20 @@ function DocsCodeBlock(props = {}) {
1660
1823
  const highlight = highlightSet.has(lineNumber);
1661
1824
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
1662
1825
  const displayLine = line === "" ? " " : line;
1663
- return React23.createElement(
1826
+ return React27.createElement(
1664
1827
  "span",
1665
1828
  { key: lineNumber, style },
1666
- React23.createElement("span", { style: lineContentStyle }, displayLine)
1829
+ React27.createElement("span", { style: lineContentStyle }, displayLine)
1667
1830
  );
1668
1831
  });
1669
- return React23.createElement(
1832
+ return React27.createElement(
1670
1833
  "div",
1671
1834
  { style: containerStyle },
1672
- React23.createElement(
1835
+ React27.createElement(
1673
1836
  "div",
1674
1837
  { style: headerStyle },
1675
- React23.createElement("span", null, showFilename ? filename : null),
1676
- enableCopy ? React23.createElement(
1838
+ React27.createElement("span", null, showFilename ? filename : null),
1839
+ enableCopy ? React27.createElement(
1677
1840
  "button",
1678
1841
  {
1679
1842
  type: "button",
@@ -1692,20 +1855,32 @@ function DocsCodeBlock(props = {}) {
1692
1855
  copied ? "Copied" : "Copy"
1693
1856
  ) : null
1694
1857
  ),
1695
- React23.createElement(
1858
+ React27.createElement(
1696
1859
  "pre",
1697
1860
  { ...preRest, className: preClassName, style: mergedPreStyle },
1698
- React23.createElement("code", { style: codeStyle }, lineElements)
1861
+ React27.createElement("code", { style: codeStyle }, lineElements)
1699
1862
  )
1700
1863
  );
1701
1864
  }
1865
+
1866
+ // ui/src/docs/MarkdownTable.jsx
1867
+ import React28 from "react";
1868
+ function MarkdownTable({ className = "", ...rest }) {
1869
+ const merged = ["markdown-table", className].filter(Boolean).join(" ");
1870
+ return /* @__PURE__ */ React28.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React28.createElement("table", { className: merged, ...rest }));
1871
+ }
1702
1872
  export {
1873
+ Button,
1874
+ ButtonWrapper,
1703
1875
  CanopyBrand,
1704
1876
  CanopyFooter,
1705
1877
  CanopyHeader,
1706
1878
  CanopyModal,
1879
+ Container,
1707
1880
  DocsCodeBlock,
1881
+ MarkdownTable as DocsMarkdownTable,
1708
1882
  HelloWorld,
1883
+ Image,
1709
1884
  interstitials_exports as Interstitials,
1710
1885
  Label,
1711
1886
  Layout,