@canopy-iiif/app 1.9.7 → 1.9.9

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.
package/lib/build/mdx.js CHANGED
@@ -117,21 +117,6 @@ function parseFrontmatter(src) {
117
117
  return {data, content};
118
118
  }
119
119
 
120
- function isRoadmapEntry(frontmatterData) {
121
- if (!frontmatterData || typeof frontmatterData !== "object") return false;
122
- if (!Object.prototype.hasOwnProperty.call(frontmatterData, "roadmap")) return false;
123
- const raw = frontmatterData.roadmap;
124
- if (typeof raw === "boolean") return raw;
125
- if (typeof raw === "number") return raw !== 0;
126
- if (typeof raw === "string") {
127
- const normalized = raw.trim().toLowerCase();
128
- if (!normalized) return false;
129
- if (["false", "0", "no", "off", "none"].includes(normalized)) return false;
130
- return true;
131
- }
132
- return !!raw;
133
- }
134
-
135
120
  // ESM-only in v3; load dynamically from CJS
136
121
  let MDXProviderCached = null;
137
122
  async function getMdxProvider() {
@@ -1958,7 +1943,6 @@ module.exports = {
1958
1943
  extractMarkdownSummary,
1959
1944
  isReservedFile,
1960
1945
  parseFrontmatter,
1961
- isRoadmapEntry,
1962
1946
  compileMdxFile,
1963
1947
  compileMdxToComponent,
1964
1948
  loadCustomLayout,
@@ -388,16 +388,6 @@ async function processContentEntry(absPath, pagesMetadata = []) {
388
388
  ? mdx.parseFrontmatter(source)
389
389
  : { data: null };
390
390
  const frontmatterData = frontmatter && isPlainObject(frontmatter.data) ? frontmatter.data : null;
391
- const isRoadmap = frontmatterData && typeof mdx.isRoadmapEntry === 'function'
392
- ? mdx.isRoadmapEntry(frontmatterData)
393
- : false;
394
- if (isRoadmap) {
395
- try { await fsp.rm(outPath, { force: true }); } catch (_) {}
396
- try {
397
- log(`• Skipped roadmap page ${path.relative(process.cwd(), absPath)}\n`, 'yellow', { dim: true });
398
- } catch (_) {}
399
- return;
400
- }
401
391
  ensureDirSync(path.dirname(outPath));
402
392
  try { log(`• Processing MDX ${absPath}\n`, 'blue'); } catch (_) {}
403
393
  const base = path.basename(absPath);
@@ -100,9 +100,6 @@ function collectPagesSync() {
100
100
  frontmatterData = null;
101
101
  }
102
102
  }
103
- const isRoadmap = frontmatterData && typeof mdx.isRoadmapEntry === "function"
104
- ? mdx.isRoadmapEntry(frontmatterData)
105
- : false;
106
103
  const {
107
104
  slug,
108
105
  segments: slugSegments,
@@ -129,7 +126,6 @@ function collectPagesSync() {
129
126
  fallbackTitle,
130
127
  sortKey: pageSortKey(normalizedRel),
131
128
  topSegment: slugSegments[0] || firstSegment || "",
132
- isRoadmap,
133
129
  };
134
130
  pages.push(page);
135
131
  }
@@ -154,7 +150,6 @@ function createNode(slug) {
154
150
  sortKey: slug || name,
155
151
  sourcePage: null,
156
152
  children: [],
157
- isRoadmap: false,
158
153
  };
159
154
  }
160
155
 
@@ -190,7 +185,6 @@ function getNavigationCache() {
190
185
  node.relativePath = page.relativePath;
191
186
  node.sortKey = page.sortKey || node.sortKey;
192
187
  node.hasContent = true;
193
- node.isRoadmap = !!page.isRoadmap;
194
188
  }
195
189
  }
196
190
 
@@ -258,7 +252,6 @@ function cloneNode(node, currentSlug) {
258
252
  hasContent: node.hasContent,
259
253
  relativePath: node.relativePath,
260
254
  children,
261
- isRoadmap: !!node.isRoadmap,
262
255
  };
263
256
  }
264
257
 
