@canopy-iiif/app 0.12.4 → 0.12.6

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.
@@ -2062,8 +2062,51 @@ function CanopyFooter({ className = "", children }) {
2062
2062
  return /* @__PURE__ */ React20.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React20.createElement("div", { className: "canopy-footer__inner" }, children));
2063
2063
  }
2064
2064
 
2065
- // ui/src/layout/Container.jsx
2065
+ // ui/src/layout/TeaserCard.jsx
2066
2066
  import React21 from "react";
2067
+ function TeaserCard({
2068
+ href = "",
2069
+ title = "",
2070
+ metadata = [],
2071
+ summary = "",
2072
+ thumbnail = null,
2073
+ type = "work",
2074
+ className = "",
2075
+ ...rest
2076
+ }) {
2077
+ const Tag = href ? "a" : "div";
2078
+ const classes = [
2079
+ "canopy-card",
2080
+ "canopy-card--teaser",
2081
+ "canopy-search-teaser__item",
2082
+ "canopy-teaser-card",
2083
+ className
2084
+ ].filter(Boolean).join(" ");
2085
+ const showThumb = type === "work" && thumbnail;
2086
+ const metaLine = (Array.isArray(metadata) && metadata.length ? metadata.filter(Boolean) : summary ? [summary] : []).filter(Boolean).slice(0, 2).join(" \u2022 ");
2087
+ return /* @__PURE__ */ React21.createElement(
2088
+ Tag,
2089
+ {
2090
+ className: classes,
2091
+ href: href || void 0,
2092
+ "data-canopy-item": href ? "" : void 0,
2093
+ ...rest
2094
+ },
2095
+ showThumb ? /* @__PURE__ */ React21.createElement("div", { className: "canopy-search-teaser__thumb" }, /* @__PURE__ */ React21.createElement(
2096
+ "img",
2097
+ {
2098
+ src: thumbnail,
2099
+ alt: "",
2100
+ loading: "lazy",
2101
+ className: "canopy-search-teaser__thumb-img"
2102
+ }
2103
+ )) : null,
2104
+ /* @__PURE__ */ React21.createElement("div", { className: "canopy-search-teaser__text" }, /* @__PURE__ */ React21.createElement("span", { className: "canopy-search-teaser__title" }, title || href || "Untitled"), metaLine ? /* @__PURE__ */ React21.createElement("span", { className: "canopy-search-teaser__meta" }, metaLine) : null)
2105
+ );
2106
+ }
2107
+
2108
+ // ui/src/layout/Container.jsx
2109
+ import React22 from "react";
2067
2110
  function Container({
2068
2111
  className = "",
2069
2112
  variant = "content",
@@ -2072,7 +2115,7 @@ function Container({
2072
2115
  }) {
2073
2116
  const variantClass = variant === "wide" ? "max-w-wide" : "max-w-content";
2074
2117
  const classes = ["mx-auto", variantClass, "w-full", className].filter(Boolean).join(" ");
2075
- return /* @__PURE__ */ React21.createElement(
2118
+ return /* @__PURE__ */ React22.createElement(
2076
2119
  "div",
2077
2120
  {
2078
2121
  className: classes,
@@ -2084,11 +2127,11 @@ function Container({
2084
2127
  }
2085
2128
 
2086
2129
  // ui/src/content/ReferencedItems.jsx
2087
- import React23 from "react";
2130
+ import React24 from "react";
2088
2131
  import navigationHelpers4 from "@canopy-iiif/app/lib/components/navigation.js";
2089
2132
 
2090
2133
  // ui/src/layout/Card.jsx
2091
- import React22, { useEffect as useEffect5, useRef, useState as useState5 } from "react";
2134
+ import React23, { useEffect as useEffect5, useRef, useState as useState5 } from "react";
2092
2135
  function Card({
2093
2136
  href,
2094
2137
  src,
@@ -2146,8 +2189,8 @@ function Card({
2146
2189
  const h = Number(imgHeight);
2147
2190
  const ratio = Number.isFinite(Number(aspectRatio)) && Number(aspectRatio) > 0 ? Number(aspectRatio) : Number.isFinite(w) && w > 0 && Number.isFinite(h) && h > 0 ? w / h : void 0;
2148
2191
  const paddingPercent = ratio ? 100 / ratio : 100;
2149
- const caption = /* @__PURE__ */ React22.createElement("figcaption", null, title && /* @__PURE__ */ React22.createElement("span", null, title), subtitle && /* @__PURE__ */ React22.createElement("span", null, subtitle), children);
2150
- return /* @__PURE__ */ React22.createElement(
2192
+ const caption = /* @__PURE__ */ React23.createElement("figcaption", null, title && /* @__PURE__ */ React23.createElement("span", null, title), subtitle && /* @__PURE__ */ React23.createElement("span", null, subtitle), children);
2193
+ return /* @__PURE__ */ React23.createElement(
2151
2194
  "a",
2152
2195
  {
2153
2196
  href,
@@ -2159,13 +2202,13 @@ function Card({
2159
2202
  "data-image-loaded": imageLoaded ? "true" : "false",
2160
2203
  ...rest
2161
2204
  },
2162
- /* @__PURE__ */ React22.createElement("figure", null, src ? ratio ? /* @__PURE__ */ React22.createElement(
2205
+ /* @__PURE__ */ React23.createElement("figure", null, src ? ratio ? /* @__PURE__ */ React23.createElement(
2163
2206
  "div",
2164
2207
  {
2165
2208
  className: "canopy-card-media",
2166
2209
  style: { "--canopy-card-padding": `${paddingPercent}%` }
2167
2210
  },
2168
- inView ? /* @__PURE__ */ React22.createElement(
2211
+ inView ? /* @__PURE__ */ React23.createElement(
2169
2212
  "img",
2170
2213
  {
2171
2214
  src,
@@ -2175,7 +2218,7 @@ function Card({
2175
2218
  onError: () => setImageLoaded(true)
2176
2219
  }
2177
2220
  ) : null
2178
- ) : /* @__PURE__ */ React22.createElement(
2221
+ ) : /* @__PURE__ */ React23.createElement(
2179
2222
  "img",
2180
2223
  {
2181
2224
  src,
@@ -2194,7 +2237,7 @@ function useReferencedItems(itemsProp) {
2194
2237
  if (Array.isArray(itemsProp)) return itemsProp;
2195
2238
  const PageContext = navigationHelpers4 && typeof navigationHelpers4.getPageContext === "function" ? navigationHelpers4.getPageContext() : null;
2196
2239
  if (!PageContext) return [];
2197
- const context = React23.useContext(PageContext);
2240
+ const context = React24.useContext(PageContext);
2198
2241
  const items = context && context.page ? context.page.referencedItems : null;
2199
2242
  return Array.isArray(items) ? items : [];
2200
2243
  }
@@ -2214,13 +2257,13 @@ function ReferencedItems({
2214
2257
  "referenced-items--empty",
2215
2258
  className
2216
2259
  ].filter(Boolean).join(" ");
2217
- return /* @__PURE__ */ React23.createElement("div", { className: emptyClass, ...rest }, typeof emptyLabel === "function" ? emptyLabel() : emptyLabel);
2260
+ return /* @__PURE__ */ React24.createElement("div", { className: emptyClass, ...rest }, typeof emptyLabel === "function" ? emptyLabel() : emptyLabel);
2218
2261
  }
2219
2262
  const containerClassName = ["referenced-items", className].filter(Boolean).join(" ");
2220
- return /* @__PURE__ */ React23.createElement("section", { className: containerClassName, ...rest }, children, /* @__PURE__ */ React23.createElement("div", { className: "referenced-items__grid", role: "list" }, items.map((item) => {
2263
+ return /* @__PURE__ */ React24.createElement("section", { className: containerClassName, ...rest }, children, /* @__PURE__ */ React24.createElement("div", { className: "referenced-items__grid", role: "list" }, items.map((item) => {
2221
2264
  if (!item) return null;
2222
2265
  const key = item.href || item.slug || item.id;
2223
- return /* @__PURE__ */ React23.createElement("div", { className: "referenced-items__item", role: "listitem", key }, /* @__PURE__ */ React23.createElement(
2266
+ return /* @__PURE__ */ React24.createElement("div", { className: "referenced-items__item", role: "listitem", key }, /* @__PURE__ */ React24.createElement(
2224
2267
  Card,
2225
2268
  {
2226
2269
  href: item.href,
@@ -2237,7 +2280,7 @@ function ReferencedItems({
2237
2280
  }
2238
2281
 
2239
2282
  // ui/src/content/References.jsx
2240
- import React24 from "react";
2283
+ import React25 from "react";
2241
2284
  import navigationHelpers5 from "@canopy-iiif/app/lib/components/navigation.js";
2242
2285
  function getPageContext() {
2243
2286
  if (!navigationHelpers5 || typeof navigationHelpers5.getPageContext !== "function") {
@@ -2267,7 +2310,7 @@ function References({
2267
2310
  ...rest
2268
2311
  }) {
2269
2312
  const PageContext = getPageContext();
2270
- const context = PageContext ? React24.useContext(PageContext) : null;
2313
+ const context = PageContext ? React25.useContext(PageContext) : null;
2271
2314
  const contextPage = context && context.page ? context.page : null;
2272
2315
  const manifestId = id || contextPage && contextPage.manifestId || "";
2273
2316
  const contextReferences = !id && contextPage && Array.isArray(contextPage.referencedBy) ? contextPage.referencedBy : null;
@@ -2276,11 +2319,808 @@ function References({
2276
2319
  const entries = references && references.length ? references : null;
2277
2320
  if (!entries || !entries.length) return null;
2278
2321
  const containerClass = ["references", className].filter(Boolean).join(" ");
2279
- return /* @__PURE__ */ React24.createElement("dl", { className: containerClass, ...rest }, /* @__PURE__ */ React24.createElement("div", { className: "references__group" }, /* @__PURE__ */ React24.createElement("dt", null, title), entries.map((entry) => /* @__PURE__ */ React24.createElement("dd", { key: entry.href, className: "references__item" }, /* @__PURE__ */ React24.createElement("a", { href: entry.href }, entry.title || entry.href)))));
2322
+ return /* @__PURE__ */ React25.createElement("dl", { className: containerClass, ...rest }, /* @__PURE__ */ React25.createElement("div", { className: "references__group" }, /* @__PURE__ */ React25.createElement("dt", null, title), entries.map((entry) => /* @__PURE__ */ React25.createElement("dd", { key: entry.href, className: "references__item" }, /* @__PURE__ */ React25.createElement("a", { href: entry.href }, entry.title || entry.href)))));
2323
+ }
2324
+
2325
+ // ui/src/content/timeline/MdxTimeline.jsx
2326
+ import React27 from "react";
2327
+ import ReactDOMServer from "react-dom/server";
2328
+
2329
+ // ui/src/content/timeline/Timeline.jsx
2330
+ import React26 from "react";
2331
+
2332
+ // ui/src/content/timeline/date-utils.js
2333
+ var FALLBACK_LOCALE = (() => {
2334
+ try {
2335
+ return new Intl.Locale("en-US");
2336
+ } catch (_) {
2337
+ return "en-US";
2338
+ }
2339
+ })();
2340
+ function createLocale(localeValue) {
2341
+ if (!localeValue) return FALLBACK_LOCALE;
2342
+ if (typeof Intl !== "undefined" && localeValue instanceof Intl.Locale)
2343
+ return localeValue;
2344
+ try {
2345
+ return new Intl.Locale(localeValue);
2346
+ } catch (_) {
2347
+ return FALLBACK_LOCALE;
2348
+ }
2349
+ }
2350
+ function parseMonthIndex(name = "", locale = FALLBACK_LOCALE) {
2351
+ const localeId = typeof locale === "string" ? locale : locale && locale.baseName ? locale.baseName : "en-US";
2352
+ const normalized = String(name || "").trim().toLocaleLowerCase(localeId);
2353
+ if (!normalized) return 0;
2354
+ const months = [
2355
+ "january",
2356
+ "february",
2357
+ "march",
2358
+ "april",
2359
+ "may",
2360
+ "june",
2361
+ "july",
2362
+ "august",
2363
+ "september",
2364
+ "october",
2365
+ "november",
2366
+ "december"
2367
+ ];
2368
+ const idx = months.indexOf(normalized);
2369
+ return idx === -1 ? 0 : idx;
2370
+ }
2371
+ function parseStructuredInput(value) {
2372
+ if (!value || typeof value !== "object") return null;
2373
+ const { year, month, day } = value;
2374
+ if (!Number.isFinite(Number(year))) return null;
2375
+ const y = Number(year);
2376
+ const m = Number.isFinite(Number(month)) ? Number(month) - 1 : 0;
2377
+ const d = Number.isFinite(Number(day)) ? Number(day) : 1;
2378
+ return new Date(Date.UTC(y, Math.max(0, Math.min(11, m)), Math.max(1, Math.min(31, d))));
2379
+ }
2380
+ function createDateFromInput(value, { locale } = {}) {
2381
+ const loc = createLocale(locale);
2382
+ if (value instanceof Date) return new Date(value.getTime());
2383
+ if (Number.isFinite(Number(value))) {
2384
+ const year = Number(value);
2385
+ return new Date(Date.UTC(year, 0, 1));
2386
+ }
2387
+ if (value && typeof value === "object") {
2388
+ const structured = parseStructuredInput(value);
2389
+ if (structured) return structured;
2390
+ }
2391
+ const text = typeof value === "string" ? value.trim() : "";
2392
+ if (!text) return null;
2393
+ const isoYear = text.match(/^(\d{4})$/);
2394
+ if (isoYear) {
2395
+ return new Date(Date.UTC(Number(isoYear[1]), 0, 1));
2396
+ }
2397
+ const isoYearMonth = text.match(/^(\d{4})-(\d{2})$/);
2398
+ if (isoYearMonth) {
2399
+ return new Date(
2400
+ Date.UTC(Number(isoYearMonth[1]), Number(isoYearMonth[2]) - 1, 1)
2401
+ );
2402
+ }
2403
+ const isoDate = text.match(/^(\d{4})-(\d{2})-(\d{2})$/);
2404
+ if (isoDate) {
2405
+ return new Date(
2406
+ Date.UTC(
2407
+ Number(isoDate[1]),
2408
+ Number(isoDate[2]) - 1,
2409
+ Number(isoDate[3])
2410
+ )
2411
+ );
2412
+ }
2413
+ const monthDayYear = text.match(/^([A-Za-z]+)\s+(\d{1,2})(?:,)?\s*(\d{4})$/);
2414
+ if (monthDayYear) {
2415
+ const month = parseMonthIndex(monthDayYear[1], loc);
2416
+ const day = Number(monthDayYear[2]);
2417
+ const year = Number(monthDayYear[3]);
2418
+ return new Date(Date.UTC(year, month, day));
2419
+ }
2420
+ const monthYear = text.match(/^([A-Za-z]+)\s+(\d{4})$/);
2421
+ if (monthYear) {
2422
+ const month = parseMonthIndex(monthYear[1], loc);
2423
+ const year = Number(monthYear[2]);
2424
+ return new Date(Date.UTC(year, month, 1));
2425
+ }
2426
+ const fallback = new Date(text);
2427
+ if (!Number.isNaN(fallback.getTime())) return fallback;
2428
+ const alt = new Date(Date.parse(text));
2429
+ if (!Number.isNaN(alt.getTime())) return alt;
2430
+ return null;
2431
+ }
2432
+ function formatDateLabel(date, { granularity = "day", locale } = {}) {
2433
+ if (!(date instanceof Date) || Number.isNaN(date.getTime())) return "";
2434
+ const loc = createLocale(locale);
2435
+ const baseName = typeof loc === "string" ? loc : loc.baseName || "en-US";
2436
+ const options = { year: "numeric" };
2437
+ if (granularity === "month" || granularity === "day") {
2438
+ options.month = "long";
2439
+ }
2440
+ if (granularity === "day") {
2441
+ options.day = "numeric";
2442
+ }
2443
+ options.timeZone = "UTC";
2444
+ try {
2445
+ const formatter = new Intl.DateTimeFormat(baseName, options);
2446
+ return formatter.format(date);
2447
+ } catch (_) {
2448
+ return date.toISOString().slice(0, 10);
2449
+ }
2450
+ }
2451
+ function normalizeRange(range = {}) {
2452
+ const { start, end, granularity = "year", locale } = range || {};
2453
+ const loc = createLocale(locale);
2454
+ const startDate = createDateFromInput(start || /* @__PURE__ */ new Date(), { locale: loc }) || new Date(Date.UTC(0, 0, 1));
2455
+ let endDate = createDateFromInput(end, { locale: loc });
2456
+ if (!endDate) {
2457
+ const copy = new Date(startDate.getTime());
2458
+ copy.setUTCFullYear(copy.getUTCFullYear() + 1);
2459
+ endDate = copy;
2460
+ }
2461
+ if (endDate <= startDate) {
2462
+ const nextDay = new Date(startDate.getTime());
2463
+ nextDay.setUTCDate(nextDay.getUTCDate() + 1);
2464
+ endDate = nextDay;
2465
+ }
2466
+ const span = Math.max(1, endDate - startDate);
2467
+ return {
2468
+ startDate,
2469
+ endDate,
2470
+ span,
2471
+ granularity: granularity === "month" || granularity === "day" ? granularity : "year",
2472
+ locale: loc
2473
+ };
2474
+ }
2475
+ function buildPointMetadata({ value, granularity, locale }) {
2476
+ const date = createDateFromInput(value, { locale });
2477
+ return {
2478
+ date,
2479
+ label: formatDateLabel(date, { granularity, locale }),
2480
+ timestamp: date ? date.getTime() : null
2481
+ };
2482
+ }
2483
+ function clampProgress(value) {
2484
+ if (!Number.isFinite(value)) return 0;
2485
+ if (value < 0) return 0;
2486
+ if (value > 1) return 1;
2487
+ return value;
2488
+ }
2489
+
2490
+ // ui/src/content/timeline/Timeline.jsx
2491
+ var DAY_MS = 24 * 60 * 60 * 1e3;
2492
+ var DEFAULT_TRACK_HEIGHT = 640;
2493
+ var MIN_HEIGHT_PER_POINT = 220;
2494
+ function getThresholdMs(threshold, granularity) {
2495
+ const value = Number(threshold);
2496
+ if (!Number.isFinite(value) || value <= 0) return 0;
2497
+ if (granularity === "day") return value * DAY_MS;
2498
+ if (granularity === "month") return value * 30 * DAY_MS;
2499
+ return value * 365 * DAY_MS;
2500
+ }
2501
+ function buildGroupedEntries(points, thresholdMs, options) {
2502
+ if (!Array.isArray(points) || !points.length) return [];
2503
+ if (!thresholdMs) return points.map((point) => ({ type: "point", point }));
2504
+ const entries = [];
2505
+ let currentGroup = null;
2506
+ let groupCounter = 0;
2507
+ function flush() {
2508
+ if (!currentGroup) return;
2509
+ if (currentGroup.points.length > 1) {
2510
+ const firstPoint = currentGroup.points[0];
2511
+ entries.push({
2512
+ type: "group",
2513
+ id: `canopy-timeline-group-${groupCounter}-${currentGroup.start}`,
2514
+ points: currentGroup.points,
2515
+ progress: firstPoint.progress,
2516
+ side: firstPoint.side,
2517
+ label: formatGroupLabel(currentGroup.start, currentGroup.end, options),
2518
+ count: currentGroup.points.length
2519
+ });
2520
+ groupCounter += 1;
2521
+ } else {
2522
+ entries.push({ type: "point", point: currentGroup.points[0] });
2523
+ }
2524
+ currentGroup = null;
2525
+ }
2526
+ points.forEach((point) => {
2527
+ const timestamp = point && point.meta ? point.meta.timestamp : null;
2528
+ if (!Number.isFinite(timestamp)) {
2529
+ flush();
2530
+ if (point) entries.push({ type: "point", point });
2531
+ return;
2532
+ }
2533
+ if (!currentGroup) {
2534
+ currentGroup = {
2535
+ points: [point],
2536
+ start: timestamp,
2537
+ end: timestamp,
2538
+ last: timestamp
2539
+ };
2540
+ return;
2541
+ }
2542
+ const diff = Math.abs(timestamp - currentGroup.last);
2543
+ if (diff <= thresholdMs) {
2544
+ currentGroup.points.push(point);
2545
+ currentGroup.last = timestamp;
2546
+ if (timestamp < currentGroup.start) currentGroup.start = timestamp;
2547
+ if (timestamp > currentGroup.end) currentGroup.end = timestamp;
2548
+ } else {
2549
+ flush();
2550
+ currentGroup = {
2551
+ points: [point],
2552
+ start: timestamp,
2553
+ end: timestamp,
2554
+ last: timestamp
2555
+ };
2556
+ }
2557
+ });
2558
+ flush();
2559
+ return entries;
2560
+ }
2561
+ function formatGroupLabel(startTs, endTs, options) {
2562
+ if (!Number.isFinite(startTs) || !Number.isFinite(endTs)) return "";
2563
+ const startLabel = formatDateLabel(new Date(startTs), options);
2564
+ const endLabel = formatDateLabel(new Date(endTs), options);
2565
+ if (!startLabel || !endLabel) return "";
2566
+ return startLabel === endLabel ? startLabel : `${startLabel} \u2013 ${endLabel}`;
2567
+ }
2568
+ function deriveRangeOverrides(points, range) {
2569
+ const timestamps = points.map((point) => point && point.meta ? point.meta.timestamp : null).filter((timestamp) => Number.isFinite(timestamp));
2570
+ if (!timestamps.length) return range || {};
2571
+ const earliest = Math.min(...timestamps);
2572
+ const latest = Math.max(...timestamps);
2573
+ return {
2574
+ ...range,
2575
+ start: range && range.start ? range.start : new Date(earliest),
2576
+ end: range && range.end ? range.end : new Date(latest)
2577
+ };
2578
+ }
2579
+ function getActivePointId(points) {
2580
+ const highlighted = points.find((point) => point && point.highlight);
2581
+ if (highlighted) return highlighted.id;
2582
+ return points.length ? points[0].id : null;
2583
+ }
2584
+ function formatRangeLabel(rangeInfo) {
2585
+ if (!rangeInfo) return "";
2586
+ const startLabel = formatDateLabel(rangeInfo.startDate, {
2587
+ granularity: rangeInfo.granularity,
2588
+ locale: rangeInfo.locale
2589
+ });
2590
+ const endLabel = formatDateLabel(rangeInfo.endDate, {
2591
+ granularity: rangeInfo.granularity,
2592
+ locale: rangeInfo.locale
2593
+ });
2594
+ if (!startLabel || !endLabel) return "";
2595
+ if (startLabel === endLabel) return startLabel;
2596
+ return `${startLabel} \u2013 ${endLabel}`;
2597
+ }
2598
+ function sanitizePoints(points) {
2599
+ if (!Array.isArray(points)) return [];
2600
+ return points.map((point, index) => {
2601
+ if (!point) return null;
2602
+ const meta = point.meta || {};
2603
+ const timestamp = Number(meta.timestamp);
2604
+ const manifests = Array.isArray(point.manifests) ? point.manifests.map((manifest) => manifest ? { ...manifest } : null).filter(Boolean) : [];
2605
+ const resources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
2606
+ return {
2607
+ ...point,
2608
+ id: point.id || `timeline-point-${index}`,
2609
+ title: point.title || point.label || `Point ${index + 1}`,
2610
+ summary: point.summary || point.description || "",
2611
+ detailsHtml: point.detailsHtml || "",
2612
+ highlight: !!point.highlight,
2613
+ side: point.side === "left" || point.side === "right" ? point.side : null,
2614
+ meta: {
2615
+ label: meta.label || "",
2616
+ timestamp: Number.isFinite(timestamp) ? timestamp : null
2617
+ },
2618
+ manifests,
2619
+ resources
2620
+ };
2621
+ }).filter(Boolean);
2622
+ }
2623
+ function resolveTrackHeight(height, pointCount) {
2624
+ const minimumPx = Math.max(
2625
+ DEFAULT_TRACK_HEIGHT,
2626
+ pointCount * MIN_HEIGHT_PER_POINT
2627
+ );
2628
+ const fallback = `${minimumPx}px`;
2629
+ if (height == null) return fallback;
2630
+ if (typeof height === "number") {
2631
+ const numeric = Number(height);
2632
+ if (Number.isFinite(numeric)) {
2633
+ return `${Math.max(numeric, pointCount * MIN_HEIGHT_PER_POINT)}px`;
2634
+ }
2635
+ return fallback;
2636
+ }
2637
+ if (typeof height === "string") {
2638
+ const trimmed = height.trim();
2639
+ if (!trimmed) return fallback;
2640
+ const numeric = Number(trimmed);
2641
+ if (Number.isFinite(numeric)) {
2642
+ return `${Math.max(numeric, pointCount * MIN_HEIGHT_PER_POINT)}px`;
2643
+ }
2644
+ return trimmed;
2645
+ }
2646
+ return fallback;
2647
+ }
2648
+ function TimelineConnector({ side, isActive, highlight }) {
2649
+ const connectorClasses = [
2650
+ "canopy-timeline__connector",
2651
+ side === "left" ? "canopy-timeline__connector--left" : "canopy-timeline__connector--right"
2652
+ ].filter(Boolean).join(" ");
2653
+ const dotClasses = [
2654
+ "canopy-timeline__connector-dot",
2655
+ highlight || isActive ? "is-active" : ""
2656
+ ].filter(Boolean).join(" ");
2657
+ return /* @__PURE__ */ React26.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React26.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement("span", { className: dotClasses }), /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__connector-line" })));
2658
+ }
2659
+ function renderResourceSection(point) {
2660
+ if (!point) return null;
2661
+ const manifestCards = Array.isArray(point.manifests) ? point.manifests.filter(Boolean) : [];
2662
+ const legacyResources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
2663
+ if (!manifestCards.length && !legacyResources.length) return null;
2664
+ return /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React26.createElement("div", { key: manifest.id || manifest.href }, /* @__PURE__ */ React26.createElement(
2665
+ TeaserCard,
2666
+ {
2667
+ href: manifest.href,
2668
+ title: manifest.title || manifest.href,
2669
+ summary: manifest.summary,
2670
+ metadata: Array.isArray(manifest.metadata) && manifest.metadata.length ? manifest.metadata : manifest.summary ? [manifest.summary] : [],
2671
+ thumbnail: manifest.thumbnail,
2672
+ type: manifest.type || "work"
2673
+ }
2674
+ ))), legacyResources.map((resource, idx) => /* @__PURE__ */ React26.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React26.createElement(
2675
+ TeaserCard,
2676
+ {
2677
+ href: resource.href,
2678
+ title: resource.label || resource.title || resource.href,
2679
+ summary: resource.summary,
2680
+ thumbnail: resource.thumbnail,
2681
+ type: resource.type || "resource"
2682
+ }
2683
+ )))));
2684
+ }
2685
+ function Timeline({
2686
+ className = "",
2687
+ title,
2688
+ description,
2689
+ range: rangeProp,
2690
+ locale: localeProp = "en-US",
2691
+ height = DEFAULT_TRACK_HEIGHT,
2692
+ threshold: thresholdProp = null,
2693
+ steps = null,
2694
+ points: pointsProp,
2695
+ __canopyTimeline: payload = null,
2696
+ ...rest
2697
+ }) {
2698
+ const payloadPoints = payload && Array.isArray(payload.points) ? payload.points : null;
2699
+ const rawPoints = React26.useMemo(() => {
2700
+ if (Array.isArray(pointsProp) && pointsProp.length) return pointsProp;
2701
+ if (payloadPoints && payloadPoints.length) return payloadPoints;
2702
+ return [];
2703
+ }, [pointsProp, payloadPoints]);
2704
+ const sanitizedPoints = React26.useMemo(
2705
+ () => sanitizePoints(rawPoints),
2706
+ [rawPoints]
2707
+ );
2708
+ const localeValue = payload && payload.locale ? payload.locale : localeProp;
2709
+ const baseLocale = React26.useMemo(
2710
+ () => createLocale(localeValue),
2711
+ [localeValue]
2712
+ );
2713
+ const rangeInput = payload && payload.range ? payload.range : rangeProp || {};
2714
+ const rangeOverrides = React26.useMemo(
2715
+ () => deriveRangeOverrides(sanitizedPoints, rangeInput),
2716
+ [sanitizedPoints, rangeInput]
2717
+ );
2718
+ const effectiveRange = React26.useMemo(
2719
+ () => normalizeRange({
2720
+ ...rangeOverrides,
2721
+ locale: baseLocale
2722
+ }),
2723
+ [rangeOverrides, baseLocale]
2724
+ );
2725
+ const spanStart = effectiveRange.startDate.getTime();
2726
+ const span = effectiveRange.span;
2727
+ const pointsWithPosition = React26.useMemo(() => {
2728
+ if (!sanitizedPoints.length) return [];
2729
+ return sanitizedPoints.map((point, index) => {
2730
+ const timestamp = point.meta.timestamp;
2731
+ const fallbackProgress = sanitizedPoints.length > 1 ? index / (sanitizedPoints.length - 1) : 0;
2732
+ const progress = Number.isFinite(timestamp) ? clampProgress((timestamp - spanStart) / span) : fallbackProgress;
2733
+ const side = point.side || (index % 2 === 0 ? "left" : "right");
2734
+ return {
2735
+ ...point,
2736
+ progress,
2737
+ side
2738
+ };
2739
+ });
2740
+ }, [sanitizedPoints, spanStart, span]);
2741
+ const [activeId, setActiveId] = React26.useState(
2742
+ () => getActivePointId(pointsWithPosition)
2743
+ );
2744
+ React26.useEffect(() => {
2745
+ setActiveId(getActivePointId(pointsWithPosition));
2746
+ }, [pointsWithPosition]);
2747
+ const thresholdValue = typeof thresholdProp === "number" ? thresholdProp : payload && payload.threshold != null ? payload.threshold : null;
2748
+ const stepsValue = typeof steps === "number" ? Number(steps) : payload && typeof payload.steps === "number" ? Number(payload.steps) : null;
2749
+ const thresholdMs = React26.useMemo(
2750
+ () => getThresholdMs(thresholdValue, effectiveRange.granularity),
2751
+ [thresholdValue, effectiveRange.granularity]
2752
+ );
2753
+ const groupedEntries = React26.useMemo(
2754
+ () => buildGroupedEntries(pointsWithPosition, thresholdMs, {
2755
+ granularity: effectiveRange.granularity,
2756
+ locale: baseLocale
2757
+ }),
2758
+ [pointsWithPosition, thresholdMs, effectiveRange.granularity, baseLocale]
2759
+ );
2760
+ const [expandedGroupIds, setExpandedGroupIds] = React26.useState(
2761
+ () => /* @__PURE__ */ new Set()
2762
+ );
2763
+ React26.useEffect(() => {
2764
+ setExpandedGroupIds((prev) => {
2765
+ if (!prev || prev.size === 0) return prev;
2766
+ const validIds = new Set(
2767
+ groupedEntries.filter((entry) => entry.type === "group").map((entry) => entry.id)
2768
+ );
2769
+ const next = /* @__PURE__ */ new Set();
2770
+ let changed = false;
2771
+ prev.forEach((id) => {
2772
+ if (validIds.has(id)) next.add(id);
2773
+ else changed = true;
2774
+ });
2775
+ return changed ? next : prev;
2776
+ });
2777
+ }, [groupedEntries]);
2778
+ const toggleGroup = React26.useCallback((groupId) => {
2779
+ setExpandedGroupIds((prev) => {
2780
+ const next = new Set(prev || []);
2781
+ if (next.has(groupId)) next.delete(groupId);
2782
+ else next.add(groupId);
2783
+ return next;
2784
+ });
2785
+ }, []);
2786
+ const trackHeight = resolveTrackHeight(height, pointsWithPosition.length);
2787
+ const containerClasses = ["canopy-timeline", className].filter(Boolean).join(" ");
2788
+ const rangeLabel = formatRangeLabel(effectiveRange);
2789
+ function renderPointEntry(point) {
2790
+ if (!point) return null;
2791
+ const wrapperClasses = [
2792
+ "canopy-timeline__point-wrapper",
2793
+ point.side === "left" ? "canopy-timeline__point-wrapper--left" : "canopy-timeline__point-wrapper--right"
2794
+ ].filter(Boolean).join(" ");
2795
+ const wrapperStyle = { top: `${point.progress * 100}%` };
2796
+ const cardClasses = [
2797
+ "canopy-timeline__point",
2798
+ point.id === activeId ? "is-active" : "",
2799
+ point.highlight ? "is-highlighted" : ""
2800
+ ].filter(Boolean).join(" ");
2801
+ const connector = /* @__PURE__ */ React26.createElement(
2802
+ TimelineConnector,
2803
+ {
2804
+ side: point.side,
2805
+ isActive: point.id === activeId,
2806
+ highlight: point.highlight
2807
+ }
2808
+ );
2809
+ const body = /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
2810
+ const resourceSection = renderResourceSection(point);
2811
+ return /* @__PURE__ */ React26.createElement(
2812
+ "div",
2813
+ {
2814
+ key: point.id,
2815
+ className: wrapperClasses,
2816
+ style: wrapperStyle,
2817
+ role: "listitem"
2818
+ },
2819
+ point.side === "left" ? /* @__PURE__ */ React26.createElement(React26.Fragment, null, /* @__PURE__ */ React26.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React26.createElement(React26.Fragment, null, connector, /* @__PURE__ */ React26.createElement("div", { className: cardClasses }, body, resourceSection))
2820
+ );
2821
+ }
2822
+ function renderGroupEntry(entry) {
2823
+ const wrapperClasses = [
2824
+ "canopy-timeline__point-wrapper",
2825
+ entry.side === "left" ? "canopy-timeline__point-wrapper--left" : "canopy-timeline__point-wrapper--right"
2826
+ ].filter(Boolean).join(" ");
2827
+ const wrapperStyle = { top: `${entry.progress * 100}%` };
2828
+ const isExpanded = expandedGroupIds.has(entry.id);
2829
+ const hasActivePoint = entry.points.some((point) => point.id === activeId);
2830
+ const connector = /* @__PURE__ */ React26.createElement(
2831
+ TimelineConnector,
2832
+ {
2833
+ side: entry.side,
2834
+ isActive: hasActivePoint,
2835
+ highlight: hasActivePoint
2836
+ }
2837
+ );
2838
+ const groupClasses = [
2839
+ "canopy-timeline__group",
2840
+ isExpanded ? "is-expanded" : "",
2841
+ hasActivePoint ? "is-active" : ""
2842
+ ].filter(Boolean).join(" ");
2843
+ const countLabel = `${entry.count} event${entry.count > 1 ? "s" : ""}`;
2844
+ const header = /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__point-date" }, entry.label), /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__group-count" }, countLabel)), /* @__PURE__ */ React26.createElement(
2845
+ "button",
2846
+ {
2847
+ type: "button",
2848
+ className: "canopy-timeline__group-toggle",
2849
+ "aria-expanded": isExpanded ? "true" : "false",
2850
+ onClick: () => toggleGroup(entry.id)
2851
+ },
2852
+ isExpanded ? "Hide details" : "Show details"
2853
+ ));
2854
+ const groupPoints = isExpanded ? /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React26.createElement(
2855
+ "button",
2856
+ {
2857
+ key: point.id,
2858
+ type: "button",
2859
+ className: [
2860
+ "canopy-timeline__group-point",
2861
+ point.id === activeId ? "is-active" : ""
2862
+ ].filter(Boolean).join(" "),
2863
+ onClick: () => setActiveId(point.id)
2864
+ },
2865
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label),
2866
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
2867
+ ))) : null;
2868
+ const groupCard = /* @__PURE__ */ React26.createElement("div", { className: groupClasses }, header, groupPoints);
2869
+ return /* @__PURE__ */ React26.createElement(
2870
+ "div",
2871
+ {
2872
+ key: entry.id,
2873
+ className: wrapperClasses,
2874
+ style: wrapperStyle,
2875
+ role: "listitem"
2876
+ },
2877
+ entry.side === "left" ? /* @__PURE__ */ React26.createElement(React26.Fragment, null, groupCard, connector) : /* @__PURE__ */ React26.createElement(React26.Fragment, null, connector, groupCard)
2878
+ );
2879
+ }
2880
+ return /* @__PURE__ */ React26.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React26.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React26.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React26.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React26.createElement(
2881
+ "div",
2882
+ {
2883
+ className: "canopy-timeline__list",
2884
+ role: "list",
2885
+ style: { minHeight: trackHeight }
2886
+ },
2887
+ /* @__PURE__ */ React26.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
2888
+ renderSteps(stepsValue, effectiveRange),
2889
+ groupedEntries.map((entry) => {
2890
+ if (entry.type === "group") return renderGroupEntry(entry);
2891
+ return renderPointEntry(entry.point);
2892
+ })
2893
+ )));
2894
+ }
2895
+ function renderSteps(stepSize, range) {
2896
+ if (!Number.isFinite(stepSize) || stepSize <= 0 || !range) return null;
2897
+ const startYear = range.startDate.getUTCFullYear();
2898
+ const endYear = range.endDate.getUTCFullYear();
2899
+ const markers = [];
2900
+ if (startYear < endYear) {
2901
+ markers.push(
2902
+ /* @__PURE__ */ React26.createElement(
2903
+ "span",
2904
+ {
2905
+ key: "timeline-step-start",
2906
+ className: "canopy-timeline__step canopy-timeline__step--start",
2907
+ style: { top: "0%" },
2908
+ "aria-hidden": "true"
2909
+ },
2910
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__step-line" }),
2911
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
2912
+ )
2913
+ );
2914
+ markers.push(
2915
+ /* @__PURE__ */ React26.createElement(
2916
+ "span",
2917
+ {
2918
+ key: "timeline-step-end",
2919
+ className: "canopy-timeline__step canopy-timeline__step--end",
2920
+ style: { top: "100%" },
2921
+ "aria-hidden": "true"
2922
+ },
2923
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__step-line" }),
2924
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
2925
+ )
2926
+ );
2927
+ }
2928
+ const baseYear = Math.ceil(startYear / stepSize) * stepSize;
2929
+ for (let year = baseYear; year <= endYear; year += stepSize) {
2930
+ const timestamp = Date.UTC(year, 0, 1);
2931
+ const progress = (timestamp - range.startDate.getTime()) / range.span;
2932
+ if (progress <= 0 || progress >= 1) continue;
2933
+ markers.push(
2934
+ /* @__PURE__ */ React26.createElement(
2935
+ "span",
2936
+ {
2937
+ key: `timeline-step-${year}`,
2938
+ className: "canopy-timeline__step",
2939
+ style: { top: `calc(${progress * 100}% - 0.5px)` },
2940
+ "aria-hidden": "true"
2941
+ },
2942
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__step-line" }),
2943
+ /* @__PURE__ */ React26.createElement("span", { className: "canopy-timeline__step-label" }, year)
2944
+ )
2945
+ );
2946
+ }
2947
+ return markers.length ? markers : null;
2948
+ }
2949
+
2950
+ // ui/src/content/timeline/TimelinePoint.jsx
2951
+ function TimelinePoint() {
2952
+ return null;
2953
+ }
2954
+ TimelinePoint.displayName = "TimelinePoint";
2955
+
2956
+ // ui/src/content/timeline/MdxTimeline.jsx
2957
+ import navigationHelpers6 from "@canopy-iiif/app/lib/components/navigation.js";
2958
+ function normalizeManifestId(raw) {
2959
+ if (!raw) return "";
2960
+ try {
2961
+ const url = new URL(String(raw));
2962
+ url.hash = "";
2963
+ const params = Array.from(url.searchParams.entries()).sort(
2964
+ (a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1])
2965
+ );
2966
+ url.search = "";
2967
+ params.forEach(([key, value]) => url.searchParams.append(key, value));
2968
+ return url.toString();
2969
+ } catch (_) {
2970
+ return String(raw || "").trim();
2971
+ }
2972
+ }
2973
+ var PageContextFallback = React27.createContext(null);
2974
+ function useReferencedManifestMap() {
2975
+ var _a, _b;
2976
+ const PageContext = ((_b = (_a = navigationHelpers6) == null ? void 0 : _a.getPageContext) == null ? void 0 : _b.call(_a)) || PageContextFallback;
2977
+ const pageContext = React27.useContext(PageContext);
2978
+ const referencedItems = pageContext && pageContext.page && Array.isArray(pageContext.page.referencedItems) ? pageContext.page.referencedItems : [];
2979
+ return React27.useMemo(() => {
2980
+ const map = /* @__PURE__ */ new Map();
2981
+ referencedItems.forEach((item) => {
2982
+ if (!item) return;
2983
+ const id = item.id || item.href;
2984
+ if (!id) return;
2985
+ const normalized = normalizeManifestId(id);
2986
+ map.set(normalized, item);
2987
+ map.set(String(id), item);
2988
+ });
2989
+ return map;
2990
+ }, [referencedItems]);
2991
+ }
2992
+ function normalizeResource(resource, index) {
2993
+ if (!resource) return null;
2994
+ const href = resource.href || resource.id || "";
2995
+ const label = resource.label || resource.title || href || `Resource ${index + 1}`;
2996
+ return {
2997
+ id: resource.id || href || `timeline-resource-${index}`,
2998
+ href,
2999
+ label,
3000
+ type: resource.type || "IIIF",
3001
+ thumbnail: resource.thumbnail || null,
3002
+ summary: resource.summary || null
3003
+ };
3004
+ }
3005
+ function normalizePoint(child, index, options) {
3006
+ var _a, _b, _c, _d, _e, _f;
3007
+ if (!React27.isValidElement(child)) return null;
3008
+ if (child.type !== TimelinePoint && ((_a = child.type) == null ? void 0 : _a.displayName) !== "TimelinePoint")
3009
+ return null;
3010
+ const props = child.props || {};
3011
+ const id = props.id || `timeline-point-${index}`;
3012
+ const granularity = props.precision || props.granularity || options && options.range && options.range.granularity || "year";
3013
+ const value = (_f = (_e = (_d = (_c = (_b = props.date) != null ? _b : props.value) != null ? _c : props.timestamp) != null ? _d : props.year) != null ? _e : props.label) != null ? _f : props.title;
3014
+ const meta = buildPointMetadata({
3015
+ value,
3016
+ granularity,
3017
+ locale: options.locale
3018
+ });
3019
+ let detailsHtml = "";
3020
+ try {
3021
+ if (props.children) {
3022
+ detailsHtml = ReactDOMServer.renderToStaticMarkup(
3023
+ React27.createElement(React27.Fragment, null, props.children)
3024
+ );
3025
+ }
3026
+ } catch (_) {
3027
+ detailsHtml = "";
3028
+ }
3029
+ const resources = Array.isArray(props.iiifResources) ? props.iiifResources.map(normalizeResource).filter(Boolean) : [];
3030
+ const manifestValues = Array.isArray(props.referencedManifests) ? props.referencedManifests : props.manifest ? [props.manifest] : Array.isArray(props.manifests) ? props.manifests : [];
3031
+ const manifests = resolveManifestReferences(manifestValues, options.manifestMap);
3032
+ return {
3033
+ id,
3034
+ title: props.title || props.label || `Point ${index + 1}`,
3035
+ summary: props.summary || props.description || "",
3036
+ description: props.description || "",
3037
+ highlight: props.highlight === true || props.highlight === "true",
3038
+ side: props.side || props.align || props.alignment || null,
3039
+ meta: {
3040
+ label: meta.label,
3041
+ timestamp: meta.timestamp
3042
+ },
3043
+ detailsHtml,
3044
+ resources,
3045
+ manifests
3046
+ };
3047
+ }
3048
+ function resolveManifestReferences(ids, manifestMap) {
3049
+ if (!Array.isArray(ids) || !ids.length || !manifestMap) return [];
3050
+ const seen = /* @__PURE__ */ new Set();
3051
+ const out = [];
3052
+ ids.forEach((value) => {
3053
+ if (!value) return;
3054
+ const normalized = normalizeManifestId(value);
3055
+ if (!normalized || seen.has(normalized)) return;
3056
+ seen.add(normalized);
3057
+ const record = manifestMap.get(normalized);
3058
+ if (!record) return;
3059
+ out.push({
3060
+ id: record.id || value,
3061
+ href: record.href || null,
3062
+ title: record.title || record.href || value,
3063
+ summary: record.summary || "",
3064
+ thumbnail: record.thumbnail || null,
3065
+ thumbnailWidth: record.thumbnailWidth,
3066
+ thumbnailHeight: record.thumbnailHeight,
3067
+ type: record.type || "work",
3068
+ metadata: Array.isArray(record.metadata) && record.metadata.length ? record.metadata : record.summary ? [record.summary] : []
3069
+ });
3070
+ });
3071
+ return out;
3072
+ }
3073
+ function serializeProps(props, payload, locale) {
3074
+ const clone = {};
3075
+ Object.keys(props || {}).forEach((key) => {
3076
+ if (key === "children") return;
3077
+ const value = props[key];
3078
+ if (typeof value === "function" || typeof value === "symbol") return;
3079
+ clone[key] = value;
3080
+ });
3081
+ clone.locale = locale;
3082
+ clone.__canopyTimeline = payload;
3083
+ return clone;
3084
+ }
3085
+ function serializeForScript(data) {
3086
+ try {
3087
+ return JSON.stringify(data).replace(/</g, "\\u003c");
3088
+ } catch (_) {
3089
+ return "{}";
3090
+ }
3091
+ }
3092
+ function MdxTimeline({ children, ...rest }) {
3093
+ const localeValue = rest.locale || "en-US";
3094
+ const localeObj = createLocale(localeValue);
3095
+ const localeBase = typeof localeObj === "string" ? localeObj : localeObj.baseName || "en-US";
3096
+ const manifestMap = useReferencedManifestMap();
3097
+ const childArray = React27.Children.toArray(children);
3098
+ const points = childArray.map(
3099
+ (child, index) => normalizePoint(child, index, {
3100
+ range: rest.range || {},
3101
+ locale: localeObj,
3102
+ manifestMap
3103
+ })
3104
+ ).filter(Boolean);
3105
+ const payload = {
3106
+ points,
3107
+ locale: localeBase,
3108
+ range: rest.range || null,
3109
+ threshold: rest.threshold != null ? rest.threshold : null,
3110
+ steps: rest.steps != null ? rest.steps : null
3111
+ };
3112
+ const json = serializeForScript(serializeProps(rest, payload, localeBase));
3113
+ return /* @__PURE__ */ React27.createElement("div", { "data-canopy-timeline": "1" }, /* @__PURE__ */ React27.createElement(Timeline, { ...rest, __canopyTimeline: payload }), /* @__PURE__ */ React27.createElement(
3114
+ "script",
3115
+ {
3116
+ type: "application/json",
3117
+ dangerouslySetInnerHTML: { __html: json }
3118
+ }
3119
+ ));
2280
3120
  }
2281
3121
 
2282
3122
  // ui/src/search/MdxSearchResults.jsx
2283
- import React25 from "react";
3123
+ import React28 from "react";
2284
3124
  function MdxSearchResults(props) {
2285
3125
  let json = "{}";
2286
3126
  try {
@@ -2288,11 +3128,11 @@ function MdxSearchResults(props) {
2288
3128
  } catch (_) {
2289
3129
  json = "{}";
2290
3130
  }
2291
- return /* @__PURE__ */ React25.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React25.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3131
+ return /* @__PURE__ */ React28.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React28.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
2292
3132
  }
2293
3133
 
2294
3134
  // ui/src/search/SearchSummary.jsx
2295
- import React26 from "react";
3135
+ import React29 from "react";
2296
3136
  function SearchSummary(props) {
2297
3137
  let json = "{}";
2298
3138
  try {
@@ -2300,11 +3140,11 @@ function SearchSummary(props) {
2300
3140
  } catch (_) {
2301
3141
  json = "{}";
2302
3142
  }
2303
- return /* @__PURE__ */ React26.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React26.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3143
+ return /* @__PURE__ */ React29.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React29.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
2304
3144
  }
2305
3145
 
2306
3146
  // ui/src/search/MdxSearchTabs.jsx
2307
- import React27 from "react";
3147
+ import React30 from "react";
2308
3148
  function MdxSearchTabs(props) {
2309
3149
  let json = "{}";
2310
3150
  try {
@@ -2312,11 +3152,11 @@ function MdxSearchTabs(props) {
2312
3152
  } catch (_) {
2313
3153
  json = "{}";
2314
3154
  }
2315
- return /* @__PURE__ */ React27.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React27.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
3155
+ return /* @__PURE__ */ React30.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React30.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
2316
3156
  }
2317
3157
 
2318
3158
  // ui/src/search-form/MdxSearchFormModal.jsx
2319
- import React28 from "react";
3159
+ import React31 from "react";
2320
3160
  function MdxSearchFormModal(props = {}) {
2321
3161
  const {
2322
3162
  placeholder = "Search\u2026",
@@ -2332,11 +3172,11 @@ function MdxSearchFormModal(props = {}) {
2332
3172
  const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
2333
3173
  const resolvedSearchPath = resolveSearchPath(searchPath);
2334
3174
  const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
2335
- return /* @__PURE__ */ React28.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React28.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React28.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React28.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React28.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
3175
+ return /* @__PURE__ */ React31.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React31.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React31.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React31.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React31.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
2336
3176
  }
2337
3177
 
2338
3178
  // ui/src/iiif/ManifestPrimitives.jsx
2339
- import React29 from "react";
3179
+ import React32 from "react";
2340
3180
  import {
2341
3181
  Label as CloverLabel,
2342
3182
  Metadata as CloverMetadata,
@@ -2361,28 +3201,28 @@ function ensureMetadata(items) {
2361
3201
  function Label({ manifest, label, ...rest }) {
2362
3202
  const intl = label || manifest && manifest.label;
2363
3203
  if (!hasInternationalValue(intl)) return null;
2364
- return /* @__PURE__ */ React29.createElement(CloverLabel, { label: intl, ...rest });
3204
+ return /* @__PURE__ */ React32.createElement(CloverLabel, { label: intl, ...rest });
2365
3205
  }
2366
3206
  function Summary({ manifest, summary, ...rest }) {
2367
3207
  const intl = summary || manifest && manifest.summary;
2368
3208
  if (!hasInternationalValue(intl)) return null;
2369
- return /* @__PURE__ */ React29.createElement(CloverSummary, { summary: intl, ...rest });
3209
+ return /* @__PURE__ */ React32.createElement(CloverSummary, { summary: intl, ...rest });
2370
3210
  }
2371
3211
  function Metadata({ manifest, metadata, ...rest }) {
2372
3212
  const items = ensureMetadata(metadata || manifest && manifest.metadata);
2373
3213
  if (!items.length) return null;
2374
- return /* @__PURE__ */ React29.createElement(CloverMetadata, { metadata: items, ...rest });
3214
+ return /* @__PURE__ */ React32.createElement(CloverMetadata, { metadata: items, ...rest });
2375
3215
  }
2376
3216
  function RequiredStatement({ manifest, requiredStatement, ...rest }) {
2377
3217
  const stmt = requiredStatement || manifest && manifest.requiredStatement;
2378
3218
  if (!stmt || !hasInternationalValue(stmt.label) || !hasInternationalValue(stmt.value)) {
2379
3219
  return null;
2380
3220
  }
2381
- return /* @__PURE__ */ React29.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
3221
+ return /* @__PURE__ */ React32.createElement(CloverRequiredStatement, { requiredStatement: stmt, ...rest });
2382
3222
  }
2383
3223
 
2384
3224
  // ui/src/docs/CodeBlock.jsx
2385
- import React30 from "react";
3225
+ import React33 from "react";
2386
3226
  function parseHighlightAttr(attr) {
2387
3227
  if (!attr) return /* @__PURE__ */ new Set();
2388
3228
  const cleaned = String(attr || "").trim();
@@ -2428,10 +3268,10 @@ var highlightBaseStyle = {
2428
3268
  };
2429
3269
  function DocsCodeBlock(props = {}) {
2430
3270
  const { children, ...rest } = props;
2431
- const childArray = React30.Children.toArray(children);
2432
- const codeElement = childArray.find((el) => React30.isValidElement(el));
3271
+ const childArray = React33.Children.toArray(children);
3272
+ const codeElement = childArray.find((el) => React33.isValidElement(el));
2433
3273
  if (!codeElement || !codeElement.props) {
2434
- return React30.createElement("pre", props);
3274
+ return React33.createElement("pre", props);
2435
3275
  }
2436
3276
  const {
2437
3277
  className = "",
@@ -2446,8 +3286,8 @@ function DocsCodeBlock(props = {}) {
2446
3286
  const highlightSet = parseHighlightAttr(highlightAttr);
2447
3287
  const copyAttr = codeProps["data-copy"];
2448
3288
  const enableCopy = copyAttr !== void 0 ? copyAttr === true || copyAttr === "true" || copyAttr === "" : false;
2449
- const [copied, setCopied] = React30.useState(false);
2450
- const handleCopy = React30.useCallback(async () => {
3289
+ const [copied, setCopied] = React33.useState(false);
3290
+ const handleCopy = React33.useCallback(async () => {
2451
3291
  const text = rawCode;
2452
3292
  try {
2453
3293
  if (typeof navigator !== "undefined" && navigator.clipboard && navigator.clipboard.writeText) {
@@ -2512,20 +3352,20 @@ function DocsCodeBlock(props = {}) {
2512
3352
  const highlight = highlightSet.has(lineNumber);
2513
3353
  const style = highlight ? { ...baseLineStyle, ...highlightBaseStyle } : baseLineStyle;
2514
3354
  const displayLine = line === "" ? " " : line;
2515
- return React30.createElement(
3355
+ return React33.createElement(
2516
3356
  "span",
2517
3357
  { key: lineNumber, style },
2518
- React30.createElement("span", { style: lineContentStyle }, displayLine)
3358
+ React33.createElement("span", { style: lineContentStyle }, displayLine)
2519
3359
  );
2520
3360
  });
2521
- return React30.createElement(
3361
+ return React33.createElement(
2522
3362
  "div",
2523
3363
  { style: containerStyle },
2524
- showHeader ? React30.createElement(
3364
+ showHeader ? React33.createElement(
2525
3365
  "div",
2526
3366
  { style: headerStyle },
2527
- React30.createElement("span", null, showFilename ? filename : null),
2528
- enableCopy ? React30.createElement(
3367
+ React33.createElement("span", null, showFilename ? filename : null),
3368
+ enableCopy ? React33.createElement(
2529
3369
  "button",
2530
3370
  {
2531
3371
  type: "button",
@@ -2544,29 +3384,29 @@ function DocsCodeBlock(props = {}) {
2544
3384
  copied ? "Copied" : "Copy"
2545
3385
  ) : null
2546
3386
  ) : null,
2547
- React30.createElement(
3387
+ React33.createElement(
2548
3388
  "pre",
2549
3389
  { ...preRest, className: preClassName, style: mergedPreStyle },
2550
- React30.createElement("code", { style: codeStyle }, lineElements)
3390
+ React33.createElement("code", { style: codeStyle }, lineElements)
2551
3391
  )
2552
3392
  );
2553
3393
  }
2554
3394
 
2555
3395
  // ui/src/docs/MarkdownTable.jsx
2556
- import React31 from "react";
3396
+ import React34 from "react";
2557
3397
  function MarkdownTable({ className = "", ...rest }) {
2558
3398
  const merged = ["markdown-table", className].filter(Boolean).join(" ");
2559
- return /* @__PURE__ */ React31.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React31.createElement("table", { className: merged, ...rest }));
3399
+ return /* @__PURE__ */ React34.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React34.createElement("table", { className: merged, ...rest }));
2560
3400
  }
2561
3401
 
2562
3402
  // ui/src/docs/Diagram.jsx
2563
- import React32 from "react";
3403
+ import React35 from "react";
2564
3404
  function CanopyDiagram() {
2565
- return /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React32.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Collection A"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React32.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React32.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Collection B"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React32.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React32.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React32.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Automated content"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React32.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React32.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React32.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React32.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Search index"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React32.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React32.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React32.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Work pages"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React32.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React32.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React32.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React32.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React32.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React32.createElement("li", null, "Optional annotation dataset"))))));
3405
+ return /* @__PURE__ */ React35.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React35.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React35.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React35.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Collection A"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React35.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React35.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Collection B"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React35.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React35.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React35.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React35.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React35.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React35.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Automated content"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React35.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React35.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React35.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React35.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Search index"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React35.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React35.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React35.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React35.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React35.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React35.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React35.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Work pages"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React35.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React35.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React35.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React35.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React35.createElement("article", null, /* @__PURE__ */ React35.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React35.createElement("ul", null, /* @__PURE__ */ React35.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React35.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React35.createElement("li", null, "Optional annotation dataset"))))));
2566
3406
  }
2567
3407
 
2568
3408
  // ui/src/docs/ThemeShowcase.jsx
2569
- import React33 from "react";
3409
+ import React36 from "react";
2570
3410
 
2571
3411
  // ../../node_modules/@radix-ui/colors/index.mjs
2572
3412
  var colors_exports = {};
@@ -6399,21 +7239,21 @@ var ACCENT_COLOR_NAMES = [
6399
7239
  "sky"
6400
7240
  ];
6401
7241
  var GRAY_COLOR_NAMES = ["gray", "mauve", "slate", "sage", "olive", "sand"];
6402
- var Section = ({ title, description, children }) => /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React33.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React33.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
6403
- var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React33.createElement("strong", null, label)), /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React33.createElement(
7242
+ var Section = ({ title, description, children }) => /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase__section" }, /* @__PURE__ */ React36.createElement("h3", { className: "canopy-theme-showcase__section-title" }, title), description ? /* @__PURE__ */ React36.createElement("p", { className: "canopy-theme-showcase__section-description" }, description) : null, children);
7243
+ var ColorScaleRow = ({ label, prefix }) => /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase__scale-row" }, /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase__scale-label" }, /* @__PURE__ */ React36.createElement("strong", null, label)), /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase__scale-track" }, COLOR_STOPS.map((stop) => /* @__PURE__ */ React36.createElement(
6404
7244
  "div",
6405
7245
  {
6406
7246
  key: `${label}-${stop}`,
6407
7247
  className: "canopy-theme-showcase__scale-stop"
6408
7248
  },
6409
- /* @__PURE__ */ React33.createElement(
7249
+ /* @__PURE__ */ React36.createElement(
6410
7250
  "span",
6411
7251
  {
6412
7252
  className: "canopy-theme-showcase__scale-chip",
6413
7253
  style: { backgroundColor: `var(${prefix}-${stop})` }
6414
7254
  }
6415
7255
  ),
6416
- /* @__PURE__ */ React33.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
7256
+ /* @__PURE__ */ React36.createElement("span", { className: "canopy-theme-showcase__scale-token" }, stop)
6417
7257
  ))));
6418
7258
  function ThemeShowcase() {
6419
7259
  const accentColors = ACCENT_COLOR_NAMES;
@@ -6424,15 +7264,15 @@ function ThemeShowcase() {
6424
7264
  if (!scale) return null;
6425
7265
  return scale[`${name}9`] || Object.values(scale)[8];
6426
7266
  };
6427
- const ColorsLabeled = ({ colors }) => /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
7267
+ const ColorsLabeled = ({ colors }) => /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase__swatch-grid" }, colors.map((name) => {
6428
7268
  const colorValue = getRadixSwatch(name);
6429
- return /* @__PURE__ */ React33.createElement("div", { key: name, className: "canopy-theme-showcase__swatch" }, /* @__PURE__ */ React33.createElement(
7269
+ return /* @__PURE__ */ React36.createElement("div", { key: name, className: "canopy-theme-showcase__swatch" }, /* @__PURE__ */ React36.createElement(
6430
7270
  "div",
6431
7271
  {
6432
7272
  className: "canopy-theme-showcase__swatch-chip",
6433
7273
  style: { background: colorValue || "var(--color-gray-200)" }
6434
7274
  }
6435
- ), /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase__swatch-label" }, name));
7275
+ ), /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase__swatch-label" }, name));
6436
7276
  }));
6437
7277
  const styles = `
6438
7278
  .canopy-theme-showcase__section {
@@ -6516,13 +7356,13 @@ function ThemeShowcase() {
6516
7356
  font-weight: 300;
6517
7357
  }
6518
7358
  `;
6519
- return /* @__PURE__ */ React33.createElement("div", { className: "canopy-theme-showcase" }, /* @__PURE__ */ React33.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React33.createElement(
7359
+ return /* @__PURE__ */ React36.createElement("div", { className: "canopy-theme-showcase" }, /* @__PURE__ */ React36.createElement("style", { dangerouslySetInnerHTML: { __html: styles } }), /* @__PURE__ */ React36.createElement(
6520
7360
  Section,
6521
7361
  {
6522
7362
  title: "Color scales",
6523
7363
  description: "Accent and gray ramps from the active theme."
6524
7364
  },
6525
- /* @__PURE__ */ React33.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React33.createElement(
7365
+ /* @__PURE__ */ React36.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem" } }, COLOR_SCALES.map((scale) => /* @__PURE__ */ React36.createElement(
6526
7366
  ColorScaleRow,
6527
7367
  {
6528
7368
  key: scale.label,
@@ -6530,20 +7370,20 @@ function ThemeShowcase() {
6530
7370
  prefix: scale.prefix
6531
7371
  }
6532
7372
  )))
6533
- ), /* @__PURE__ */ React33.createElement(
7373
+ ), /* @__PURE__ */ React36.createElement(
6534
7374
  Section,
6535
7375
  {
6536
7376
  title: "Accent palette options",
6537
7377
  description: "All accent color families available via Radix Themes."
6538
7378
  },
6539
- /* @__PURE__ */ React33.createElement(ColorsLabeled, { colors: accentColors })
6540
- ), /* @__PURE__ */ React33.createElement(
7379
+ /* @__PURE__ */ React36.createElement(ColorsLabeled, { colors: accentColors })
7380
+ ), /* @__PURE__ */ React36.createElement(
6541
7381
  Section,
6542
7382
  {
6543
7383
  title: "Gray palette options",
6544
7384
  description: "Neutral ramps you can assign via the theme block."
6545
7385
  },
6546
- /* @__PURE__ */ React33.createElement(ColorsLabeled, { colors: grayColors })
7386
+ /* @__PURE__ */ React36.createElement(ColorsLabeled, { colors: grayColors })
6547
7387
  ));
6548
7388
  }
6549
7389
  export {
@@ -6578,7 +7418,10 @@ export {
6578
7418
  Slider,
6579
7419
  SubNavigation,
6580
7420
  Summary,
7421
+ TeaserCard,
6581
7422
  ThemeShowcase,
7423
+ MdxTimeline as Timeline,
7424
+ TimelinePoint,
6582
7425
  Viewer
6583
7426
  };
6584
7427
  //# sourceMappingURL=server.mjs.map