ardo 3.2.1 → 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 (78) hide show
  1. package/README.md +49 -41
  2. package/dist/{DocPage-CIBiCAxZ.js → DocPage-Dy7OrCP2.js} +160 -31
  3. package/dist/DocPage-Dy7OrCP2.js.map +1 -0
  4. package/dist/assets/src/ui/DocPage.css.ts.vanilla-CWL92vUE.css +117 -0
  5. package/dist/assets/src/ui/ErrorBoundary.css.ts.vanilla-C4usIU4z.css +112 -0
  6. package/dist/assets/src/ui/Footer.css.ts.vanilla-DGTyff5Y.css +171 -0
  7. package/dist/assets/src/ui/{Header.css.ts.vanilla-8QL0Jzgk.css → Header.css.ts.vanilla-DEcLj8r0.css} +53 -27
  8. package/dist/assets/src/ui/{Layout.css.ts.vanilla-Bpx_-gJt.css → Layout.css.ts.vanilla-ClOa1YZm.css} +6 -10
  9. package/dist/assets/src/ui/Sidebar.css.ts.vanilla-IxNEQEBv.css +199 -0
  10. package/dist/assets/src/ui/{Toc.css.ts.vanilla-CYqcWgvD.css → Toc.css.ts.vanilla-CQbpEdTg.css} +9 -21
  11. package/dist/assets/src/ui/components/{CodeBlock.css.ts.vanilla-lNKqskjQ.css → CodeBlock.css.ts.vanilla-BxDJ2gKc.css} +38 -20
  12. package/dist/assets/src/ui/components/{CopyButton.css.ts.vanilla-DZZ5jgTM.css → CopyButton.css.ts.vanilla-CO2awD6S.css} +6 -7
  13. package/dist/assets/src/ui/components/{Features.css.ts.vanilla-D-pNXM9Q.css → Features.css.ts.vanilla-ggYasCFy.css} +7 -3
  14. package/dist/assets/src/ui/components/HeaderSearch.css.ts.vanilla-KAo_Mlc-.css +68 -0
  15. package/dist/assets/src/ui/components/{Hero.css.ts.vanilla-DHJVZ6GX.css → Hero.css.ts.vanilla-DY_BH0W6.css} +9 -6
  16. package/dist/assets/src/ui/components/{Search.css.ts.vanilla-BYpWHzky.css → Search.css.ts.vanilla-NQZH1eLo.css} +19 -7
  17. package/dist/assets/src/ui/{content.css.ts.vanilla-O_RaSPXm.css → content.css.ts.vanilla-CJnrOQNh.css} +43 -17
  18. package/dist/assets/src/ui/theme/{dark.css.ts.vanilla-2iJgcpbU.css → dark.css.ts.vanilla-CQef5pk2.css} +32 -28
  19. package/dist/assets/src/ui/theme/{light.css.ts.vanilla-CwinfWSf.css → light.css.ts.vanilla-D8gxaS1c.css} +32 -28
  20. package/dist/assets/src/ui/theme/{reset.css.ts.vanilla-0Q3pLjfC.css → reset.css.ts.vanilla-e2dF1d0f.css} +4 -0
  21. package/dist/{brand-icons-DLJKqTun.js → brand-icons-Di8w0Nu9.js} +1 -1
  22. package/dist/{brand-icons-DLJKqTun.js.map → brand-icons-Di8w0Nu9.js.map} +1 -1
  23. package/dist/config/index.d.ts +2 -2
  24. package/dist/config/index.d.ts.map +1 -1
  25. package/dist/config/index.js +1 -0
  26. package/dist/config/index.js.map +1 -1
  27. package/dist/{contract.css-DYvFVCFE.d.ts → contract.css-qPyk_asd.d.ts} +5 -1
  28. package/dist/contract.css-qPyk_asd.d.ts.map +1 -0
  29. package/dist/favicon-Cx-inut3.js +7 -0
  30. package/dist/favicon-Cx-inut3.js.map +1 -0
  31. package/dist/{generator-DPtRXxM_.js → generator-CYSyo4Vz.js} +7 -6
  32. package/dist/generator-CYSyo4Vz.js.map +1 -0
  33. package/dist/icons/index.js +1 -1
  34. package/dist/{index-BTeHvysI.d.ts → index-BcekgOfA.d.ts} +82 -12
  35. package/dist/index-BcekgOfA.d.ts.map +1 -0
  36. package/dist/{index-DySzkJlC.d.ts → index-CuMTHUxX.d.ts} +8 -2
  37. package/dist/index-CuMTHUxX.d.ts.map +1 -0
  38. package/dist/index.d.ts +4 -4
  39. package/dist/index.js +3 -3
  40. package/dist/mdx/provider.js +1 -1
  41. package/dist/runtime/index.d.ts +1 -1
  42. package/dist/runtime/index.js +1 -1
  43. package/dist/{sidebar-utils-1Skqle1Q.js → sidebar-utils-C06DJsx4.js} +15 -4
  44. package/dist/sidebar-utils-C06DJsx4.js.map +1 -0
  45. package/dist/theme/index.d.ts +16 -4
  46. package/dist/theme/index.d.ts.map +1 -1
  47. package/dist/theme/index.js +84 -59
  48. package/dist/theme/index.js.map +1 -1
  49. package/dist/typedoc/components/index.d.ts +1 -1
  50. package/dist/typedoc/index.d.ts +1 -1
  51. package/dist/typedoc/index.d.ts.map +1 -1
  52. package/dist/typedoc/index.js +1 -1
  53. package/dist/{types-CTd_mkrv.d.ts → types-B75OhnGa.d.ts} +21 -3
  54. package/dist/types-B75OhnGa.d.ts.map +1 -0
  55. package/dist/{types-BCuJBsJu.d.ts → types-Ck2Vm7NB.d.ts} +2 -2
  56. package/dist/{types-BCuJBsJu.d.ts.map → types-Ck2Vm7NB.d.ts.map} +1 -1
  57. package/dist/ui/index.d.ts +2 -2
  58. package/dist/ui/index.js +3 -3
  59. package/dist/ui/styles.css +704 -382
  60. package/dist/ui/styles.js +15 -15
  61. package/dist/{ui-3grzJSsq.js → ui-AGPGBunC.js} +729 -280
  62. package/dist/ui-AGPGBunC.js.map +1 -0
  63. package/dist/vite/index.d.ts +18 -2
  64. package/dist/vite/index.d.ts.map +1 -1
  65. package/dist/vite/index.js +446 -121
  66. package/dist/vite/index.js.map +1 -1
  67. package/package.json +14 -13
  68. package/dist/DocPage-CIBiCAxZ.js.map +0 -1
  69. package/dist/assets/src/ui/DocPage.css.ts.vanilla-CXKuz4U-.css +0 -34
  70. package/dist/assets/src/ui/Footer.css.ts.vanilla-BSzPIPt4.css +0 -100
  71. package/dist/assets/src/ui/Sidebar.css.ts.vanilla-D70qXTEr.css +0 -115
  72. package/dist/contract.css-DYvFVCFE.d.ts.map +0 -1
  73. package/dist/generator-DPtRXxM_.js.map +0 -1
  74. package/dist/index-BTeHvysI.d.ts.map +0 -1
  75. package/dist/index-DySzkJlC.d.ts.map +0 -1
  76. package/dist/sidebar-utils-1Skqle1Q.js.map +0 -1
  77. package/dist/types-CTd_mkrv.d.ts.map +0 -1
  78. package/dist/ui-3grzJSsq.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 { A as home, F as footerContainer, G as SearchIcon, H as MessageCircleIcon, K as SunIcon, L as footerLink, M as layout, O as ArdoLayout, U as MonitorIcon, V as ChevronDownIcon, W as MoonIcon, j as homeMain, q as XIcon, z as footerPrimary } from "./DocPage-CIBiCAxZ.js";
