ardo 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/{DocPage-B0Fr09xS.js → DocPage-Dy7OrCP2.js} +21 -19
  2. package/dist/DocPage-Dy7OrCP2.js.map +1 -0
  3. package/dist/assets/src/ui/{DocPage.css.ts.vanilla-BzG5Z7uL.css → DocPage.css.ts.vanilla-CWL92vUE.css} +8 -19
  4. package/dist/assets/src/ui/ErrorBoundary.css.ts.vanilla-C4usIU4z.css +112 -0
  5. package/dist/assets/src/ui/{Footer.css.ts.vanilla-YkYGSrJ5.css → Footer.css.ts.vanilla-DGTyff5Y.css} +39 -17
  6. package/dist/assets/src/ui/{Header.css.ts.vanilla-DoHPoq3b.css → Header.css.ts.vanilla-DEcLj8r0.css} +27 -45
  7. package/dist/assets/src/ui/{Layout.css.ts.vanilla-C7TVummJ.css → Layout.css.ts.vanilla-ClOa1YZm.css} +0 -3
  8. package/dist/assets/src/ui/{Sidebar.css.ts.vanilla-PyKaHp0J.css → Sidebar.css.ts.vanilla-IxNEQEBv.css} +57 -31
  9. package/dist/assets/src/ui/{Toc.css.ts.vanilla-Py3sLE1P.css → Toc.css.ts.vanilla-CQbpEdTg.css} +5 -5
  10. package/dist/assets/src/ui/components/{CodeBlock.css.ts.vanilla-DL4KE8dp.css → CodeBlock.css.ts.vanilla-BxDJ2gKc.css} +14 -12
  11. package/dist/assets/src/ui/components/{CopyButton.css.ts.vanilla-DSjVT6G0.css → CopyButton.css.ts.vanilla-CO2awD6S.css} +4 -5
  12. package/dist/assets/src/ui/components/HeaderSearch.css.ts.vanilla-KAo_Mlc-.css +68 -0
  13. package/dist/assets/src/ui/components/{Search.css.ts.vanilla-CX2EM6hW.css → Search.css.ts.vanilla-NQZH1eLo.css} +17 -6
  14. package/dist/assets/src/ui/theme/{dark.css.ts.vanilla-C40AfyaZ.css → dark.css.ts.vanilla-CQef5pk2.css} +11 -7
  15. package/dist/assets/src/ui/theme/{light.css.ts.vanilla-D0oxKOX2.css → light.css.ts.vanilla-D8gxaS1c.css} +11 -7
  16. package/dist/{brand-icons-DLJKqTun.js → brand-icons-Di8w0Nu9.js} +1 -1
  17. package/dist/{brand-icons-DLJKqTun.js.map → brand-icons-Di8w0Nu9.js.map} +1 -1
  18. package/dist/config/index.d.ts +1 -1
  19. package/dist/{contract.css-ZxmDpwAM.d.ts → contract.css-qPyk_asd.d.ts} +5 -1
  20. package/dist/contract.css-qPyk_asd.d.ts.map +1 -0
  21. package/dist/{generator-DX0PP1xf.js → generator-CYSyo4Vz.js} +7 -6
  22. package/dist/generator-CYSyo4Vz.js.map +1 -0
  23. package/dist/icons/index.js +1 -1
  24. package/dist/{index-CWfXolzQ.d.ts → index-BcekgOfA.d.ts} +68 -7
  25. package/dist/index-BcekgOfA.d.ts.map +1 -0
  26. package/dist/{index-BNKprp9_.d.ts → index-CuMTHUxX.d.ts} +8 -2
  27. package/dist/index-CuMTHUxX.d.ts.map +1 -0
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +3 -3
  30. package/dist/mdx/provider.js +1 -1
  31. package/dist/runtime/index.d.ts +1 -1
  32. package/dist/runtime/index.js +1 -1
  33. package/dist/{sidebar-utils-1Skqle1Q.js → sidebar-utils-C06DJsx4.js} +15 -4
  34. package/dist/sidebar-utils-C06DJsx4.js.map +1 -0
  35. package/dist/theme/index.d.ts +16 -4
  36. package/dist/theme/index.d.ts.map +1 -1
  37. package/dist/theme/index.js +46 -23
  38. package/dist/theme/index.js.map +1 -1
  39. package/dist/typedoc/components/index.d.ts +1 -1
  40. package/dist/typedoc/index.d.ts +1 -1
  41. package/dist/typedoc/index.d.ts.map +1 -1
  42. package/dist/typedoc/index.js +1 -1
  43. package/dist/{types-Do3OQY8G.d.ts → types-B75OhnGa.d.ts} +13 -2
  44. package/dist/types-B75OhnGa.d.ts.map +1 -0
  45. package/dist/{types-DvGYhH63.d.ts → types-Ck2Vm7NB.d.ts} +2 -2
  46. package/dist/{types-DvGYhH63.d.ts.map → types-Ck2Vm7NB.d.ts.map} +1 -1
  47. package/dist/ui/index.d.ts +2 -2
  48. package/dist/ui/index.js +3 -3
  49. package/dist/ui/styles.css +342 -306
  50. package/dist/ui/styles.js +11 -11
  51. package/dist/{ui-BzUIfYJm.js → ui-AGPGBunC.js} +584 -276
  52. package/dist/ui-AGPGBunC.js.map +1 -0
  53. package/dist/vite/index.d.ts +1 -1
  54. package/dist/vite/index.d.ts.map +1 -1
  55. package/dist/vite/index.js +165 -24
  56. package/dist/vite/index.js.map +1 -1
  57. package/package.json +13 -13
  58. package/dist/DocPage-B0Fr09xS.js.map +0 -1
  59. package/dist/contract.css-ZxmDpwAM.d.ts.map +0 -1
  60. package/dist/generator-DX0PP1xf.js.map +0 -1
  61. package/dist/index-BNKprp9_.d.ts.map +0 -1
  62. package/dist/index-CWfXolzQ.d.ts.map +0 -1
  63. package/dist/sidebar-utils-1Skqle1Q.js.map +0 -1
  64. package/dist/types-Do3OQY8G.d.ts.map +0 -1
  65. package/dist/ui-BzUIfYJm.js.map +0 -1