@@ -0,0 +1,80 @@
1
+ /**
2
+ * This file is auto-generated from content/locale.yml.
3
+ * Run `node packages/helpers/locales/sync-default-locale.js` after updating locale copy.
4
+ */
5
+ module.exports = {
6
+ common: {
7
+ actions: {
8
+ open: 'Open',
9
+ close: 'Close',
10
+ clear: 'Clear',
11
+ clear_all: 'Clear all',
12
+ done: 'Done',
13
+ show: 'Show',
14
+ hide: 'Hide'
15
+ },
16
+ nouns: {
17
+ search: 'Search',
18
+ filters: 'Filters',
19
+ results: 'results',
20
+ values: 'values',
21
+ types: 'types',
22
+ items: 'items',
23
+ navigation: 'navigation',
24
+ section_navigation: 'section navigation',
25
+ content_navigation: 'content navigation',
26
+ gallery: 'gallery',
27
+ gallery_thumbnails: 'gallery thumbnails',
28
+ map: 'map',
29
+ map_data: 'map data',
30
+ map_locations: 'map locations',
31
+ item_label: 'Item',
32
+ details: 'details',
33
+ breadcrumb: 'Breadcrumb',
34
+ home: 'Home'
35
+ },
36
+ types: {
37
+ work: 'Works',
38
+ page: 'Pages',
39
+ docs: 'Docs'
40
+ },
41
+ statuses: {
42
+ loading: 'Loading…',
43
+ empty_short: 'No {content}',
44
+ empty_detail: 'No {content} available.',
45
+ unavailable: '{content} unavailable.',
46
+ unavailable_detail: '{content} is unavailable.',
47
+ failed: 'Unable to load {content}.',
48
+ loading_content: 'Loading {content}…',
49
+ summary_content: 'Showing {shown} of {total} {content}',
50
+ search_summary: 'Found {shown} of {total} in {type} for "{query}"',
51
+ no_matches: 'No matches found.'
52
+ },
53
+ phrases: {
54
+ placeholder_search: 'Search…',
55
+ results_label: 'Search results',
56
+ open_content: 'Open {content}',
57
+ close_content: 'Close {content}',
58
+ show_content: 'Show {content}',
59
+ hide_content: 'Hide {content}',
60
+ nav_label: '{content} navigation',
61
+ search_content: 'Search {content}',
62
+ filter_values: 'Filter {content} values',
63
+ clear_content_search: 'Clear {content} search',
64
+ none_applied: 'No {content} applied',
65
+ scroll_direction_content: 'Scroll {direction} through {content}',
66
+ step_content: '{direction} {content}',
67
+ item_numbered: '{content} {index}',
68
+ content_key: '{content} key'
69
+ },
70
+ directions: {
71
+ left: 'left',
72
+ right: 'right',
73
+ previous: 'Previous',
74
+ next: 'Next'
75
+ },
76
+ misc: {
77
+ referenced_by: 'Referenced by'
78
+ }
79
+ }
80
+ };
package/lib/locales.js CHANGED
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const yaml = require('js-yaml');
4
4
  const {resolveCanopyConfigPath} = require('./config-path');
5
+ const DEFAULT_LOCALE_MESSAGES = require('./default-locale');
5
6
 
6
7
  const CONTENT_DIR = path.resolve('content');
7
8
 