4
- import React, { Children, cloneElement, createContext, isValidElement, use, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
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
+ 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-BYpWHzky.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-8QL0Jzgk.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-D70qXTEr.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
- import "./assets/src/ui/components/Features.css.ts.vanilla-D-pNXM9Q.css";
17
- import "./assets/src/ui/components/Hero.css.ts.vanilla-DHJVZ6GX.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
17
+ import "./assets/src/ui/components/Features.css.ts.vanilla-ggYasCFy.css";
18
+ import "./assets/src/ui/components/Hero.css.ts.vanilla-DY_BH0W6.css";
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
@@ -456,10 +598,11 @@ function getInitialTheme() {
456
598
  }
457
599
  function ArdoThemeToggle() {
458
600
  const [theme, setTheme] = useState(getInitialTheme);
459
- const [mounted] = useState(isBrowser);
601
+ const mounted = useSyncExternalStore(subscribeMounted, getClientMounted, getServerMounted);
460
602
  useEffect(() => {
603
+ if (!mounted) return;
461
604
  applyTheme(theme);
462
- }, [theme]);
605
+ }, [mounted, theme]);
463
606
  const toggleTheme = () => {
464
607
  const nextTheme = theme === "light" ? "dark" : theme === "dark" ? "system" : "light";
465
608
  setTheme(nextTheme);
@@ -501,39 +644,56 @@ function applyTheme(theme) {
501
644
  root.classList.toggle("light", theme === "light");
502
645
  }
503
646
  }