@@ -1,66 +1,230 @@
1
- import { a as ArdoProvider, l as useArdoPageData, o as ArdoSiteConfigProvider, s as useArdoConfig, u as useArdoSidebar } from "./sidebar-utils-1Skqle1Q.js";
2
- import { c as TwitterIcon, i as LinkedinIcon, n as GithubIcon, o as NpmIcon, u as YoutubeIcon } from "./brand-icons-DLJKqTun.js";
3
- import { $ as SunIcon, A as home, F as footerContainer, G as FileCodeIcon, H as BoxIcon, J as MonitorIcon, K as FileTextIcon, L as footerLink, M as layout, O as ArdoLayout, Q as SettingsIcon, U as ChevronDownIcon, V as BookOpenIcon, W as CodeIcon, X as PackageIcon, Y as MoonIcon, Z as SearchIcon, et as WrenchIcon, j as homeMain, q as MessageCircleIcon, tt as XIcon, z as footerPrimary } from "./DocPage-B0Fr09xS.js";
1
+ import { a as ArdoProvider, d as useArdoSidebar, l as useArdoContexts, o as ArdoSiteConfigProvider, s as useArdoConfig, u as useArdoPageData } from "./sidebar-utils-C06DJsx4.js";
2
+ import { $ as footerLink, A as home, B as MonitorIcon, F as ChevronDownIcon, G as SunIcon, H as PackageIcon, I as CodeIcon, K as WrenchIcon, L as FileCodeIcon, M as layout, N as BookOpenIcon, O as ArdoLayout, P as BoxIcon, R as FileTextIcon, U as SearchIcon, V as MoonIcon, W as SettingsIcon, Y as footerArdoLink, Z as footerContainer, j as homeMain, nt as footerPrimary, q as XIcon, tt as footerOwl, z as MessageCircleIcon } from "./DocPage-Dy7OrCP2.js";
3
+ import { c as TwitterIcon, i as LinkedinIcon, n as GithubIcon, o as NpmIcon, u as YoutubeIcon } from "./brand-icons-Di8w0Nu9.js";
4
4
  import React, { Children, cloneElement, createContext, isValidElement, use, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
- import { Link, NavLink, Outlet, useLocation, useNavigate } from "react-router";
6
+ import { Link, NavLink, Outlet, isRouteErrorResponse, useLocation, useMatches, useNavigate, useRouteError } from "react-router";
7
+ import "./assets/src/ui/components/HeaderSearch.css.ts.vanilla-KAo_Mlc-.css";
7
8
  import MiniSearch from "minisearch";
8
9
  import searchDocs from "virtual:ardo/search-index";
9
- import "./assets/src/ui/components/Search.css.ts.vanilla-CX2EM6hW.css";
10
+ import "./assets/src/ui/components/Search.css.ts.vanilla-NQZH1eLo.css";
10
11
  import { createPortal } from "react-dom";
11
12
  import "./assets/src/ui/components/ThemeToggle.css.ts.vanilla---sSUELC.css";
12
- import "./assets/src/ui/Header.css.ts.vanilla-DoHPoq3b.css";
13
+ import "./assets/src/ui/Header.css.ts.vanilla-DEcLj8r0.css";
13
14
  import "./assets/src/ui/Nav.css.ts.vanilla-CsAQjogy.css";
14
- import "./assets/src/ui/Sidebar.css.ts.vanilla-PyKaHp0J.css";
15
+ import "./assets/src/ui/Sidebar.css.ts.vanilla-IxNEQEBv.css";
15
16
  import "./assets/src/ui/theme/animations.css.ts.vanilla-D6ImVUKy.css";
16
17
  import "./assets/src/ui/components/Features.css.ts.vanilla-ggYasCFy.css";
17
18
  import "./assets/src/ui/components/Hero.css.ts.vanilla-DY_BH0W6.css";
18
- //#region src/ui/components/Search.css.ts
19
- var search = "wxcdiv0";
20
- var searchField = "wxcdiv1";
21
- var searchFooter = "wxcdiv9";
22
- var searchInput = "wxcdiv2";
23
- var searchKbd = "wxcdivb";
24
- var searchNoResults = "wxcdiv8";
25
- var searchPopover = "wxcdiv3";
26
- var searchResult = "wxcdiv5";
27
- var searchResultTitle = "wxcdiv6";
28
- var searchResults = "wxcdiv4";
29
- //#endregion
30
- //#region src/ui/components/SearchPopover.tsx
19
+ import "./assets/src/ui/ErrorBoundary.css.ts.vanilla-C4usIU4z.css";
20
+ //#region src/ui/OwlMark.tsx
31
21
  /**
32
- * Renders the search popover as a portal attached to document.body.
33
- * Uses getBoundingClientRect to position it below the anchor element.
22
+ * Decorative line-art owl, drawn in `currentColor`. Used by the default
23
+ * error and empty states. Set `color` on the parent (or `style.color`)
24
+ * to recolor; set `title` to give it accessible meaning.
34
25
  */
35
- function SearchPopover({ anchorRef, children }) {
36
- const [pos, setPos] = useState({
37
- top: 0,
38
- left: 0,
39
- width: 0
40
- });
41
- useLayoutEffect(() => {
42
- const el = anchorRef.current;
43
- if (!el) return;
44
- const rect = el.getBoundingClientRect();
45
- setPos({
46
- top: rect.bottom + 8,
47
- left: rect.left,
48
- width: Math.max(rect.width, 400)
49
- });
50
- }, [anchorRef]);
51
- if (typeof document === "undefined") return null;
52
- return createPortal(/* @__PURE__ */ jsx("div", {
53
- className: searchPopover,
26
+ function ArdoOwlMark({ size = 96, title, ...props }) {
27
+ return /* @__PURE__ */ jsxs("svg", {
28
+ xmlns: "http://www.w3.org/2000/svg",
29
+ viewBox: "0 0 600 600",
30
+ width: size,
31
+ height: size,
54
32
  style: {
55
- top: pos.top,
56
- left: pos.left,
57
- width: Math.min(pos.width, globalThis.innerWidth - 32)
33
+ strokeLinecap: "round",
34
+ strokeLinejoin: "round"
58
35
  },
59
- children
60
- }), document.body);
36
+ role: title == null ? "presentation" : "img",
37
+ "aria-hidden": title == null ? true : void 0,
38
+ ...props,
39
+ children: [
40
+ title == null ? null : /* @__PURE__ */ jsx("title", { children: title }),
41
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("symbol", {
42
+ id: "ardo-owl-eye",
43
+ overflow: "visible",
44
+ children: [
45
+ /* @__PURE__ */ jsx("path", { d: "M300 300 151 128l2 178-41 94h93c-35 32-55 68-63 107m63-106 95 81m-32-96 28 88" }),
46
+ /* @__PURE__ */ jsx("ellipse", {
47
+ cx: "222",
48
+ cy: "327",
49
+ rx: "20",
50
+ ry: "33",
51
+ fill: "currentColor",
52
+ opacity: "0.6"
53
+ }),
54
+ /* @__PURE__ */ jsx("circle", {
55
+ cx: "227",
56
+ cy: "324",
57
+ r: "71"
58
+ })
59
+ ]
60
+ }) }),
61
+ /* @__PURE__ */ jsxs("g", {
62
+ fill: "none",
63
+ stroke: "currentColor",
64
+ strokeWidth: "16",
65
+ children: [
66
+ /* @__PURE__ */ jsx("path", { d: "M155 318c2-70 66-126 145-126s143 56 145 126" }),
67
+ /* @__PURE__ */ jsx("circle", {
68
+ cx: "300",
69
+ cy: "290",
70
+ r: "270"
71
+ }),
72
+ /* @__PURE__ */ jsx("use", { href: "#ardo-owl-eye" }),
73
+ /* @__PURE__ */ jsx("use", {
74
+ href: "#ardo-owl-eye",
75
+ transform: "matrix(-1 0 0 1 600 0)"
76
+ })
77
+ ]
78
+ })
79
+ ]
80
+ });
61
81
  }
62
82
  //#endregion