@@ -102,7 +103,8 @@ function deepMerge(base, override) {
102
103
 
103
104
  function readLocaleMessages(lang) {
104
105
  const basePath = path.join(CONTENT_DIR, 'locale.yml');
105
- const defaultMessages = readYamlFile(basePath) || {};
106
+ const userMessages = readYamlFile(basePath);
107
+ const defaultMessages = deepMerge(DEFAULT_LOCALE_MESSAGES, userMessages) || {};
106
108
  if (!lang) return defaultMessages;
107
109
  const langPath = path.join(CONTENT_DIR, lang, 'locale.yml');
108
110
  const override = readYamlFile(langPath);
@@ -6,6 +6,38 @@ import {
6
6
  SearchFiltersDialog,
7
7
  } from "@canopy-iiif/app/ui";
8
8
  import resultTemplates from "__CANOPY_SEARCH_RESULT_TEMPLATES__";
9
+ const SITE_TITLE_FALLBACK = "Site title";
10
+
11
+ function readSiteTitleFromGlobals() {
12
+ try {
13
+ if (
14
+ typeof window !== "undefined" &&
15
+ typeof window.CANOPY_SITE_TITLE === "string"
16
+ ) {
17
+ const raw = window.CANOPY_SITE_TITLE.trim();
18
+ if (raw) return raw;
19
+ }
20
+ } catch (_) {}
21
+ try {
22
+ if (
23
+ typeof globalThis !== "undefined" &&
24
+ typeof globalThis.CANOPY_SITE_TITLE === "string"
25
+ ) {
26
+ const raw = globalThis.CANOPY_SITE_TITLE.trim();
27
+ if (raw) return raw;
28
+ }
29
+ } catch (_) {}
30
+ try {
31
+ if (typeof document !== "undefined" && document.title) {
32
+ const trimmed = document.title.trim();
33
+ if (!trimmed) return "";
34
+ const parts = trimmed.split("|").map((part) => part.trim()).filter(Boolean);
35
+ if (parts.length > 1) return parts[parts.length - 1];
36
+ return trimmed;
37
+ }
38
+ } catch (_) {}
39
+ return "";
40
+ }
9
41
 
10
42
  function readBasePath() {
11
43
  const normalize = (val) => {
@@ -60,6 +92,11 @@ function apiUrl(pathname) {
60
92
  return `${base}/${cleaned}`;
61
93
  }
62
94
 
95
+ function resolveSiteTitle() {
96
+ const title = readSiteTitleFromGlobals();
97
+ return title || SITE_TITLE_FALLBACK;
98
+ }
99
+
63
100
  function readDocumentLocale() {
64
101
  try {
65
102
  if (
@@ -909,6 +946,8 @@ function TabsMount() {
909
946
  const handleToggle = (facetSlug, valueSlug, checked) => {
910
947
  if (toggleFilter) toggleFilter(facetSlug, valueSlug, checked);
911
948
  };
949
+ const brandLabel = useMemo(() => resolveSiteTitle(), []);
950
+ const brandHref = useMemo(() => withBasePath("/"), []);
912
951
  return (
913
952
  <>
914
953
  <SearchTabsUI
@@ -928,6 +967,8 @@ function TabsMount() {
928
967
  selected={filters}
929
968
  onToggle={handleToggle}
930
969
  onClear={() => clearFilters && clearFilters()}
970
+ brandLabel={brandLabel}
971
+ brandHref={brandHref}
931
972
  />
932
973
  ) : null}
933
974
  </>
@@ -16,6 +16,7 @@ const {
16
16
  getLocaleRouteEntries,
17
17
  getDefaultRoute,
18
18
  getDefaultLocaleCode,
19
+ getSiteTitle,
19
20
  } = require('../common');
20
21
  const { resolveCanopyConfigPath } = require('../config-path');
21
22
 
@@ -310,6 +311,7 @@ async function buildSearchPageForEntry(routeEntry) {
310
311
  typeof searchPageMeta.description === 'string'
311
312
  ? searchPageMeta.description
312
313
  : '';
314
+ const siteTitle = typeof getSiteTitle === 'function' ? getSiteTitle() : '';
313
315
  const pageDetails = {
314
316
  title: pageTitle,
315
317
  description: pageDescription,
@@ -366,6 +368,10 @@ async function buildSearchPageForEntry(routeEntry) {
366
368
  }
367
369
  }
368
370
  let headExtra = vendorTags + head + importMap + customRuntimeTag;
371
+ if (siteTitle && typeof siteTitle === 'string') {
372
+ const siteTitleScript = `<script>window.CANOPY_SITE_TITLE=${JSON.stringify(siteTitle)}</script>`;
373
+ headExtra = siteTitleScript + headExtra;
374
+ }
369
375
  try {
370
376
  const { BASE_PATH } = require('../common');
371
377
  if (BASE_PATH) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.9.7",
3
+ "version": "1.9.9",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
package/ui/dist/index.mjs CHANGED
@@ -43186,10 +43186,11 @@ function Button({
43186
43186
  variant = "primary",
43187
43187
  className = "",
43188
43188
  children,
43189
+ as: Element = "a",
43189
43190
  ...rest
43190
43191
  }) {
43191
43192
  const resolvedVariant = VARIANTS.has(variant) ? variant : "primary";
43192
- const computedRel = target === "_blank" && !rel ? "noopener noreferrer" : rel;
43193
+ const computedRel = Element === "a" && target === "_blank" && !rel ? "noopener noreferrer" : rel;
43193
43194
  const content = children != null ? children : label;
43194
43195
  if (!content) return null;
43195
43196
  const classes = [
@@ -43197,17 +43198,19 @@ function Button({
43197
43198
  `canopy-button--${resolvedVariant}`,
43198
43199
  className
43199
43200
  ].filter(Boolean).join(" ");
43200
- return /* @__PURE__ */ React6.createElement(
43201
- "a",
43202
- {
43203
- href,
43204
- className: classes,
43205
- target,
43206
- rel: computedRel,
43207
- ...rest
43208
- },
43209
- content
43210
- );
43201
+ const elementProps = {
43202
+ className: classes,
43203
+ ...rest
43204
+ };
43205
+ if (Element === "a") {
43206
+ elementProps.href = href;
43207
+ if (target) elementProps.target = target;
43208
+ if (computedRel) elementProps.rel = computedRel;
43209
+ } else {
43210
+ if (typeof target !== "undefined") elementProps.target = target;
43211
+ if (computedRel) elementProps.rel = computedRel;
43212
+ }
43213
+ return /* @__PURE__ */ React6.createElement(Element, { ...elementProps }, content);
43211
43214
  }
43212
43215
 
43213
43216
  // ui/src/layout/ButtonWrapper.jsx
@@ -43449,22 +43452,40 @@ function SearchPanel(props = {}) {
43449
43452
  }
43450
43453
 
43451
43454
  // ui/src/layout/CanopyBrand.jsx
43455
+ import React13 from "react";
43456
+
43457
+ // ui/src/layout/pageContext.js
43452
43458
  import React12 from "react";
43459
+ var CONTEXT_KEY = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
43460
+ function getSharedRoot() {
43461
+ if (typeof globalThis !== "undefined") return globalThis;
43462
+ if (typeof window !== "undefined") return window;
43463
+ if (typeof global !== "undefined") return global;
43464
+ return null;
43465
+ }
43466
+ function getSafePageContext() {
43467
+ const root2 = getSharedRoot();
43468
+ if (root2 && root2[CONTEXT_KEY]) return root2[CONTEXT_KEY];
43469
+ const ctx = React12.createContext({ navigation: null, page: null, site: null });
43470
+ if (root2) root2[CONTEXT_KEY] = ctx;
43471
+ return ctx;
43472
+ }
43473
+
43474
+ // ui/src/layout/CanopyBrand.jsx
43475
+ var PageContext = getSafePageContext();
43453
43476
  function CanopyBrand(props = {}) {
43454
- const {
43455
- labelId,
43456
- label = "Canopy IIIF",
43457
- href = "/",
43458
- className,
43459
- Logo
43460
- } = props || {};
43477
+ const { labelId, label: labelProp, href = "/", className, Logo } = props || {};
43478
+ const context = React13.useContext(PageContext);
43479
+ const contextSiteTitle = context && context.site && typeof context.site.title === "string" ? context.site.title.trim() : "";
43480
+ const normalizedLabel = typeof labelProp === "string" && labelProp.trim() ? labelProp : "";
43481
+ const resolvedLabel = normalizedLabel || contextSiteTitle || "Site title";
43461
43482
  const spanProps = labelId ? { id: labelId } : {};
43462
43483
  const classes = ["canopy-logo", className].filter(Boolean).join(" ");
43463
- return /* @__PURE__ */ React12.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React12.createElement(Logo, null) : null, /* @__PURE__ */ React12.createElement("span", { ...spanProps }, label));
43484
+ return /* @__PURE__ */ React13.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React13.createElement(Logo, null) : null, /* @__PURE__ */ React13.createElement("span", { ...spanProps }, resolvedLabel));
43464
43485
  }
43465
43486
 
43466
43487
  // ui/src/layout/CanopyModal.jsx
43467
- import React13 from "react";
43488
+ import React14 from "react";
43468
43489
  function CanopyModal(props = {}) {
43469
43490
  const {
43470
43491
  id,
@@ -43515,7 +43536,7 @@ function CanopyModal(props = {}) {
43515
43536
  if (padded) bodyClasses.push("canopy-modal__body--padded");
43516
43537
  if (bodyClassName) bodyClasses.push(bodyClassName);
43517
43538
  const bodyClassNameValue = bodyClasses.join(" ");
43518
- return /* @__PURE__ */ React13.createElement("div", { ...modalProps }, /* @__PURE__ */ React13.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React13.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React13.createElement(
43539
+ return /* @__PURE__ */ React14.createElement("div", { ...modalProps }, /* @__PURE__ */ React14.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React14.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React14.createElement(
43519
43540
  "svg",
43520
43541
  {
43521
43542
  xmlns: "http://www.w3.org/2000/svg",
@@ -43525,8 +43546,8 @@ function CanopyModal(props = {}) {
43525
43546
  strokeWidth: "1.5",
43526
43547
  className: "canopy-modal__close-icon"
43527
43548
  },
43528
- /* @__PURE__ */ React13.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
43529
- ), /* @__PURE__ */ React13.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React13.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React13.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React13.createElement(
43549
+ /* @__PURE__ */ React14.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
43550
+ ), /* @__PURE__ */ React14.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React14.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React14.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React14.createElement(
43530
43551
  CanopyBrand,
43531
43552
  {
43532
43553
  labelId: resolvedLabelId,
@@ -43539,7 +43560,7 @@ function CanopyModal(props = {}) {
43539
43560
  }
43540
43561
 
43541
43562
  // ui/src/layout/NavigationTree.jsx
43542
- import React14 from "react";
43563
+ import React15 from "react";
43543
43564
  function normalizeDepth(depth) {
43544
43565
  if (typeof depth !== "number") return 0;
43545
43566
  return Math.max(0, Math.min(5, depth));
@@ -43548,7 +43569,7 @@ function NavigationTreeList({ nodes, depth, parentKey }) {
43548
43569
  if (!Array.isArray(nodes) || !nodes.length) return null;
43549
43570
  const listClasses = ["canopy-nav-tree__list"];
43550
43571
  if (depth > 0) listClasses.push("canopy-nav-tree__list--nested");
43551
- return /* @__PURE__ */ React14.createElement("ul", { className: listClasses.join(" "), role: "list" }, nodes.map((node, index) => /* @__PURE__ */ React14.createElement(
43572
+ return /* @__PURE__ */ React15.createElement("ul", { className: listClasses.join(" "), role: "list" }, nodes.map((node, index) => /* @__PURE__ */ React15.createElement(
43552
43573
  NavigationTreeItem,
43553
43574
  {
43554
43575
  key: node.slug || node.href || node.title || `${parentKey}-${index}`,
@@ -43561,20 +43582,18 @@ function NavigationTreeList({ nodes, depth, parentKey }) {
43561
43582
  function NavigationTreeItem({ node, depth, nodeKey }) {
43562
43583
  if (!node) return null;
43563
43584
  const hasChildren = Array.isArray(node.children) && node.children.length > 0;
43564
- const isRoadmap = !!node.isRoadmap;
43565
- const isInteractive = !!(node.href && !isRoadmap);
43585
+ const isInteractive = !!node.href;
43566
43586
  const Tag = isInteractive ? "a" : "span";
43567
43587
  const depthClass = `depth-${normalizeDepth(depth + 1)}`;
43568
43588
  const classes = ["canopy-nav-tree__link", depthClass];
43569
- if (!isInteractive && !isRoadmap) classes.push("is-label");
43570
- if (isRoadmap) classes.push("is-disabled");
43589
+ if (!isInteractive) classes.push("is-label");
43571
43590
  if (node.isActive) classes.push("is-active");
43572
43591
  const isRootLevel = depth < 0;
43573
43592
  const panelId = hasChildren ? `canopy-section-${nodeKey}` : null;
43574
43593
  const allowToggle = hasChildren && !isRootLevel;
43575
43594
  const defaultExpanded = allowToggle ? !!node.isExpanded : true;
43576
43595
  const toggleLabel = node.title ? `Toggle ${node.title} menu` : "Toggle section menu";
43577
- return /* @__PURE__ */ React14.createElement(
43596
+ return /* @__PURE__ */ React15.createElement(
43578
43597
  "li",
43579
43598
  {
43580
43599
  className: "canopy-nav-tree__item",
@@ -43583,7 +43602,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
43583
43602
  "data-expanded": allowToggle ? defaultExpanded ? "true" : "false" : void 0,
43584
43603
  "data-default-expanded": allowToggle && defaultExpanded ? "true" : void 0
43585
43604
  },
43586
- /* @__PURE__ */ React14.createElement("div", { className: "canopy-nav-tree__row" }, /* @__PURE__ */ React14.createElement("div", { className: "canopy-nav-tree__link-wrapper" }, /* @__PURE__ */ React14.createElement(
43605
+ /* @__PURE__ */ React15.createElement("div", { className: "canopy-nav-tree__row" }, /* @__PURE__ */ React15.createElement("div", { className: "canopy-nav-tree__link-wrapper" }, /* @__PURE__ */ React15.createElement(
43587
43606
  Tag,
43588
43607
  {
43589
43608
  className: classes.join(" "),
@@ -43591,9 +43610,8 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
43591
43610
  "aria-current": node.isActive ? "page" : void 0,
43592
43611
  tabIndex: isInteractive ? void 0 : -1
43593
43612
  },
43594
- node.title || node.slug,
43595
- isRoadmap ? /* @__PURE__ */ React14.createElement("span", { className: "canopy-nav-tree__badge" }, "Roadmap") : null
43596
- )), allowToggle ? /* @__PURE__ */ React14.createElement(
43613
+ node.title || node.slug
43614
+ )), allowToggle ? /* @__PURE__ */ React15.createElement(
43597
43615
  "button",
43598
43616
  {
43599
43617
  type: "button",
@@ -43603,7 +43621,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
43603
43621
  "aria-label": toggleLabel,
43604
43622
  "data-canopy-nav-item-toggle": panelId || void 0
43605
43623
  },
43606
- /* @__PURE__ */ React14.createElement(
43624
+ /* @__PURE__ */ React15.createElement(
43607
43625
  "svg",
43608
43626
  {
43609
43627
  xmlns: "http://www.w3.org/2000/svg",
@@ -43613,7 +43631,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
43613
43631
  strokeWidth: "1.5",
43614
43632
  className: "canopy-nav-tree__toggle-icon"
43615
43633
  },
43616
- /* @__PURE__ */ React14.createElement(
43634
+ /* @__PURE__ */ React15.createElement(
43617
43635
  "path",
43618
43636
  {
43619
43637
  strokeLinecap: "round",
@@ -43622,9 +43640,9 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
43622
43640
  }
43623
43641
  )
43624
43642
  ),
43625
- /* @__PURE__ */ React14.createElement("span", { className: "sr-only" }, toggleLabel)
43643
+ /* @__PURE__ */ React15.createElement("span", { className: "sr-only" }, toggleLabel)
43626
43644
  ) : null),
43627
- hasChildren ? /* @__PURE__ */ React14.createElement(
43645
+ hasChildren ? /* @__PURE__ */ React15.createElement(
43628
43646
  "div",
43629
43647
  {
43630
43648
  id: panelId || void 0,
@@ -43632,7 +43650,7 @@ function NavigationTreeItem({ node, depth, nodeKey }) {
43632
43650
  "aria-hidden": allowToggle ? defaultExpanded ? "false" : "true" : "false",
43633
43651
  hidden: allowToggle ? !defaultExpanded : void 0
43634
43652
  },
43635
- /* @__PURE__ */ React14.createElement(
43653
+ /* @__PURE__ */ React15.createElement(
43636
43654
  NavigationTreeList,
43637
43655
  {
43638
43656
  nodes: node.children,
@@ -43657,15 +43675,15 @@ function NavigationTree({
43657
43675
  const nodes = includeRoot ? [root2] : root2.children;
43658
43676
  if (!Array.isArray(nodes) || !nodes.length) return null;
43659
43677
  const combinedClassName = ["canopy-nav-tree", className].filter(Boolean).join(" ");
43660
- return /* @__PURE__ */ React14.createElement(
43678
+ return /* @__PURE__ */ React15.createElement(
43661
43679
  Component,
43662
43680
  {
43663
43681
  className: combinedClassName,
43664
43682
  "data-canopy-nav-tree": "true",
43665
43683
  ...rest
43666
43684
  },
43667
- heading ? /* @__PURE__ */ React14.createElement("div", { className: headingClassName }, heading) : null,
43668
- /* @__PURE__ */ React14.createElement(
43685
+ heading ? /* @__PURE__ */ React15.createElement("div", { className: headingClassName }, heading) : null,
43686
+ /* @__PURE__ */ React15.createElement(
43669
43687
  NavigationTreeList,
43670
43688
  {
43671
43689
  nodes,
@@ -43676,23 +43694,6 @@ function NavigationTree({
43676
43694
  );
43677
43695
  }
43678
43696
 
43679
- // ui/src/layout/pageContext.js
43680
- import React15 from "react";
43681
- var CONTEXT_KEY = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
43682
- function getSharedRoot() {
43683
- if (typeof globalThis !== "undefined") return globalThis;
43684
- if (typeof window !== "undefined") return window;
43685
- if (typeof global !== "undefined") return global;
43686
- return null;
43687
- }
43688
- function getSafePageContext() {
43689
- const root2 = getSharedRoot();
43690
- if (root2 && root2[CONTEXT_KEY]) return root2[CONTEXT_KEY];
43691
- const ctx = React15.createContext({ navigation: null, page: null, site: null });
43692
- if (root2) root2[CONTEXT_KEY] = ctx;
43693
- return ctx;
43694
- }
43695
-
43696
43697
  // ui/src/layout/LanguageToggle.jsx
43697
43698
  import React17 from "react";
43698
43699
 
@@ -44005,8 +44006,8 @@ function LanguageToggle({
44005
44006
  label,
44006
44007
  ariaLabel
44007
44008
  }) {
44008
- const PageContext = getSafePageContext();
44009
- const context = React17.useContext(PageContext);
44009
+ const PageContext3 = getSafePageContext();
44010
+ const context = React17.useContext(PageContext3);
44010
44011
  const siteLanguageToggle = context && context.site && context.site.languageToggle || null;
44011
44012
  const pageData = page || (context && context.page ? context.page : null);
44012
44013
  const resolvedToggle = languageToggle === false ? null : languageToggle === true || typeof languageToggle === "undefined" ? siteLanguageToggle : languageToggle;
@@ -44404,8 +44405,8 @@ function CanopyHeader(props = {}) {
44404
44405
  logo: SiteLogo,
44405
44406
  languageToggle: languageToggleProp
44406
44407
  } = props;
44407
- const PageContext = getSafePageContext();
44408
- const context = React18.useContext(PageContext);
44408
+ const PageContext3 = getSafePageContext();
44409
+ const context = React18.useContext(PageContext3);
44409
44410
  const contextPrimaryNav = context && Array.isArray(context.primaryNavigation) ? context.primaryNavigation : [];
44410
44411
  const navLinks = navLinksProp && navLinksProp.length ? ensureArray(navLinksProp) : ensureArray(contextPrimaryNav);
44411
44412
  const contextNavigation = context && context.navigation ? context.navigation : null;
@@ -45534,7 +45535,7 @@ function SearchTabs({
45534
45535
  const orderedTypes = Array.isArray(types) ? types : [];
45535
45536
  const toLabel = (t) => t && t.length ? t.charAt(0).toUpperCase() + t.slice(1) : "";
45536
45537
  const hasFilters = typeof onOpenFilters === "function";
45537
- const filterBadge = activeFilterCount > 0 ? ` (${activeFilterCount})` : "";
45538
+ const filterBadge = activeFilterCount > 0 ? /* @__PURE__ */ React34.createElement("span", { className: "canopy-search-tabs__filters-count" }, "(", activeFilterCount, ")") : null;
45538
45539
  const [itemBoundingBox, setItemBoundingBox] = useState6(null);
45539
45540
  const [wrapperBoundingBox, setWrapperBoundingBox] = useState6(null);
45540
45541
  const [highlightedTab, setHighlightedTab] = useState6(null);
@@ -45614,19 +45615,23 @@ function SearchTabs({
45614
45615
  );
45615
45616
  })
45616
45617
  ), hasFilters ? /* @__PURE__ */ React34.createElement(
45617
- "button",
45618
+ Button,
45618
45619
  {
45620
+ as: "button",
45619
45621
  type: "button",
45622
+ variant: "primary",
45620
45623
  onClick: () => onOpenFilters && onOpenFilters(),
45621
45624
  "aria-expanded": filtersOpen ? "true" : "false",
45622
- className: "inline-flex items-center gap-2 rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:border-brand-200 hover:bg-brand-50 hover:text-brand-700"
45625
+ className: "canopy-search-tabs__filters-button"
45623
45626
  },
45624
- /* @__PURE__ */ React34.createElement("span", null, filtersLabel, filterBadge)
45627
+ /* @__PURE__ */ React34.createElement("span", null, filtersLabel),
45628
+ filterBadge
45625
45629
  ) : null);
45626
45630
  }
45627
45631
 
45628
45632
  // ui/src/search/SearchFiltersDialog.jsx
45629
45633
  import React35 from "react";
45634
+ var PageContext2 = getSafePageContext();
45630
45635
  function toArray(input) {
45631
45636
  if (!input) return [];
45632
45637
  if (Array.isArray(input)) return input;
@@ -45734,7 +45739,7 @@ function SearchFiltersDialog(props = {}) {
45734
45739
  onClear,
45735
45740
  title,
45736
45741
  subtitle = "Refine results by metadata",
45737
- brandLabel = "Canopy IIIF",
45742
+ brandLabel: brandLabelProp,
45738
45743
  brandHref = "/",
45739
45744
  logo: SiteLogo
45740
45745
  } = props;
@@ -45757,6 +45762,9 @@ function SearchFiltersDialog(props = {}) {
45757
45762
  if (root2) root2.style.overflow = prevRoot;
45758
45763
  };
45759
45764
  }, [open]);
45765
+ const context = React35.useContext(PageContext2);
45766
+ const contextSiteTitle = context && context.site && typeof context.site.title === "string" ? context.site.title.trim() : "";
45767
+ const resolvedBrandLabel = typeof brandLabelProp === "string" && brandLabelProp.trim() ? brandLabelProp : contextSiteTitle || "Site title";
45760
45768
  if (!open) return null;
45761
45769
  const brandId = "canopy-modal-filters-label";
45762
45770
  const subtitleText = subtitle != null ? subtitle : title;
@@ -45767,7 +45775,7 @@ function SearchFiltersDialog(props = {}) {
45767
45775
  variant: "filters",
45768
45776
  open: true,
45769
45777
  labelledBy: brandId,
45770
- label: brandLabel,
45778
+ label: resolvedBrandLabel,
45771
45779
  logo: SiteLogo,
45772
45780
  href: brandHref,
45773
45781
  closeLabel: "Close filters",
@@ -47646,8 +47654,8 @@ function getReferencedModule() {
47646
47654
  return referencedModule;
47647
47655
  }
47648
47656
  function useReferencedManifestMap() {
47649
- const PageContext = getPageContext() || PageContextFallback;
47650
- const pageContext = React42.useContext(PageContext);
47657
+ const PageContext3 = getPageContext() || PageContextFallback;
47658
+ const pageContext = React42.useContext(PageContext3);
47651
47659
  const referencedItems = pageContext && pageContext.page && Array.isArray(pageContext.page.referencedItems) ? pageContext.page.referencedItems : [];
47652
47660
  return React42.useMemo(() => {
47653
47661
  const map = /* @__PURE__ */ new Map();