647
+ function subscribeMounted() {
648
+ return noop;
649
+ }
650
+ function getClientMounted() {
651
+ return true;
652
+ }
653
+ function getServerMounted() {
654
+ return false;
655
+ }
656
+ function noop() {}
504
657
  function isTheme(value) {
505
658
  return value === "dark" || value === "light" || value === "system";
506
659
  }
507
660
  var headerContainer = "qjc2r51";
508
661
  var headerLeft = "qjc2r52";
509
- var headerRight = "qjc2r53";
510
- var logoLink = "qjc2r54";
511
- var mobileBackdrop = "qjc2r5a";
512
- var mobilePanel = "qjc2r5b";
513
- var mobilePanelClose = "qjc2r5d";
514
- var mobilePanelHeader = "qjc2r5c";
515
- var mobilePanelSidebar = "qjc2r5f";
662
+ var headerRight = "qjc2r54";
663
+ var logoLink = "qjc2r55";
664
+ var mobileBackdrop = "qjc2r5b";
665
+ var mobilePanel = "qjc2r5c";
666
+ var mobilePanelClose = "qjc2r5e";
667
+ var mobilePanelHeader = "qjc2r5d";
668
+ var mobilePanelSidebar = "qjc2r5g";
516
669
  //#endregion
517
670
  //#region src/ui/Header.tsx