63
- //#region src/ui/components/Search.tsx
83
+ //#region src/ui/Footer.tsx
84
+ function formatBuildTime(iso) {
85
+ try {
86
+ return new Date(iso).toLocaleDateString("en-US", {
87
+ month: "long",
88
+ day: "numeric",
89
+ year: "numeric"
90
+ });
91
+ } catch {
92
+ return iso;
93
+ }
94
+ }
95
+ /**
96
+ * Footer component with structured layout for project info, sponsor, and build metadata.
97
+ *
98
+ * Automatically pulls data from Ardo context (`config.project`, `config.buildTime`,
99
+ * `config.buildHash`). Props serve as overrides.
100
+ *
101
+ * When `children` is provided, all automatic rendering is skipped.
102
+ *
103
+ * @example Automatic (zero-config)
104
+ * ```tsx
105
+ * <Footer />
106
+ * ```
107
+ *
108
+ * @example With overrides
109
+ * ```tsx
110
+ * <Footer
111
+ * sponsor={{ text: "Sebastian Software", link: "https://sebastian-software.com/oss" }}
112
+ * message="Released under the MIT License."
113
+ * copyright="Copyright 2026 Sebastian Software GmbH"
114
+ * />
115
+ * ```
116
+ *
117
+ * @example Custom content
118
+ * ```tsx
119
+ * <Footer>
120
+ * <CustomFooterContent />
121
+ * </Footer>
122
+ * ```
123
+ */
124
+ function FooterProjectLink({ project, config }) {
125
+ const resolved = project ?? config.project;
126
+ const name = resolved?.name ?? "";
127
+ const version = resolved?.version ?? "";
128
+ const homepage = resolved?.homepage ?? "";
129
+ if (name === "") return null;
130
+ const label = version !== "" ? `${name} v${version}` : name;
131
+ return homepage !== "" ? /* @__PURE__ */ jsx("a", {
132
+ href: homepage,
133
+ className: footerLink,
134
+ children: label
135
+ }) : /* @__PURE__ */ jsx("span", { children: label });
136
+ }
137
+ function FooterSponsorLink({ sponsor }) {
138
+ const text = sponsor?.text ?? "";
139
+ const link = sponsor?.link ?? "";
140
+ if (text === "" || link === "") return null;
141
+ return /* @__PURE__ */ jsxs("a", {
142
+ href: link,
143
+ className: footerLink,
144
+ children: ["Sponsored by ", text]
145
+ });
146
+ }
147
+ function FooterPrimaryLine({ project, sponsor, ardoLink, config }) {
148
+ const items = [];
149
+ const projectNode = /* @__PURE__ */ jsx(FooterProjectLink, {
150
+ project,
151
+ config
152
+ });
153
+ const sponsorNode = /* @__PURE__ */ jsx(FooterSponsorLink, { sponsor });
154
+ const hasProject = (project ?? config.project)?.name !== void 0 && (project ?? config.project)?.name !== "";
155
+ const hasSponsor = (sponsor?.text ?? "") !== "" && (sponsor?.link ?? "") !== "";
156
+ if (hasProject) items.push(projectNode);
157
+ if (ardoLink) items.push(/* @__PURE__ */ jsxs("a", {
158
+ href: "https://ardo-docs.dev",
159
+ className: footerArdoLink,
160
+ children: [/* @__PURE__ */ jsx(ArdoOwlMark, {
161
+ size: 16,
162
+ className: footerOwl,
163
+ title: ""
164
+ }), "Built with Ardo"]
165
+ }));
166
+ if (hasSponsor) items.push(sponsorNode);
167
+ if (items.length === 0) return null;
168
+ return /* @__PURE__ */ jsx("p", {
169
+ className: footerPrimary,
170
+ children: items.map((item, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("span", {
171
+ className: "_169q00b3",
172
+ "aria-hidden": "true"
173
+ }), item] }, i))
174
+ });
175
+ }
176
+ function ArdoFooter({ children, className, ardoLink = true, message, copyright, project, sponsor, buildTime, buildHash }) {
177
+ const config = useArdoConfig();
178
+ const resolvedBuildTime = buildTime ?? config.buildTime;
179
+ const resolvedBuildHash = buildHash ?? config.buildHash;
180
+ if (children != null) return /* @__PURE__ */ jsx("footer", {
181
+ className: className ?? "_169q00b0",
182
+ children: /* @__PURE__ */ jsx("div", {
183
+ className: footerContainer,
184
+ children
185
+ })
186
+ });
187
+ return /* @__PURE__ */ jsx("footer", {
188
+ className: className ?? "_169q00b0",
189
+ children: /* @__PURE__ */ jsxs("div", {
190
+ className: footerContainer,
191
+ children: [
192
+ /* @__PURE__ */ jsx(FooterPrimaryLine, {
193
+ project,
194
+ sponsor,
195
+ ardoLink,
196
+ config
197
+ }),
198
+ (message ?? "") !== "" && /* @__PURE__ */ jsx("p", {
199
+ className: "_169q00b7",
200
+ dangerouslySetInnerHTML: { __html: message ?? "" }
201
+ }),
202
+ (copyright ?? "") !== "" && /* @__PURE__ */ jsx("p", {
203
+ className: "_169q00b8",
204
+ dangerouslySetInnerHTML: { __html: copyright ?? "" }
205
+ }),
206
+ (resolvedBuildTime ?? "") !== "" && /* @__PURE__ */ jsxs("p", {
207
+ className: "_169q00b9",
208
+ children: [
209
+ "Built on ",
210
+ formatBuildTime(resolvedBuildTime ?? ""),
211
+ (resolvedBuildHash ?? "") !== "" && /* @__PURE__ */ jsxs(Fragment, { children: [
212
+ " (",
213
+ resolvedBuildHash,
214
+ ")"
215
+ ] })
216
+ ]
217
+ })
218
+ ]
219
+ })
220
+ });
221
+ }
222
+ //#endregion
223
+ //#region src/ui/components/HeaderSearch.css.ts
224
+ var inline = "xapf7e0";
225
+ var trigger = "xapf7e1";
226
+ //#endregion
227
+ //#region src/ui/components/search-hooks.ts
64
228
  function useGlobalSearchShortcut(inputRef, setIsOpen) {
65
229
  useEffect(() => {
66
230
  const handleGlobalKeyDown = (e) => {
@@ -100,6 +264,54 @@ function useOutsideClick({ containerRef, popoverClass, isOpen, setIsOpen }) {
100
264
  setIsOpen
101
265
  ]);
102
266
  }
267
+ //#endregion
268
+ //#region src/ui/components/Search.css.ts
269
+ var search = "wxcdiv0";
270
+ var searchField = "wxcdiv1";
271
+ var searchFooter = "wxcdiva";
272
+ var searchInput = "wxcdiv2";
273
+ var searchKbd = "wxcdivc";
274
+ var searchNoResults = "wxcdiv8";
275
+ var searchNoResultsOwl = "wxcdiv9";
276
+ var searchPopover = "wxcdiv3";
277
+ var searchResult = "wxcdiv5";
278
+ var searchResultTitle = "wxcdiv6";
279
+ var searchResults = "wxcdiv4";
280
+ //#endregion
281
+ //#region src/ui/components/SearchPopover.tsx
282
+ /**
283
+ * Renders the search popover as a portal attached to document.body.
284
+ * Uses getBoundingClientRect to position it below the anchor element.
285
+ */
286
+ function SearchPopover({ anchorRef, children }) {
287
+ const [pos, setPos] = useState({
288
+ top: 0,
289
+ left: 0,
290
+ width: 0
291
+ });
292
+ useLayoutEffect(() => {
293
+ const el = anchorRef.current;
294
+ if (!el) return;
295
+ const rect = el.getBoundingClientRect();
296
+ setPos({
297
+ top: rect.bottom + 8,
298
+ left: rect.left,
299
+ width: Math.max(rect.width, 400)
300
+ });
301
+ }, [anchorRef]);
302
+ if (typeof document === "undefined") return null;
303
+ return createPortal(/* @__PURE__ */ jsx("div", {
304
+ className: searchPopover,
305
+ style: {
306
+ top: pos.top,
307
+ left: pos.left,
308
+ width: Math.min(pos.width, globalThis.innerWidth - 32)
309
+ },
310
+ children
311
+ }), document.body);
312
+ }
313
+ //#endregion
314
+ //#region src/ui/components/Search.tsx
103
315
  function useSearchIndex() {
104
316
  return useMemo(() => {
105
317
  const index = new MiniSearch({
@@ -153,11 +365,15 @@ function SearchResults({ results, selectedIndex, query, onClose }) {
153
365
  }) }, result.id))
154
366
  }) : /* @__PURE__ */ jsxs("div", {
155
367
  className: searchNoResults,
156
- children: [
368
+ children: [/* @__PURE__ */ jsx(ArdoOwlMark, {
369
+ size: 36,
370
+ className: searchNoResultsOwl,
371
+ title: ""
372
+ }), /* @__PURE__ */ jsxs("span", { children: [
157
373
  "No results found for \"",
158
374
  query,
159
375
  "\""
160
- ]
376
+ ] })]
161
377
  }), /* @__PURE__ */ jsxs("div", {
162
378
  className: searchFooter,
163
379
  children: [
@@ -219,7 +435,7 @@ function SearchInput({ inputRef, placeholder, query, hasQuery, onSearch, onKeyDo
219
435
  }),
220
436
  hasQuery && /* @__PURE__ */ jsx("button", {
221
437
  type: "button",
222
- className: "wxcdiva",
438
+ className: "wxcdivb",
223
439
  onClick: (e) => {
224
440
  e.stopPropagation();
225
441
  onSearch("");
@@ -235,12 +451,15 @@ function SearchInput({ inputRef, placeholder, query, hasQuery, onSearch, onKeyDo
235
451
  ]
236
452
  });
237
453
  }
238
- function ArdoSearch({ placeholder = "Search..." }) {
454
+ function ArdoSearch({ placeholder = "Search...", autoFocus = false }) {
239
455
  const navigate = useNavigate();
240
456
  const containerRef = useRef(null);
241
457
  const inputRef = useRef(null);
242
458
  const state = useSearch(useSearchIndex());
243
459
  const hasQuery = state.query.trim().length > 0;
460
+ useEffect(() => {
461
+ if (autoFocus) inputRef.current?.focus();
462
+ }, [autoFocus]);
244
463
  useGlobalSearchShortcut(inputRef, state.setIsOpen);
245
464
  useOutsideClick({
246
465
  containerRef,
@@ -307,140 +526,63 @@ function ArdoSearch({ placeholder = "Search..." }) {
307
526
  });
308
527
  }
309
528
  //#endregion
310
- //#region src/ui/Footer.tsx
311
- function formatBuildTime(iso) {
312
- try {
313
- return new Date(iso).toLocaleDateString("en-US", {
314
- month: "long",
315
- day: "numeric",
316
- year: "numeric"
317
- });
318
- } catch {
319
- return iso;
320
- }
321
- }
529
+ //#region src/ui/components/HeaderSearch.tsx
322
530
  /**
323
- * Footer component with structured layout for project info, sponsor, and build metadata.
324
- *
325
- * Automatically pulls data from Ardo context (`config.project`, `config.buildTime`,
326
- * `config.buildHash`). Props serve as overrides.
327
- *
328
- * When `children` is provided, all automatic rendering is skipped.
329
- *
330
- * @example Automatic (zero-config)
331
- * ```tsx
332
- * <Footer />
333
- * ```
334
- *
335
- * @example With overrides
336
- * ```tsx
337
- * <Footer
338
- * sponsor={{ text: "Sebastian Software", link: "https://sebastian-software.com/oss" }}
339
- * message="Released under the MIT License."
340
- * copyright="Copyright 2026 Sebastian Software GmbH"
341
- * />
342
- * ```
343
- *
344
- * @example Custom content
345
- * ```tsx
346
- * <Footer>
347
- * <CustomFooterContent />
348
- * </Footer>
349
- * ```
531
+ * Header-hosted search. On desktop it renders the search input inline; on
532
+ * narrow viewports it collapses to an icon button that opens a full-width
533
+ * search overlay.
350
534
  */
351
- function FooterProjectLink({ project, config }) {
352
- const resolved = project ?? config.project;
353
- const name = resolved?.name ?? "";
354
- const version = resolved?.version ?? "";
355
- const homepage = resolved?.homepage ?? "";
356
- if (name === "") return null;
357
- const label = version !== "" ? `${name} v${version}` : name;
358
- return homepage !== "" ? /* @__PURE__ */ jsx("a", {
359
- href: homepage,
360
- className: footerLink,
361
- children: label
362
- }) : /* @__PURE__ */ jsx("span", { children: label });
363
- }
364
- function FooterSponsorLink({ sponsor }) {
365
- const text = sponsor?.text ?? "";
366
- const link = sponsor?.link ?? "";
367
- if (text === "" || link === "") return null;
368
- return /* @__PURE__ */ jsxs("a", {
369
- href: link,
370
- className: footerLink,
371
- children: ["Sponsored by ", text]
372
- });
373
- }
374
- function FooterPrimaryLine({ project, sponsor, ardoLink, config }) {
375
- const items = [];
376
- const projectNode = /* @__PURE__ */ jsx(FooterProjectLink, {
377
- project,
378
- config
379
- });
380
- const sponsorNode = /* @__PURE__ */ jsx(FooterSponsorLink, { sponsor });
381
- const hasProject = (project ?? config.project)?.name !== void 0 && (project ?? config.project)?.name !== "";
382
- const hasSponsor = (sponsor?.text ?? "") !== "" && (sponsor?.link ?? "") !== "";
383
- if (hasProject) items.push(projectNode);
384
- if (ardoLink) items.push(/* @__PURE__ */ jsx("a", {
385
- href: "https://ardo-docs.dev",
386
- className: footerLink,
387
- children: "Built with Ardo"
388
- }));
389
- if (hasSponsor) items.push(sponsorNode);
390
- if (items.length === 0) return null;
391
- return /* @__PURE__ */ jsx("p", {
392
- className: footerPrimary,
393
- children: items.map((item, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("span", {
394
- className: "_169q00b3",
395
- "aria-hidden": "true"
396
- }), item] }, i))
397
- });
398
- }
399
- function ArdoFooter({ children, className, ardoLink = true, message, copyright, project, sponsor, buildTime, buildHash }) {
400
- const config = useArdoConfig();
401
- const resolvedBuildTime = buildTime ?? config.buildTime;
402
- const resolvedBuildHash = buildHash ?? config.buildHash;
403
- if (children != null) return /* @__PURE__ */ jsx("footer", {
404
- className: className ?? "_169q00b0",
405
- children: /* @__PURE__ */ jsx("div", {
406
- className: footerContainer,
407
- children
408
- })
409
- });
410
- return /* @__PURE__ */ jsx("footer", {
411
- className: className ?? "_169q00b0",
412
- children: /* @__PURE__ */ jsxs("div", {
413
- className: footerContainer,
414
- children: [
415
- /* @__PURE__ */ jsx(FooterPrimaryLine, {
416
- project,
417
- sponsor,
418
- ardoLink,
419
- config
420
- }),
421
- (message ?? "") !== "" && /* @__PURE__ */ jsx("p", {
422
- className: "_169q00b5",
423
- dangerouslySetInnerHTML: { __html: message ?? "" }
424
- }),
425
- (copyright ?? "") !== "" && /* @__PURE__ */ jsx("p", {
426
- className: "_169q00b6",
427
- dangerouslySetInnerHTML: { __html: copyright ?? "" }
428
- }),
429
- (resolvedBuildTime ?? "") !== "" && /* @__PURE__ */ jsxs("p", {
430
- className: "_169q00b7",
431
- children: [
432
- "Built on ",
433
- formatBuildTime(resolvedBuildTime ?? ""),
434
- (resolvedBuildHash ?? "") !== "" && /* @__PURE__ */ jsxs(Fragment, { children: [
435
- " (",
436
- resolvedBuildHash,
437
- ")"
438
- ] })
439
- ]
440
- })
441
- ]
535
+ function ArdoHeaderSearch({ placeholder }) {
536
+ const [overlayOpen, setOverlayOpen] = useState(false);
537
+ useEffect(() => {
538
+ setOverlayOpen(false);
539
+ }, [useLocation().pathname]);
540
+ useEffect(() => {
541
+ if (!overlayOpen) return;
542
+ const handleKey = (event) => {
543
+ if (event.key === "Escape") setOverlayOpen(false);
544
+ };
545
+ document.addEventListener("keydown", handleKey);
546
+ return () => {
547
+ document.removeEventListener("keydown", handleKey);
548
+ };
549
+ }, [overlayOpen]);
550
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
551
+ /* @__PURE__ */ jsx("div", {
552
+ className: inline,
553
+ children: /* @__PURE__ */ jsx(ArdoSearch, { placeholder })
554
+ }),
555
+ /* @__PURE__ */ jsx("button", {
556
+ type: "button",
557
+ className: trigger,
558
+ "aria-label": "Search",
559
+ onClick: () => {
560
+ setOverlayOpen(true);
561
+ },
562
+ children: /* @__PURE__ */ jsx(SearchIcon, { size: 20 })
563
+ }),
564
+ overlayOpen && /* @__PURE__ */ jsx("div", {
565
+ className: "xapf7e2",
566
+ children: /* @__PURE__ */ jsxs("div", {
567
+ className: "xapf7e3",
568
+ children: [/* @__PURE__ */ jsx("div", {
569
+ className: "xapf7e4",
570
+ children: /* @__PURE__ */ jsx(ArdoSearch, {
571
+ placeholder,
572
+ autoFocus: true
573
+ })
574
+ }), /* @__PURE__ */ jsx("button", {
575
+ type: "button",
576
+ className: "xapf7e5",
577
+ "aria-label": "Close search",
578
+ onClick: () => {
579
+ setOverlayOpen(false);
580
+ },
581
+ children: /* @__PURE__ */ jsx(XIcon, { size: 20 })
582
+ })]
583
+ })
442
584
  })
443
- });
585
+ ] });
444
586
  }
445
587
  //#endregion
446
588
  //#region src/ui/components/ThemeToggle.css.ts
@@ -517,8 +659,8 @@ function isTheme(value) {
517
659
  }
518
660
  var headerContainer = "qjc2r51";
519
661
  var headerLeft = "qjc2r52";
520
- var headerRight = "qjc2r53";
521
- var logoLink = "qjc2r54";
662
+ var headerRight = "qjc2r54";
663
+ var logoLink = "qjc2r55";
522
664
  var mobileBackdrop = "qjc2r5b";
523
665
  var mobilePanel = "qjc2r5c";
524
666
  var mobilePanelClose = "qjc2r5e";
@@ -526,42 +668,49 @@ var mobilePanelHeader = "qjc2r5d";
526
668
  var mobilePanelSidebar = "qjc2r5g";
527
669
  //#endregion
528
670
  //#region src/ui/Header.tsx
529
- function ArdoHeader({ logo: logo$2, title, nav, actions, search = false, searchPlaceholder, themeToggle = true, mobileMenuContent, className }) {
671
+ /**
672
+ * Mobile menu open state — resets on navigation and locks body scroll
673
+ * while open.
674
+ */
675
+ function useMobileMenu() {
530
676
  const location = useLocation();
531
- const config = useArdoConfig();
532
- const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
533
- const resolvedLogo = logo$2;
534
- const resolvedTitle = title ?? config.title;
535
- const hasLogo = resolvedLogo !== void 0;
536
- const hasTitle = resolvedTitle !== "";
537
- const hasMobileMenu = mobileMenuContent != null;
677
+ const [open, setOpen] = useState(false);
538
678
  useEffect(() => {
539
- setMobileMenuOpen(false);
679
+ setOpen(false);
540
680
  }, [location.pathname]);
541
681
  useEffect(() => {
542
- if (mobileMenuOpen) document.body.style.overflow = "hidden";
543
- else document.body.style.overflow = "";
682
+ document.body.style.overflow = open ? "hidden" : "";
544
683
  return () => {
545
684
  document.body.style.overflow = "";
546
685
  };
547
- }, [mobileMenuOpen]);
548
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("header", {
686
+ }, [open]);
687
+ return [open, setOpen];
688
+ }
689
+ function ArdoHeader({ logo: logo$2, title, nav, actions, search = true, searchPlaceholder, themeToggle = true, mobileMenuContent, className }) {
690
+ const config = useArdoConfig();
691
+ const [mobileMenuOpen, setMobileMenuOpen] = useMobileMenu();
692
+ const resolvedLogo = logo$2;
693
+ const resolvedTitle = title ?? config.title;
694
+ const hasLogo = resolvedLogo !== void 0;
695
+ const hasTitle = resolvedTitle !== "";
696
+ const hasMobileMenu = mobileMenuContent != null;
697
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("header", {
549
698
  className: className ?? "qjc2r50",
550
- children: [/* @__PURE__ */ jsxs("div", {
699
+ children: /* @__PURE__ */ jsxs("div", {
551
700
  className: headerContainer,
552
701
  children: [
553
702
  /* @__PURE__ */ jsxs("div", {
554
703
  className: headerLeft,
555
704
  children: [hasMobileMenu && /* @__PURE__ */ jsx("button", {
556
705
  type: "button",
557
- className: "qjc2r57",
706
+ className: "qjc2r58",
558
707
  onClick: () => {
559
708
  setMobileMenuOpen(!mobileMenuOpen);
560
709
  },
561
710
  "aria-label": "Toggle menu",
562
711
  "aria-expanded": mobileMenuOpen,
563
712
  children: /* @__PURE__ */ jsxs("span", {
564
- className: "qjc2r58",
713
+ className: "qjc2r59",
565
714
  children: [
566
715
  /* @__PURE__ */ jsx("span", {}),
567
716
  /* @__PURE__ */ jsx("span", {}),
@@ -574,30 +723,30 @@ function ArdoHeader({ logo: logo$2, title, nav, actions, search = false, searchP
574
723
  children: [hasLogo && /* @__PURE__ */ jsx("img", {
575
724
  src: typeof resolvedLogo === "string" ? resolvedLogo : resolvedLogo.light,
576
725
  alt: resolvedTitle,
577
- className: "qjc2r55"
726
+ className: "qjc2r56"
578
727
  }), hasTitle && /* @__PURE__ */ jsx("span", {
579
- className: "qjc2r56",
728
+ className: "qjc2r57",
580
729
  children: resolvedTitle
581
730
  })]
582
731
  })]
583
732
  }),
584
- nav != null && /* @__PURE__ */ jsx("div", {
585
- className: "qjc2r59",
586
- children: nav
733
+ search && /* @__PURE__ */ jsx("div", {
734
+ className: "qjc2r53",
735
+ children: /* @__PURE__ */ jsx(ArdoHeaderSearch, { placeholder: searchPlaceholder })
587
736
  }),
588
737
  /* @__PURE__ */ jsxs("div", {
589
738
  className: headerRight,
590
739
  children: [
591
- search && /* @__PURE__ */ jsx(ArdoSearch, { placeholder: searchPlaceholder }),
740
+ nav != null && /* @__PURE__ */ jsx("div", {
741
+ className: "qjc2r5a",
742
+ children: nav
743
+ }),
592
744
  themeToggle && /* @__PURE__ */ jsx(ArdoThemeToggle, {}),
593
745
  actions
594
746
  ]
595
747
  })
596
748
  ]
597
- }), nav != null && /* @__PURE__ */ jsx("div", {
598
- className: "qjc2r5a",
599
- children: nav
600
- })]
749
+ })
601
750
  }), hasMobileMenu && /* @__PURE__ */ jsx(MobileSlidePanel, {
602
751
  isOpen: mobileMenuOpen,
603
752
  logo: resolvedLogo,
@@ -629,7 +778,7 @@ function MobileSlidePanel({ isOpen, logo: logo$1, title, nav, themeToggle, child
629
778
  children: logo$1 != null && /* @__PURE__ */ jsx("img", {
630
779
  src: typeof logo$1 === "string" ? logo$1 : logo$1.light,
631
780
  alt: title,
632
- className: "qjc2r55"
781
+ className: "qjc2r56"
633
782
  })
634
783
  }), /* @__PURE__ */ jsxs("div", {
635
784
  style: {
@@ -686,19 +835,21 @@ function SocialIcon({ icon }) {
686
835
  const IconComponent = socialIcons[icon];
687
836
  return /* @__PURE__ */ jsx(IconComponent, { size: 20 });
688
837
  }
689
- var sidebarItem = "_1057ydna";
690
- var sidebarItemHeader = "_1057ydnb";
691
- var sidebarLink = "_1057ydnc";
692
- var sidebarList = "_1057ydn7";
693
- var sidebarList0 = "_1057ydn8";
694
- var sidebarList1 = "_1057ydn9";
695
- var sidebarNav = "_1057ydn6";
696
- var sidebarPanel = "_1057ydn4";
838
+ var sidebarItem = "_1057ydnc";
839
+ var sidebarItemHeader = "_1057ydne";
840
+ var sidebarLink = "_1057ydnf";
841
+ var sidebarList = "_1057ydn9";
842
+ var sidebarList0 = "_1057ydna";
843
+ var sidebarList1 = "_1057ydnb";
844
+ var sidebarNav = "_1057ydn8";
845
+ var sidebarPanel = "_1057ydn6";
697
846
  var sidebarRail = "_1057ydn1";
698
- var sidebarRailLink = "_1057ydn3";
699
- var sidebarRailList = "_1057ydn2";
700
- var sidebarText = "_1057ydnd";
701
- var sidebarTextButton = "_1057ydne";
847
+ var sidebarRailItem = "_1057ydn2";
848
+ var sidebarRailLabel = "_1057ydn3";
849
+ var sidebarRailLink = "_1057ydn5";
850
+ var sidebarRailList = "_1057ydn4";
851
+ var sidebarText = "_1057ydng";
852
+ var sidebarTextButton = "_1057ydnh";
702
853
  //#endregion
703
854
  //#region src/ui/SidebarRail.tsx
704
855
  function SidebarRail({ items }) {
@@ -708,21 +859,45 @@ function SidebarRail({ items }) {
708
859
  className: sidebarRail,
709
860
  children: /* @__PURE__ */ jsx("ul", {
710
861
  className: sidebarRailList,
711
- children: items.map((item, index) => /* @__PURE__ */ jsx("li", { children: item.to !== void 0 ? /* @__PURE__ */ jsx(NavLink, {
712
- to: item.to,
713
- title: item.label,
714
- "aria-label": item.label,
715
- className: ({ isActive }) => [sidebarRailLink, (item.active || isActive) && "active"].filter(Boolean).join(" "),
716
- children: resolveRailIcon(item, index)
717
- }) : /* @__PURE__ */ jsx("span", {
718
- className: sidebarRailLink,
719
- title: item.label,
720
- "aria-label": item.label,
721
- children: resolveRailIcon(item, index)
722
- }) }, item.key))
862
+ children: items.map((item, index) => /* @__PURE__ */ jsxs("li", {
863
+ className: sidebarRailItem,
864
+ children: [item.to !== void 0 ? /* @__PURE__ */ jsx(NavLink, {
865
+ to: item.to,
866
+ "aria-label": item.label,
867
+ className: ({ isActive }) => [sidebarRailLink, (item.active || isActive) && "active"].filter(Boolean).join(" "),
868
+ children: resolveRailIcon(item, index)
869
+ }) : /* @__PURE__ */ jsx("span", {
870
+ className: sidebarRailLink,
871
+ "aria-label": item.label,
872
+ children: resolveRailIcon(item, index)
873
+ }), /* @__PURE__ */ jsx("span", {
874
+ className: sidebarRailLabel,
875
+ "aria-hidden": "true",
876
+ children: item.label
877
+ })]
878
+ }, item.key))
723
879
  })
724
880
  });
725
881
  }
882
+ function getContextRailItems(contexts, activeId) {
883
+ return contexts.map((ctx) => ({
884
+ key: ctx.id,
885
+ label: ctx.label,
886
+ to: ctx.href,
887
+ iconKey: inferContextIconKey(ctx),
888
+ active: ctx.id === activeId
889
+ }));
890
+ }
891
+ function inferContextIconKey(ctx) {
892
+ const normalized = `${ctx.id} ${ctx.label}`.toLowerCase();
893
+ if (normalized.includes("api")) return "api";
894
+ if (normalized.includes("component")) return "components";
895
+ if (normalized.includes("custom") || normalized.includes("config")) return "settings";
896
+ if (normalized.includes("deploy") || normalized.includes("trouble")) return "tools";
897
+ if (normalized.includes("guide") || normalized.includes("intro")) return "guide";
898
+ if (normalized.includes("writing") || normalized.includes("markdown")) return "docs";
899
+ return "book";
900
+ }
726
901
  function getDataRailItems(items, currentPath) {
727
902
  return items.map((item, index) => ({
728
903
  key: item.link ?? `${item.text}-${String(index)}`,
@@ -824,10 +999,11 @@ function useSidebarContext() {
824
999
  function ArdoSidebar({ items, children, header, className }) {
825
1000
  const { pathname } = useLocation();
826
1001
  const contextSidebar = useArdoSidebar();
1002
+ const { items: contexts, activeId } = useArdoContexts();
827
1003
  const hasCustomChildren = children != null;
828
1004
  const resolvedItems = items ?? (hasCustomChildren ? void 0 : contextSidebar);
829
1005
  const hasResolvedItems = (resolvedItems?.length ?? 0) > 0;
830
- const railItems = hasCustomChildren ? getRailItemsFromChildren(children, pathname) : getDataRailItems(resolvedItems ?? [], pathname);
1006
+ const railItems = contexts.length > 0 ? getContextRailItems(contexts, activeId) : hasCustomChildren ? getRailItemsFromChildren(children, pathname) : getDataRailItems(resolvedItems ?? [], pathname);
831
1007
  return /* @__PURE__ */ jsx(SidebarContext, {
832
1008
  value: useMemo(() => ({ currentPath: pathname }), [pathname]),
833
1009
  children: /* @__PURE__ */ jsxs("aside", {
@@ -835,7 +1011,7 @@ function ArdoSidebar({ items, children, header, className }) {
835
1011
  children: [/* @__PURE__ */ jsx(SidebarRail, { items: railItems }), /* @__PURE__ */ jsxs("div", {
836
1012
  className: sidebarPanel,
837
1013
  children: [header != null && /* @__PURE__ */ jsx("div", {
838
- className: "_1057ydn5",
1014
+ className: "_1057ydn7",
839
1015
  children: header
840
1016
  }), /* @__PURE__ */ jsx("nav", {
841
1017
  "aria-label": "Main navigation",
@@ -879,7 +1055,7 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
879
1055
  const hasTo = (to ?? "") !== "";
880
1056
  const canToggle = collapsible && hasChildren;
881
1057
  return /* @__PURE__ */ jsxs("li", {
882
- className: className ?? "_1057ydna",
1058
+ className: className ?? ["_1057ydnc", hasChildren && "_1057ydnd"].filter(Boolean).join(" "),
883
1059
  children: [/* @__PURE__ */ jsxs("div", {
884
1060
  className: sidebarItemHeader,
885
1061
  children: [hasTo ? /* @__PURE__ */ jsx(NavLink, {
@@ -896,7 +1072,7 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
896
1072
  children: title
897
1073
  }), canToggle && /* @__PURE__ */ jsx("button", {
898
1074
  type: "button",
899
- className: ["_1057ydnf", collapsed && "collapsed"].filter(Boolean).join(" "),
1075
+ className: ["_1057ydni", collapsed && "collapsed"].filter(Boolean).join(" "),
900
1076
  onClick: () => {
901
1077
  setCollapsed(!collapsed);
902
1078
  },
@@ -904,12 +1080,12 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
904
1080
  children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 })
905
1081
  })]
906
1082
  }), hasChildren && /* @__PURE__ */ jsx("div", {
907
- className: "_1057ydng",
1083
+ className: "_1057ydnj",
908
1084
  "data-collapsed": collapsed,
909
1085
  children: /* @__PURE__ */ jsx("div", {
910
- className: "_1057ydnh",
1086
+ className: "_1057ydnk",
911
1087
  children: /* @__PURE__ */ jsx("ul", {
912
- className: `_1057ydn7 _1057ydn9`,
1088
+ className: `_1057ydn9 _1057ydnb`,
913
1089
  children
914
1090
  })
915
1091
  })
@@ -925,7 +1101,7 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
925
1101
  * ```
926
1102
  */
927
1103
  function ArdoSidebarLink({ to, children, className }) {
928
- const baseClassName = className ?? "_1057ydnc";
1104
+ const baseClassName = className ?? "_1057ydnf";
929
1105
  return /* @__PURE__ */ jsx("li", {
930
1106
  className: sidebarItem,
931
1107
  children: /* @__PURE__ */ jsx(NavLink, {
@@ -954,7 +1130,7 @@ function SidebarItemComponent({ item, depth }) {
954
1130
  const linkClassName = [sidebarLink, hasActiveChild && "child-active"].filter(Boolean).join(" ");
955
1131
  const textButtonClassName = [[sidebarText, hasActiveChild && "child-active"].filter(Boolean).join(" "), sidebarTextButton].join(" ");
956
1132
  return /* @__PURE__ */ jsxs("li", {
957
- className: sidebarItem,
1133
+ className: [sidebarItem, hasChildren && "_1057ydnd"].filter(Boolean).join(" "),
958
1134
  children: [/* @__PURE__ */ jsxs("div", {
959
1135
  className: sidebarItemHeader,
960
1136
  children: [hasItemLink ? /* @__PURE__ */ jsx(NavLink, {
@@ -970,7 +1146,7 @@ function SidebarItemComponent({ item, depth }) {
970
1146
  children: item.text
971
1147
  }), hasChildren && /* @__PURE__ */ jsx("button", {
972
1148
  type: "button",
973
- className: ["_1057ydnf", collapsed && "collapsed"].filter(Boolean).join(" "),
1149
+ className: ["_1057ydni", collapsed && "collapsed"].filter(Boolean).join(" "),
974
1150
  onClick: () => {
975
1151
  setCollapsed(!collapsed);
976
1152
  },
@@ -978,10 +1154,10 @@ function SidebarItemComponent({ item, depth }) {
978
1154
  children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 })
979
1155
  })]
980
1156
  }), hasChildren && /* @__PURE__ */ jsx("div", {
981
- className: "_1057ydng",
1157
+ className: "_1057ydnj",
982
1158
  "data-collapsed": collapsed,
983
1159
  children: /* @__PURE__ */ jsx("div", {
984
- className: "_1057ydnh",
1160
+ className: "_1057ydnk",
985
1161
  children: /* @__PURE__ */ jsx(SidebarItems, {
986
1162
  items: childItems,
987
1163
  depth: depth + 1
@@ -1092,20 +1268,51 @@ function resolveRootHeader(header, headerProps, mobileMenuContent) {
1092
1268
  mobileMenuContent: headerProps?.mobileMenuContent ?? mobileMenuContent
1093
1269
  });
1094
1270
  }
1095
- function resolveLayoutClassName(className, isHomePage) {
1271
+ function resolveLayoutClassName(className, isBareLayout) {
1096
1272
  if (className != null) return className;
1097
- return isHomePage ? `${layout} ${home}` : layout;
1273
+ return isBareLayout ? `${layout} ${home}` : layout;
1274
+ }
1275
+ function readLayoutHandle(handle) {
1276
+ if (typeof handle !== "object" || handle === null) return void 0;
1277
+ if (!("layout" in handle)) return void 0;
1278
+ const { layout } = handle;
1279
+ return typeof layout === "string" ? layout : void 0;
1098
1280
  }
1099
- function ArdoRoot({ config, sidebar, header, sidebarContent, footer, headerProps, sidebarProps, footerProps, editLink, lastUpdated, tocLabel, className, children }) {
1281
+ /**
1282
+ * Reads the React Router `handle` exports from every active route match and
1283
+ * returns the most specific `layout` value (the deepest match wins). MDX
1284
+ * routes get this export auto-generated from `frontmatter.layout`; .tsx
1285
+ * routes set it directly with `export const handle = { layout: "bare" }`.
1286
+ */
1287
+ function useRouteLayout() {
1288
+ const matches = useMatches();
1289
+ for (let i = matches.length - 1; i >= 0; i--) {
1290
+ const layout = readLayoutHandle(matches[i].handle);
1291
+ if (layout !== void 0) return layout;
1292
+ }
1293
+ }
1294
+ function contextMatchesPath(ctx, pathname) {
1295
+ if (ctx.match != null) return typeof ctx.match === "string" ? pathname.startsWith(ctx.match) : ctx.match.test(pathname);
1296
+ const firstSegment = ctx.href.split("/").find((segment) => segment !== "");
1297
+ if (firstSegment === void 0) return false;
1298
+ const root = `/${firstSegment}`;
1299
+ return pathname === root || pathname.startsWith(`${root}/`);
1300
+ }
1301
+ function findActiveContext(contexts, pathname) {
1302
+ return contexts?.find((ctx) => contextMatchesPath(ctx, pathname));
1303
+ }
1304
+ function resolveContextSidebar(sidebar, activeContext) {
1305
+ if (Array.isArray(sidebar)) return sidebar;
1306
+ if (activeContext == null) return [];
1307
+ return sidebar[activeContext.id] ?? [];
1308
+ }
1309
+ function ArdoRoot({ config, sidebar, contexts, header, sidebarContent, footer, headerProps, sidebarProps, footerProps, editLink, lastUpdated, tocLabel, className, children }) {
1100
1310
  const location = useLocation();
1101
- const isHomePage = location.pathname === "/" || location.pathname === "";
1102
- const searchPlaceholder = headerProps?.searchPlaceholder;
1103
- const showSearch = headerProps?.search !== false;
1104
- const resolvedSidebar = isHomePage ? void 0 : wrapSidebarWithSearch(sidebarContent, sidebarProps, !isHomePage && showSearch ? /* @__PURE__ */ jsx(ArdoSearch, { placeholder: searchPlaceholder }) : void 0);
1105
- const resolvedHeader = resolveRootHeader(header, {
1106
- ...headerProps,
1107
- search: false
1108
- }, isHomePage ? void 0 : resolvedSidebar);
1311
+ const isBareLayout = useRouteLayout() === "bare" || location.pathname === "/" || location.pathname === "";
1312
+ const activeContext = useMemo(() => findActiveContext(contexts, location.pathname), [contexts, location.pathname]);
1313
+ const sidebarItems = useMemo(() => resolveContextSidebar(sidebar, activeContext), [sidebar, activeContext]);
1314
+ const resolvedSidebar = isBareLayout ? void 0 : resolveSidebar(sidebarContent, sidebarProps);
1315
+ const resolvedHeader = resolveRootHeader(header, headerProps, isBareLayout ? void 0 : resolvedSidebar);
1109
1316
  const siteConfig = useMemo(() => ({
1110
1317
  editLink,
1111
1318
  lastUpdated,
@@ -1117,9 +1324,11 @@ function ArdoRoot({ config, sidebar, header, sidebarContent, footer, headerProps
1117
1324
  ]);
1118
1325
  const content = /* @__PURE__ */ jsx(ArdoProvider, {
1119
1326
  config,
1120
- sidebar,
1327
+ sidebar: sidebarItems,
1328
+ contexts,
1329
+ activeContextId: activeContext?.id,
1121
1330
  children: /* @__PURE__ */ jsx(ArdoLayout, {
1122
- className: resolveLayoutClassName(className, isHomePage),
1331
+ className: resolveLayoutClassName(className, isBareLayout),
1123
1332
  header: resolvedHeader,
1124
1333
  sidebar: resolvedSidebar,
1125
1334
  footer: footer ?? /* @__PURE__ */ jsx(ArdoFooter, { ...footerProps }),
@@ -1132,21 +1341,13 @@ function ArdoRoot({ config, sidebar, header, sidebarContent, footer, headerProps
1132
1341
  }) : content;
1133
1342
  }
1134
1343
  /**
1135
- * Wraps sidebar content with a search field header.
1136
- * If the user provides custom sidebarContent (which is typically an <ArdoSidebar>),
1137
- * we clone it and inject the search as its `header` prop.
1138
- * Otherwise we create a default <ArdoSidebar> with search.
1344
+ * Resolves the sidebar element. Custom content is passed through as-is;
1345
+ * when none is given, a default data-driven <ArdoSidebar> is created.
1139
1346
  */
1140
- function wrapSidebarWithSearch(sidebarContent, sidebarProps, search) {
1141
- if (sidebarContent == null) return /* @__PURE__ */ jsx(ArdoSidebar, {
1142
- ...sidebarProps,
1143
- header: search
1144
- });
1145
- if (isValidElement(sidebarContent) && sidebarContent.type === ArdoSidebar) return cloneElement(sidebarContent, { header: sidebarContent.props.header ?? search });
1146
- return /* @__PURE__ */ jsx(ArdoSidebar, {
1147
- header: search,
1148
- children: sidebarContent
1149
- });
1347
+ function resolveSidebar(sidebarContent, sidebarProps) {
1348
+ if (sidebarContent == null) return /* @__PURE__ */ jsx(ArdoSidebar, { ...sidebarProps });
1349
+ if (isValidElement(sidebarContent)) return sidebarContent;
1350
+ return /* @__PURE__ */ jsx(ArdoSidebar, { children: sidebarContent });
1150
1351
  }
1151
1352
  function enhanceHeaderWithMobileMenuContent(header, mobileMenuContent) {
1152
1353
  if (!isValidElement(header) || header.type !== ArdoHeader) return header;
@@ -1316,6 +1517,113 @@ function ArdoHero({ name, text, tagline, image, actions, className, version }) {
1316
1517
  });
1317
1518
  }
1318
1519
  //#endregion
1520
+ //#region src/ui/ErrorBoundary.css.ts
1521
+ var actions = "tbxn4v6";
1522
+ var card = "tbxn4v1";
1523
+ var description = "tbxn4v5";
1524
+ var owl = "tbxn4v2";
1525
+ var primaryAction = "tbxn4v7";
1526
+ var root = "tbxn4v0";
1527
+ var status = "tbxn4v3";
1528
+ var title = "tbxn4v4";
1529
+ //#endregion
1530
+ //#region src/ui/ErrorBoundary.tsx
1531
+ const defaults = {
1532
+ notFound: {
1533
+ title: "This page wandered off",
1534
+ description: "The page you were looking for has moved, been renamed, or never existed. The owl is on the case.",
1535
+ homeLabel: "Back to home",
1536
+ homeHref: "/"
1537
+ },
1538
+ error: {
1539
+ title: "Something went wrong",
1540
+ description: "An unexpected error happened while loading this page. Try again, or head back to safer ground.",
1541
+ homeLabel: "Back to home",
1542
+ homeHref: "/"
1543
+ }
1544
+ };
1545
+ /**
1546
+ * Themed error/404 page. Drop into a React Router route as `ErrorBoundary`
1547
+ * to replace the default unstyled fallback with a layout-aware page.
1548
+ *
1549
+ * @example
1550
+ * ```tsx
1551
+ * // app/root.tsx
1552
+ * export { ArdoErrorBoundary as ErrorBoundary } from "ardo/ui"
1553
+ * ```
1554
+ */
1555
+ function ArdoErrorBoundary(props = {}) {
1556
+ const error = useRouteError();
1557
+ const is404 = isRouteErrorResponse(error) && error.status === 404;
1558
+ const isRouteError = isRouteErrorResponse(error);
1559
+ const copy = is404 ? {
1560
+ ...defaults.notFound,
1561
+ ...props.notFound
1562
+ } : {
1563
+ ...defaults.error,
1564
+ ...props.error
1565
+ };
1566
+ const statusLabel = isRouteError ? `Error ${String(error.status)}` : "Error";
1567
+ const errorDetails = is404 ? void 0 : formatErrorDetails(error);
1568
+ return /* @__PURE__ */ jsx("main", {
1569
+ className: root,
1570
+ children: /* @__PURE__ */ jsxs("section", {
1571
+ className: card,
1572
+ children: [
1573
+ /* @__PURE__ */ jsx(ArdoOwlMark, {
1574
+ size: 120,
1575
+ className: owl,
1576
+ title: ""
1577
+ }),
1578
+ /* @__PURE__ */ jsx("p", {
1579
+ className: status,
1580
+ children: statusLabel
1581
+ }),
1582
+ /* @__PURE__ */ jsx("h1", {
1583
+ className: title,
1584
+ children: copy.title
1585
+ }),
1586
+ /* @__PURE__ */ jsx("p", {
1587
+ className: description,
1588
+ children: copy.description
1589
+ }),
1590
+ /* @__PURE__ */ jsxs("div", {
1591
+ className: actions,
1592
+ children: [/* @__PURE__ */ jsx(Link, {
1593
+ to: copy.homeHref,
1594
+ className: primaryAction,
1595
+ children: copy.homeLabel
1596
+ }), !is404 && /* @__PURE__ */ jsx("button", {
1597
+ type: "button",
1598
+ onClick: () => {
1599
+ globalThis.location.reload();
1600
+ },
1601
+ className: "tbxn4v8",
1602
+ children: "Reload"
1603
+ })]
1604
+ }),
1605
+ errorDetails != null && /* @__PURE__ */ jsxs("details", {
1606
+ className: "tbxn4v9",
1607
+ children: [/* @__PURE__ */ jsx("summary", {
1608
+ className: "tbxn4va",
1609
+ children: "Technical details"
1610
+ }), /* @__PURE__ */ jsx("pre", {
1611
+ className: "tbxn4vb",
1612
+ children: errorDetails
1613
+ })]
1614
+ })
1615
+ ]
1616
+ })
1617
+ });
1618
+ }
1619
+ function formatErrorDetails(error) {
1620
+ if (isRouteErrorResponse(error)) {
1621
+ if (typeof error.data === "string" && error.data.length > 0) return error.data;
1622
+ return;
1623
+ }
1624
+ if (error instanceof Error) return error.stack ?? error.message;
1625
+ }
1626
+ //#endregion
1319
1627
  //#region src/ui/HomePage.tsx
1320
1628
  function HomeHeroSection({ hero: hero$1, fallbackTitle }) {
1321
1629
  if (hero$1 === void 0) return null;
@@ -1450,6 +1758,6 @@ function ArdoNavLink({ to, href, children, className, activeMatch: _activeMatch
1450
1758
  });
1451
1759
  }
1452
1760
  //#endregion
1453
- export { ArdoFeatureCard as a, ArdoSidebar as c, ArdoHeader as d, ArdoSocialLink as f, ArdoSearch as h, ArdoHero as i, ArdoSidebarGroup as l, ArdoFooter as m, ArdoNavLink as n, ArdoFeatures as o, ArdoThemeToggle as p, ArdoHomePage as r, ArdoRoot as s, ArdoNav as t, ArdoSidebarLink as u };
1761
+ export { ArdoOwlMark as _, ArdoHero as a, ArdoRoot as c, ArdoSidebarLink as d, ArdoHeader as f, ArdoFooter as g, ArdoSearch as h, ArdoErrorBoundary as i, ArdoSidebar as l, ArdoThemeToggle as m, ArdoNavLink as n, ArdoFeatureCard as o, ArdoSocialLink as p, ArdoHomePage as r, ArdoFeatures as s, ArdoNav as t, ArdoSidebarGroup as u };
1454
1762
 
1455
- //# sourceMappingURL=ui-BzUIfYJm.js.map
1763
+ //# sourceMappingURL=ui-AGPGBunC.js.map