518
- function ArdoHeader({ logo: logo$1, 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() {
519
676
  const location = useLocation();
520
- const config = useArdoConfig();
521
- const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
522
- const resolvedLogo = logo$1;
523
- const resolvedTitle = title ?? config.title;
524
- const hasLogo = resolvedLogo !== void 0;
525
- const hasTitle = resolvedTitle !== "";
526
- const hasMobileMenu = mobileMenuContent != null;
677
+ const [open, setOpen] = useState(false);
527
678
  useEffect(() => {
528
- setMobileMenuOpen(false);
679
+ setOpen(false);
529
680
  }, [location.pathname]);
530
681
  useEffect(() => {
531
- if (mobileMenuOpen) document.body.style.overflow = "hidden";
532
- else document.body.style.overflow = "";
682
+ document.body.style.overflow = open ? "hidden" : "";
533
683
  return () => {
534
684
  document.body.style.overflow = "";
535
685
  };
536
- }, [mobileMenuOpen]);
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;
537
697
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("header", {
538
698
  className: className ?? "qjc2r50",
539
699
  children: /* @__PURE__ */ jsxs("div", {
@@ -543,14 +703,14 @@ function ArdoHeader({ logo: logo$1, title, nav, actions, search = false, searchP
543
703
  className: headerLeft,
544
704
  children: [hasMobileMenu && /* @__PURE__ */ jsx("button", {
545
705
  type: "button",
546
- className: "qjc2r57",
706
+ className: "qjc2r58",
547
707
  onClick: () => {
548
708
  setMobileMenuOpen(!mobileMenuOpen);
549
709
  },
550
710
  "aria-label": "Toggle menu",
551
711
  "aria-expanded": mobileMenuOpen,
552
712
  children: /* @__PURE__ */ jsxs("span", {
553
- className: "qjc2r58",
713
+ className: "qjc2r59",
554
714
  children: [
555
715
  /* @__PURE__ */ jsx("span", {}),
556
716
  /* @__PURE__ */ jsx("span", {}),
@@ -563,21 +723,24 @@ function ArdoHeader({ logo: logo$1, title, nav, actions, search = false, searchP
563
723
  children: [hasLogo && /* @__PURE__ */ jsx("img", {
564
724
  src: typeof resolvedLogo === "string" ? resolvedLogo : resolvedLogo.light,
565
725
  alt: resolvedTitle,
566
- className: "qjc2r55"
726
+ className: "qjc2r56"
567
727
  }), hasTitle && /* @__PURE__ */ jsx("span", {
568
- className: "qjc2r56",
728
+ className: "qjc2r57",
569
729
  children: resolvedTitle
570
730
  })]
571
731
  })]
572
732
  }),
573
- nav != null && /* @__PURE__ */ jsx("div", {
574
- className: "qjc2r59",
575
- children: nav
733
+ search && /* @__PURE__ */ jsx("div", {
734
+ className: "qjc2r53",
735
+ children: /* @__PURE__ */ jsx(ArdoHeaderSearch, { placeholder: searchPlaceholder })
576
736
  }),
577
737
  /* @__PURE__ */ jsxs("div", {
578
738
  className: headerRight,
579
739
  children: [
580
- search && /* @__PURE__ */ jsx(ArdoSearch, { placeholder: searchPlaceholder }),
740
+ nav != null && /* @__PURE__ */ jsx("div", {
741
+ className: "qjc2r5a",
742
+ children: nav
743
+ }),
581
744
  themeToggle && /* @__PURE__ */ jsx(ArdoThemeToggle, {}),
582
745
  actions
583
746
  ]
@@ -596,7 +759,7 @@ function ArdoHeader({ logo: logo$1, title, nav, actions, search = false, searchP
596
759
  children: mobileMenuContent
597
760
  })] });
598
761
  }
599
- function MobileSlidePanel({ isOpen, logo: logo$2, title, nav, themeToggle, children, onClose }) {
762
+ function MobileSlidePanel({ isOpen, logo: logo$1, title, nav, themeToggle, children, onClose }) {
600
763
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
601
764
  className: mobileBackdrop,
602
765
  "data-open": isOpen,
@@ -612,10 +775,10 @@ function MobileSlidePanel({ isOpen, logo: logo$2, title, nav, themeToggle, child
612
775
  to: "/",
613
776
  className: logoLink,
614
777
  onClick: onClose,
615
- children: logo$2 != null && /* @__PURE__ */ jsx("img", {
616
- src: typeof logo$2 === "string" ? logo$2 : logo$2.light,
778
+ children: logo$1 != null && /* @__PURE__ */ jsx("img", {
779
+ src: typeof logo$1 === "string" ? logo$1 : logo$1.light,
617
780
  alt: title,
618
- className: "qjc2r55"
781
+ className: "qjc2r56"
619
782
  })
620
783
  }), /* @__PURE__ */ jsxs("div", {
621
784
  style: {
@@ -633,7 +796,7 @@ function MobileSlidePanel({ isOpen, logo: logo$2, title, nav, themeToggle, child
633
796
  })]
634
797
  }),
635
798
  nav != null && /* @__PURE__ */ jsx("div", {
636
- className: "qjc2r5e",
799
+ className: "qjc2r5f",
637
800
  onClickCapture: handleLinkClick(onClose),
638
801
  children: nav
639
802
  }),
@@ -672,15 +835,130 @@ function SocialIcon({ icon }) {
672
835
  const IconComponent = socialIcons[icon];
673
836
  return /* @__PURE__ */ jsx(IconComponent, { size: 20 });
674
837
  }
675
- var sidebarItem = "_1057ydn6";
676
- var sidebarItemHeader = "_1057ydn7";
677
- var sidebarLink = "_1057ydn8";
678
- var sidebarList = "_1057ydn3";
679
- var sidebarList0 = "_1057ydn4";
680
- var sidebarList1 = "_1057ydn5";
681
- var sidebarNav = "_1057ydn2";
682
- var sidebarText = "_1057ydn9";
683
- var sidebarTextButton = "_1057ydna";
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";
846
+ var sidebarRail = "_1057ydn1";
847
+ var sidebarRailItem = "_1057ydn2";
848
+ var sidebarRailLabel = "_1057ydn3";
849
+ var sidebarRailLink = "_1057ydn5";
850
+ var sidebarRailList = "_1057ydn4";
851
+ var sidebarText = "_1057ydng";
852
+ var sidebarTextButton = "_1057ydnh";
853
+ //#endregion
854
+ //#region src/ui/SidebarRail.tsx
855
+ function SidebarRail({ items }) {
856
+ if (items.length === 0) return null;
857
+ return /* @__PURE__ */ jsx("nav", {
858
+ "aria-label": "Documentation sections",
859
+ className: sidebarRail,
860
+ children: /* @__PURE__ */ jsx("ul", {
861
+ className: sidebarRailList,
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))
879
+ })
880
+ });
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
+ }
901
+ function getDataRailItems(items, currentPath) {
902
+ return items.map((item, index) => ({
903
+ key: item.link ?? `${item.text}-${String(index)}`,
904
+ label: item.text,
905
+ to: item.link ?? findFirstItemLink(item.items ?? []),
906
+ iconKey: item.icon,
907
+ active: item.link === currentPath || hasActiveDataChild(item, currentPath)
908
+ }));
909
+ }
910
+ function getTextLabel(children, fallback) {
911
+ if (typeof children === "string") return children;
912
+ if (typeof children === "number") return String(children);
913
+ return fallback;
914
+ }
915
+ function findFirstItemLink(items) {
916
+ for (const item of items) {
917
+ if ((item.link ?? "") !== "") return item.link;
918
+ const nested = findFirstItemLink(item.items ?? []);
919
+ if (nested !== void 0) return nested;
920
+ }
921
+ }
922
+ function hasActiveDataChild(item, currentPath) {
923
+ return (item.items ?? []).some((child) => child.link === currentPath || hasActiveDataChild(child, currentPath));
924
+ }
925
+ function resolveRailIcon(item, index) {
926
+ if (item.icon !== void 0) return item.icon;
927
+ const Icon = iconByKey[item.iconKey ?? inferIconKey(item.label, index)];
928
+ return /* @__PURE__ */ jsx(Icon, {
929
+ size: 18,
930
+ strokeWidth: 1.8
931
+ });
932
+ }
933
+ function inferIconKey(label, index) {
934
+ const normalized = label.toLowerCase();
935
+ if (normalized.includes("api")) return "api";
936
+ if (normalized.includes("component")) return "components";
937
+ if (normalized.includes("custom") || normalized.includes("config")) return "settings";
938
+ if (normalized.includes("deploy") || normalized.includes("trouble")) return "tools";
939
+ if (normalized.includes("writing") || normalized.includes("markdown")) return "docs";
940
+ if (normalized.includes("intro") || normalized.includes("guide")) return "guide";
941
+ return fallbackIconKeys[index % fallbackIconKeys.length];
942
+ }
943
+ const iconByKey = {
944
+ api: CodeIcon,
945
+ book: BookOpenIcon,
946
+ box: BoxIcon,
947
+ code: FileCodeIcon,
948
+ components: PackageIcon,
949
+ docs: FileTextIcon,
950
+ guide: BookOpenIcon,
951
+ settings: SettingsIcon,
952
+ tools: WrenchIcon
953
+ };
954
+ const fallbackIconKeys = [
955
+ "guide",
956
+ "docs",
957
+ "settings",
958
+ "tools",
959
+ "components",
960
+ "box"
961
+ ];
684
962
  //#endregion
685
963
  //#region src/ui/Sidebar.tsx
686
964
  const SidebarContext = createContext({ currentPath: "" });
@@ -721,26 +999,31 @@ function useSidebarContext() {
721
999
  function ArdoSidebar({ items, children, header, className }) {
722
1000
  const { pathname } = useLocation();
723
1001
  const contextSidebar = useArdoSidebar();
1002
+ const { items: contexts, activeId } = useArdoContexts();
724
1003
  const hasCustomChildren = children != null;
725
1004
  const resolvedItems = items ?? (hasCustomChildren ? void 0 : contextSidebar);
726
1005
  const hasResolvedItems = (resolvedItems?.length ?? 0) > 0;
1006
+ const railItems = contexts.length > 0 ? getContextRailItems(contexts, activeId) : hasCustomChildren ? getRailItemsFromChildren(children, pathname) : getDataRailItems(resolvedItems ?? [], pathname);
727
1007
  return /* @__PURE__ */ jsx(SidebarContext, {
728
1008
  value: useMemo(() => ({ currentPath: pathname }), [pathname]),
729
1009
  children: /* @__PURE__ */ jsxs("aside", {
730
1010
  className: className ?? "_1057ydn0",
731
- children: [header != null && /* @__PURE__ */ jsx("div", {
732
- className: "_1057ydn1",
733
- children: header
734
- }), /* @__PURE__ */ jsx("nav", {
735
- "aria-label": "Main navigation",
736
- className: sidebarNav,
737
- children: hasCustomChildren ? /* @__PURE__ */ jsx("ul", {
738
- className: `${sidebarList} ${sidebarList0}`,
739
- children
740
- }) : hasResolvedItems ? /* @__PURE__ */ jsx(SidebarItems, {
741
- items: resolvedItems ?? [],
742
- depth: 0
743
- }) : null
1011
+ children: [/* @__PURE__ */ jsx(SidebarRail, { items: railItems }), /* @__PURE__ */ jsxs("div", {
1012
+ className: sidebarPanel,
1013
+ children: [header != null && /* @__PURE__ */ jsx("div", {
1014
+ className: "_1057ydn7",
1015
+ children: header
1016
+ }), /* @__PURE__ */ jsx("nav", {
1017
+ "aria-label": "Main navigation",
1018
+ className: sidebarNav,
1019
+ children: hasCustomChildren ? /* @__PURE__ */ jsx("ul", {
1020
+ className: `${sidebarList} ${sidebarList0}`,
1021
+ children
1022
+ }) : hasResolvedItems ? /* @__PURE__ */ jsx(SidebarItems, {
1023
+ items: resolvedItems ?? [],
1024
+ depth: 0
1025
+ }) : null
1026
+ })]
744
1027
  })]
745
1028
  })
746
1029
  });
@@ -772,7 +1055,7 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
772
1055
  const hasTo = (to ?? "") !== "";
773
1056
  const canToggle = collapsible && hasChildren;
774
1057
  return /* @__PURE__ */ jsxs("li", {
775
- className: className ?? "_1057ydn6",
1058
+ className: className ?? ["_1057ydnc", hasChildren && "_1057ydnd"].filter(Boolean).join(" "),
776
1059
  children: [/* @__PURE__ */ jsxs("div", {
777
1060
  className: sidebarItemHeader,
778
1061
  children: [hasTo ? /* @__PURE__ */ jsx(NavLink, {
@@ -789,7 +1072,7 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
789
1072
  children: title
790
1073
  }), canToggle && /* @__PURE__ */ jsx("button", {
791
1074
  type: "button",
792
- className: ["_1057ydnb", collapsed && "collapsed"].filter(Boolean).join(" "),
1075
+ className: ["_1057ydni", collapsed && "collapsed"].filter(Boolean).join(" "),
793
1076
  onClick: () => {
794
1077
  setCollapsed(!collapsed);
795
1078
  },
@@ -797,12 +1080,12 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
797
1080
  children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 })
798
1081
  })]
799
1082
  }), hasChildren && /* @__PURE__ */ jsx("div", {
800
- className: "_1057ydnc",
1083
+ className: "_1057ydnj",
801
1084
  "data-collapsed": collapsed,
802
1085
  children: /* @__PURE__ */ jsx("div", {
803
- className: "_1057ydnd",
1086
+ className: "_1057ydnk",
804
1087
  children: /* @__PURE__ */ jsx("ul", {
805
- className: `_1057ydn3 _1057ydn5`,
1088
+ className: `_1057ydn9 _1057ydnb`,
806
1089
  children
807
1090
  })
808
1091
  })
@@ -818,7 +1101,7 @@ function ArdoSidebarGroup({ title, to, collapsed: initialCollapsed = false, coll
818
1101
  * ```
819
1102
  */
820
1103
  function ArdoSidebarLink({ to, children, className }) {
821
- const baseClassName = className ?? "_1057ydn8";
1104
+ const baseClassName = className ?? "_1057ydnf";
822
1105
  return /* @__PURE__ */ jsx("li", {
823
1106
  className: sidebarItem,
824
1107
  children: /* @__PURE__ */ jsx(NavLink, {
@@ -847,7 +1130,7 @@ function SidebarItemComponent({ item, depth }) {
847
1130
  const linkClassName = [sidebarLink, hasActiveChild && "child-active"].filter(Boolean).join(" ");
848
1131
  const textButtonClassName = [[sidebarText, hasActiveChild && "child-active"].filter(Boolean).join(" "), sidebarTextButton].join(" ");
849
1132
  return /* @__PURE__ */ jsxs("li", {
850
- className: sidebarItem,
1133
+ className: [sidebarItem, hasChildren && "_1057ydnd"].filter(Boolean).join(" "),
851
1134
  children: [/* @__PURE__ */ jsxs("div", {
852
1135
  className: sidebarItemHeader,
853
1136
  children: [hasItemLink ? /* @__PURE__ */ jsx(NavLink, {
@@ -863,7 +1146,7 @@ function SidebarItemComponent({ item, depth }) {
863
1146
  children: item.text
864
1147
  }), hasChildren && /* @__PURE__ */ jsx("button", {
865
1148
  type: "button",
866
- className: ["_1057ydnb", collapsed && "collapsed"].filter(Boolean).join(" "),
1149
+ className: ["_1057ydni", collapsed && "collapsed"].filter(Boolean).join(" "),
867
1150
  onClick: () => {
868
1151
  setCollapsed(!collapsed);
869
1152
  },
@@ -871,10 +1154,10 @@ function SidebarItemComponent({ item, depth }) {
871
1154
  children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 16 })
872
1155
  })]
873
1156
  }), hasChildren && /* @__PURE__ */ jsx("div", {
874
- className: "_1057ydnc",
1157
+ className: "_1057ydnj",
875
1158
  "data-collapsed": collapsed,
876
1159
  children: /* @__PURE__ */ jsx("div", {
877
- className: "_1057ydnd",
1160
+ className: "_1057ydnk",
878
1161
  children: /* @__PURE__ */ jsx(SidebarItems, {
879
1162
  items: childItems,
880
1163
  depth: depth + 1
@@ -883,6 +1166,40 @@ function SidebarItemComponent({ item, depth }) {
883
1166
  })]
884
1167
  });
885
1168
  }
1169
+ function getRailItemsFromChildren(children, currentPath) {
1170
+ return Children.toArray(children).filter(isValidElement).map((child, index) => {
1171
+ if (isSidebarGroupElement(child)) {
1172
+ const { title, to, icon } = child.props;
1173
+ return {
1174
+ key: to ?? title,
1175
+ label: title,
1176
+ to: to ?? findFirstChildLink(child.props.children),
1177
+ icon,
1178
+ active: to === currentPath || checkChildrenActive(child.props.children, currentPath)
1179
+ };
1180
+ }
1181
+ if (isSidebarLinkElement(child)) {
1182
+ const label = getTextLabel(child.props.children, `Section ${String(index + 1)}`);
1183
+ return {
1184
+ key: `${label}-${String(index)}`,
1185
+ label,
1186
+ to: child.props.to,
1187
+ active: child.props.to === currentPath
1188
+ };
1189
+ }
1190
+ }).filter((item) => item !== void 0);
1191
+ }
1192
+ function findFirstChildLink(children) {
1193
+ for (const child of Children.toArray(children)) {
1194
+ if (!isValidElement(child)) continue;
1195
+ if (isSidebarLinkElement(child)) return child.props.to;
1196
+ if (isSidebarGroupElement(child)) return findFirstGroupLink(child);
1197
+ }
1198
+ }
1199
+ function findFirstGroupLink(child) {
1200
+ if ((child.props.to ?? "") !== "") return child.props.to;
1201
+ return findFirstChildLink(child.props.children);
1202
+ }
886
1203
  function isSidebarChildActive(child, currentPath) {
887
1204
  if (isSidebarLinkElement(child)) return child.props.to === currentPath;
888
1205
  if (isSidebarGroupElement(child)) {
@@ -951,20 +1268,51 @@ function resolveRootHeader(header, headerProps, mobileMenuContent) {
951
1268
  mobileMenuContent: headerProps?.mobileMenuContent ?? mobileMenuContent
952
1269
  });
953
1270
  }
954
- function resolveLayoutClassName(className, isHomePage) {
1271
+ function resolveLayoutClassName(className, isBareLayout) {
955
1272
  if (className != null) return className;
956
- return isHomePage ? `${layout} ${home}` : layout;
1273
+ return isBareLayout ? `${layout} ${home}` : layout;
957
1274
  }
958
- function ArdoRoot({ config, sidebar, header, sidebarContent, footer, headerProps, sidebarProps, footerProps, editLink, lastUpdated, tocLabel, className, children }) {
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;
1280
+ }
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 }) {
959
1310
  const location = useLocation();
960
- const isHomePage = location.pathname === "/" || location.pathname === "";
961
- const searchPlaceholder = headerProps?.searchPlaceholder;
962
- const showSearch = headerProps?.search !== false;
963
- const resolvedSidebar = isHomePage ? void 0 : wrapSidebarWithSearch(sidebarContent, sidebarProps, !isHomePage && showSearch ? /* @__PURE__ */ jsx(ArdoSearch, { placeholder: searchPlaceholder }) : void 0);
964
- const resolvedHeader = resolveRootHeader(header, {
965
- ...headerProps,
966
- search: false
967
- }, 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);
968
1316
  const siteConfig = useMemo(() => ({
969
1317
  editLink,
970
1318
  lastUpdated,
@@ -976,9 +1324,11 @@ function ArdoRoot({ config, sidebar, header, sidebarContent, footer, headerProps
976
1324
  ]);
977
1325
  const content = /* @__PURE__ */ jsx(ArdoProvider, {
978
1326
  config,
979
- sidebar,
1327
+ sidebar: sidebarItems,
1328
+ contexts,
1329
+ activeContextId: activeContext?.id,
980
1330
  children: /* @__PURE__ */ jsx(ArdoLayout, {
981
- className: resolveLayoutClassName(className, isHomePage),
1331
+ className: resolveLayoutClassName(className, isBareLayout),
982
1332
  header: resolvedHeader,
983
1333
  sidebar: resolvedSidebar,
984
1334
  footer: footer ?? /* @__PURE__ */ jsx(ArdoFooter, { ...footerProps }),
@@ -991,21 +1341,13 @@ function ArdoRoot({ config, sidebar, header, sidebarContent, footer, headerProps
991
1341
  }) : content;
992
1342
  }
993
1343
  /**
994
- * Wraps sidebar content with a search field header.
995
- * If the user provides custom sidebarContent (which is typically an <ArdoSidebar>),
996
- * we clone it and inject the search as its `header` prop.
997
- * 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.
998
1346
  */
999
- function wrapSidebarWithSearch(sidebarContent, sidebarProps, search) {
1000
- if (sidebarContent == null) return /* @__PURE__ */ jsx(ArdoSidebar, {
1001
- ...sidebarProps,
1002
- header: search
1003
- });
1004
- if (isValidElement(sidebarContent) && sidebarContent.type === ArdoSidebar) return cloneElement(sidebarContent, { header: sidebarContent.props.header ?? search });
1005
- return /* @__PURE__ */ jsx(ArdoSidebar, {
1006
- header: search,
1007
- children: sidebarContent
1008
- });
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 });
1009
1351
  }
1010
1352
  function enhanceHeaderWithMobileMenuContent(header, mobileMenuContent) {
1011
1353
  if (!isValidElement(header) || header.type !== ArdoHeader) return header;
@@ -1175,6 +1517,113 @@ function ArdoHero({ name, text, tagline, image, actions, className, version }) {
1175
1517
  });
1176
1518
  }
1177
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
1178
1627
  //#region src/ui/HomePage.tsx
1179
1628
  function HomeHeroSection({ hero: hero$1, fallbackTitle }) {
1180
1629
  if (hero$1 === void 0) return null;
@@ -1309,6 +1758,6 @@ function ArdoNavLink({ to, href, children, className, activeMatch: _activeMatch
1309
1758
  });
1310
1759
  }
1311
1760
  //#endregion
1312
- 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 };
1313
1762
 
1314
- //# sourceMappingURL=ui-3grzJSsq.js.map
1763
+ //# sourceMappingURL=ui-AGPGBunC.js.map