zudoku 0.0.0-f3858d6 → 0.0.0-f417aae

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 (239) hide show
  1. package/README.md +121 -0
  2. package/cli.js +2 -2
  3. package/dist/app/entry.client.js +2 -2
  4. package/dist/app/entry.client.js.map +1 -1
  5. package/dist/app/entry.server.js +0 -3
  6. package/dist/app/entry.server.js.map +1 -1
  7. package/dist/app/main.d.ts +0 -1
  8. package/dist/app/main.js +23 -10
  9. package/dist/app/main.js.map +1 -1
  10. package/dist/app/standalone.js.map +1 -1
  11. package/dist/cli/common/machine-id/lib.js.map +1 -1
  12. package/dist/cli/common/outdated.js.map +1 -1
  13. package/dist/cli/common/utils/box.js.map +1 -1
  14. package/dist/config/validators/InputSidebarSchema.d.ts +21 -6
  15. package/dist/config/validators/InputSidebarSchema.js +7 -28
  16. package/dist/config/validators/InputSidebarSchema.js.map +1 -1
  17. package/dist/config/validators/SidebarSchema.d.ts +1 -1
  18. package/dist/config/validators/SidebarSchema.js +14 -11
  19. package/dist/config/validators/SidebarSchema.js.map +1 -1
  20. package/dist/config/validators/validate.d.ts +117 -95
  21. package/dist/config/validators/validate.js +23 -8
  22. package/dist/config/validators/validate.js.map +1 -1
  23. package/dist/index.d.ts +2 -5
  24. package/dist/index.js +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/lib/authentication/components/CallbackHandler.js +21 -31
  27. package/dist/lib/authentication/components/CallbackHandler.js.map +1 -1
  28. package/dist/lib/authentication/hook.d.ts +1 -1
  29. package/dist/lib/authentication/hook.js +1 -1
  30. package/dist/lib/authentication/hook.js.map +1 -1
  31. package/dist/lib/components/Header.js +1 -5
  32. package/dist/lib/components/Header.js.map +1 -1
  33. package/dist/lib/components/MobileTopNavigation.js +4 -1
  34. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  35. package/dist/lib/components/Search.js +1 -1
  36. package/dist/lib/components/Search.js.map +1 -1
  37. package/dist/lib/components/SlotletProvider.d.ts +6 -2
  38. package/dist/lib/components/SlotletProvider.js +3 -5
  39. package/dist/lib/components/SlotletProvider.js.map +1 -1
  40. package/dist/lib/components/TopNavigation.d.ts +3 -0
  41. package/dist/lib/components/TopNavigation.js +13 -30
  42. package/dist/lib/components/TopNavigation.js.map +1 -1
  43. package/dist/lib/components/context/ZudokuContext.d.ts +11 -4
  44. package/dist/lib/components/context/ZudokuContext.js +20 -26
  45. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  46. package/dist/lib/components/index.d.ts +17 -8
  47. package/dist/lib/components/index.js +10 -3
  48. package/dist/lib/components/index.js.map +1 -1
  49. package/dist/lib/components/navigation/Sidebar.js +3 -3
  50. package/dist/lib/components/navigation/Sidebar.js.map +1 -1
  51. package/dist/lib/components/navigation/SidebarCategory.js +20 -7
  52. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  53. package/dist/lib/components/navigation/SidebarItem.js +14 -5
  54. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  55. package/dist/lib/components/navigation/utils.js +14 -10
  56. package/dist/lib/components/navigation/utils.js.map +1 -1
  57. package/dist/lib/core/DevPortalContext.d.ts +7 -3
  58. package/dist/lib/core/DevPortalContext.js.map +1 -1
  59. package/dist/lib/core/plugins.d.ts +0 -1
  60. package/dist/lib/core/plugins.js.map +1 -1
  61. package/dist/lib/errors/ErrorAlert.d.ts +1 -1
  62. package/dist/lib/errors/ErrorAlert.js +8 -3
  63. package/dist/lib/errors/ErrorAlert.js.map +1 -1
  64. package/dist/lib/plugins/custom-pages/CustomPage.d.ts +2 -0
  65. package/dist/lib/plugins/custom-pages/CustomPage.js +11 -0
  66. package/dist/lib/plugins/custom-pages/CustomPage.js.map +1 -0
  67. package/dist/lib/plugins/custom-pages/index.d.ts +8 -6
  68. package/dist/lib/plugins/custom-pages/index.js +3 -4
  69. package/dist/lib/plugins/custom-pages/index.js.map +1 -1
  70. package/dist/lib/plugins/markdown/MdxPage.js +1 -1
  71. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  72. package/dist/lib/plugins/markdown/generateRoutes.d.ts +3 -0
  73. package/dist/lib/plugins/markdown/generateRoutes.js +21 -0
  74. package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -0
  75. package/dist/lib/plugins/markdown/index.d.ts +6 -5
  76. package/dist/lib/plugins/markdown/index.js +3 -31
  77. package/dist/lib/plugins/markdown/index.js.map +1 -1
  78. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  79. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  80. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  81. package/dist/lib/plugins/openapi/client/worker.js.map +1 -1
  82. package/dist/lib/plugins/openapi/index.js.map +1 -1
  83. package/dist/lib/plugins/openapi/playground/Playground.js +1 -0
  84. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  85. package/dist/lib/util/invariant.d.ts +9 -0
  86. package/dist/lib/util/invariant.js +7 -3
  87. package/dist/lib/util/invariant.js.map +1 -1
  88. package/dist/lib/util/useExposedProps.d.ts +2 -0
  89. package/dist/lib/util/useExposedProps.js +8 -0
  90. package/dist/lib/util/useExposedProps.js.map +1 -0
  91. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  92. package/dist/vite/build.js +2 -7
  93. package/dist/vite/build.js.map +1 -1
  94. package/dist/vite/config.js +1 -1
  95. package/dist/vite/config.js.map +1 -1
  96. package/dist/vite/dev-server.js +1 -1
  97. package/dist/vite/dev-server.js.map +1 -1
  98. package/dist/vite/plugin-config-reload.js.map +1 -1
  99. package/dist/vite/plugin-docs.js +26 -26
  100. package/dist/vite/plugin-docs.js.map +1 -1
  101. package/dist/vite/plugin-mdx.js +17 -0
  102. package/dist/vite/plugin-mdx.js.map +1 -1
  103. package/dist/vite/plugin-search.d.ts +3 -0
  104. package/dist/vite/plugin-search.js +26 -0
  105. package/dist/vite/plugin-search.js.map +1 -0
  106. package/dist/vite/plugin-sidebar.js +2 -5
  107. package/dist/vite/plugin-sidebar.js.map +1 -1
  108. package/dist/vite/plugin.js +2 -0
  109. package/dist/vite/plugin.js.map +1 -1
  110. package/dist/vite/prerender.d.ts +1 -5
  111. package/dist/vite/prerender.js +5 -6
  112. package/dist/vite/prerender.js.map +1 -1
  113. package/lib/{AuthenticationPlugin-Cnqy9csQ.js → AuthenticationPlugin-tBvLKsFg.js} +3 -3
  114. package/lib/{AuthenticationPlugin-Cnqy9csQ.js.map → AuthenticationPlugin-tBvLKsFg.js.map} +1 -1
  115. package/lib/{CategoryHeading-C7VfgpFZ.js → CategoryHeading-D2WS6sRI.js} +2 -2
  116. package/lib/{CategoryHeading-C7VfgpFZ.js.map → CategoryHeading-D2WS6sRI.js.map} +1 -1
  117. package/lib/ClientOnly-CVN6leDu.js +11 -0
  118. package/lib/ClientOnly-CVN6leDu.js.map +1 -0
  119. package/lib/{DeveloperHint-CNyuFROc.js → DeveloperHint-CRiZjqd2.js} +2 -2
  120. package/lib/{DeveloperHint-CNyuFROc.js.map → DeveloperHint-CRiZjqd2.js.map} +1 -1
  121. package/lib/{Input-x-t53FyR.js → Input-CO-1DOZa.js} +4 -4
  122. package/lib/{Input-x-t53FyR.js.map → Input-CO-1DOZa.js.map} +1 -1
  123. package/lib/{Markdown-C-0TaxoY.js → Markdown-DM4zv3MA.js} +9 -8
  124. package/lib/{Markdown-C-0TaxoY.js.map → Markdown-DM4zv3MA.js.map} +1 -1
  125. package/lib/{MdxPage-C5I9c7R1.js → MdxPage-tWI_P8wP.js} +27 -28
  126. package/lib/MdxPage-tWI_P8wP.js.map +1 -0
  127. package/lib/{OperationList-qsBOguHS.js → OperationList-Cd3lue0b.js} +11 -12
  128. package/lib/OperationList-Cd3lue0b.js.map +1 -0
  129. package/lib/{Route-DlKvXPAO.js → Route-DI0Y0pIV.js} +3 -3
  130. package/lib/{Route-DlKvXPAO.js.map → Route-DI0Y0pIV.js.map} +1 -1
  131. package/lib/SlotletProvider-CBqY8mp6.js +241 -0
  132. package/lib/SlotletProvider-CBqY8mp6.js.map +1 -0
  133. package/lib/{SidebarBadge-DaA0-bFW.js → Spinner-DFQhPMBl.js} +60 -58
  134. package/lib/Spinner-DFQhPMBl.js.map +1 -0
  135. package/lib/{ZudokuContext-DSipF8sq.js → ZudokuContext-DEoP3GGJ.js} +411 -460
  136. package/lib/ZudokuContext-DEoP3GGJ.js.map +1 -0
  137. package/lib/_commonjsHelpers-BkfeUUK-.js +29 -0
  138. package/lib/_commonjsHelpers-BkfeUUK-.js.map +1 -0
  139. package/lib/assets/{worker-DaFlmuyf.js → worker-Bf8vjASY.js} +2306 -2220
  140. package/lib/assets/{worker-DaFlmuyf.js.map → worker-Bf8vjASY.js.map} +1 -1
  141. package/lib/index-Bn6Lc9tq.js +9 -0
  142. package/lib/{index-DJqnphbT.js.map → index-Bn6Lc9tq.js.map} +1 -1
  143. package/lib/{index-BIl-R3aH.js → index-Bs9roz8y.js} +690 -669
  144. package/lib/index-Bs9roz8y.js.map +1 -0
  145. package/lib/{index-Dssw7Gff.js → index-CBr6BM_4.js} +13 -13
  146. package/lib/index-CBr6BM_4.js.map +1 -0
  147. package/lib/{index-SrtqdZ3j.js → index-CRo94sKK.js} +8 -6
  148. package/lib/{index-SrtqdZ3j.js.map → index-CRo94sKK.js.map} +1 -1
  149. package/lib/{index-D06ATMgg.js → index-LNp6rxyU.js} +2 -2
  150. package/lib/{index-D06ATMgg.js.map → index-LNp6rxyU.js.map} +1 -1
  151. package/lib/{index-CKmSo0py.js → index-UUT9q9f9.js} +3 -3
  152. package/lib/{index-CKmSo0py.js.map → index-UUT9q9f9.js.map} +1 -1
  153. package/lib/invariant-Caa8-XvF.js +26 -0
  154. package/lib/invariant-Caa8-XvF.js.map +1 -0
  155. package/lib/joinPath-B7kNnUX4.js +8 -0
  156. package/lib/joinPath-B7kNnUX4.js.map +1 -0
  157. package/lib/{router-Oe6YmY6B.js → router-BsfSoK2j.js} +3 -3
  158. package/lib/{router-Oe6YmY6B.js.map → router-BsfSoK2j.js.map} +1 -1
  159. package/lib/urql-core-KJnLL26g.js.map +1 -1
  160. package/lib/useExposedProps-B9K-9GTc.js +9 -0
  161. package/lib/useExposedProps-B9K-9GTc.js.map +1 -0
  162. package/lib/{AnchorLink-DovtSBJk.js → utils-G5XSiZc9.js} +275 -231
  163. package/lib/utils-G5XSiZc9.js.map +1 -0
  164. package/lib/zudoku.auth-clerk.js +1 -1
  165. package/lib/zudoku.auth-openid.js +477 -483
  166. package/lib/zudoku.auth-openid.js.map +1 -1
  167. package/lib/zudoku.components.js +937 -935
  168. package/lib/zudoku.components.js.map +1 -1
  169. package/lib/zudoku.openapi-worker.js +2354 -2268
  170. package/lib/zudoku.openapi-worker.js.map +1 -1
  171. package/lib/zudoku.plugin-api-keys.js +60 -70
  172. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  173. package/lib/zudoku.plugin-custom-pages.js +16 -8
  174. package/lib/zudoku.plugin-custom-pages.js.map +1 -1
  175. package/lib/zudoku.plugin-markdown.js +27 -93
  176. package/lib/zudoku.plugin-markdown.js.map +1 -1
  177. package/lib/zudoku.plugin-openapi.js +7 -7
  178. package/lib/zudoku.plugin-redirect.js +1 -1
  179. package/lib/zudoku.plugin-search-inkeep.js +9 -13
  180. package/lib/zudoku.plugin-search-inkeep.js.map +1 -1
  181. package/package.json +1 -1
  182. package/src/app/entry.client.tsx +2 -4
  183. package/src/app/entry.server.tsx +0 -4
  184. package/src/app/main.css +0 -5
  185. package/src/app/main.tsx +27 -13
  186. package/src/app/standalone.tsx +1 -1
  187. package/src/lib/authentication/components/CallbackHandler.tsx +20 -51
  188. package/src/lib/authentication/hook.ts +1 -1
  189. package/src/lib/components/Header.tsx +4 -12
  190. package/src/lib/components/MobileTopNavigation.tsx +9 -3
  191. package/src/lib/components/Search.tsx +1 -1
  192. package/src/lib/components/SlotletProvider.tsx +14 -7
  193. package/src/lib/components/TopNavigation.tsx +37 -58
  194. package/src/lib/components/context/ZudokuContext.ts +20 -28
  195. package/src/lib/components/index.ts +13 -4
  196. package/src/lib/components/navigation/Sidebar.tsx +6 -6
  197. package/src/lib/components/navigation/SidebarCategory.tsx +37 -27
  198. package/src/lib/components/navigation/SidebarItem.tsx +19 -18
  199. package/src/lib/components/navigation/utils.ts +16 -11
  200. package/src/lib/core/DevPortalContext.ts +7 -3
  201. package/src/lib/core/plugins.ts +0 -2
  202. package/src/lib/errors/ErrorAlert.tsx +18 -5
  203. package/src/lib/plugins/custom-pages/CustomPage.tsx +18 -0
  204. package/src/lib/plugins/custom-pages/index.tsx +11 -9
  205. package/src/lib/plugins/markdown/MdxPage.tsx +10 -8
  206. package/src/lib/plugins/markdown/generateRoutes.tsx +38 -0
  207. package/src/lib/plugins/markdown/index.tsx +12 -49
  208. package/src/lib/plugins/openapi/Endpoint.tsx +2 -2
  209. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +1 -1
  210. package/src/lib/plugins/openapi/Sidecar.tsx +1 -1
  211. package/src/lib/plugins/openapi/client/worker.ts +2 -2
  212. package/src/lib/plugins/openapi/index.tsx +1 -1
  213. package/src/lib/plugins/openapi/playground/Playground.tsx +1 -0
  214. package/src/lib/util/invariant.ts +15 -3
  215. package/src/lib/util/useExposedProps.tsx +10 -0
  216. package/src/lib/util/useScrollToAnchor.ts +1 -1
  217. package/dist/lib/plugins/markdown/resolver.d.ts +0 -38
  218. package/dist/lib/plugins/markdown/resolver.js +0 -75
  219. package/dist/lib/plugins/markdown/resolver.js.map +0 -1
  220. package/dist/vite/debug.d.ts +0 -1
  221. package/dist/vite/debug.js +0 -10
  222. package/dist/vite/debug.js.map +0 -1
  223. package/lib/AnchorLink-DovtSBJk.js.map +0 -1
  224. package/lib/ErrorPage-CUz-Zzmx.js +0 -16
  225. package/lib/ErrorPage-CUz-Zzmx.js.map +0 -1
  226. package/lib/MdxPage-C5I9c7R1.js.map +0 -1
  227. package/lib/OperationList-qsBOguHS.js.map +0 -1
  228. package/lib/SidebarBadge-DaA0-bFW.js.map +0 -1
  229. package/lib/SlotletProvider-BGEs7yyu.js +0 -240
  230. package/lib/SlotletProvider-BGEs7yyu.js.map +0 -1
  231. package/lib/Spinner-3cQDBVGr.js +0 -7
  232. package/lib/Spinner-3cQDBVGr.js.map +0 -1
  233. package/lib/ZudokuContext-DSipF8sq.js.map +0 -1
  234. package/lib/__vite-browser-external-BYRIRx8p.js +0 -9
  235. package/lib/__vite-browser-external-BYRIRx8p.js.map +0 -1
  236. package/lib/index-BIl-R3aH.js.map +0 -1
  237. package/lib/index-DJqnphbT.js +0 -35
  238. package/lib/index-Dssw7Gff.js.map +0 -1
  239. package/src/lib/plugins/markdown/resolver.ts +0 -92
@@ -1,9 +1,7 @@
1
1
  import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { createContext, useContext } from "react";
3
- import { matchPath, useLocation } from "react-router-dom";
3
+ import { useLocation } from "react-router-dom";
4
4
  import { DevPortalContext } from "../../core/DevPortalContext.js";
5
- import { joinPath } from "../../util/joinPath.js";
6
- import { traverseSidebar } from "../navigation/utils.js";
7
5
 
8
6
  export const ZudokuReactContext = createContext<DevPortalContext | undefined>(
9
7
  undefined,
@@ -27,40 +25,34 @@ export const useApiIdentities = () => {
27
25
  });
28
26
  };
29
27
 
30
- export const useCurrentNavigation = () => {
31
- const { getPluginSidebar, sidebars, topNavigation } = useZudoku();
28
+ export const useTopNavigationItem = () => {
29
+ const { topNavigation } = useZudoku();
32
30
  const location = useLocation();
33
31
 
34
- const currentSidebarItem = Object.entries(sidebars).find(([, sidebar]) => {
35
- return traverseSidebar(sidebar, (item) => {
36
- const itemId =
37
- item.type === "doc"
38
- ? joinPath(item.id)
39
- : item.type === "category" && item.link
40
- ? joinPath(item.link.id)
41
- : undefined;
32
+ const firstPart = location.pathname.split("/").at(1);
33
+ if (!firstPart) return;
42
34
 
43
- if (itemId === location.pathname) {
44
- return item;
45
- }
46
- });
47
- });
48
- const currentTopNavItem =
49
- topNavigation.find((t) => t.id === currentSidebarItem?.[0]) ??
50
- topNavigation.find((item) => matchPath(item.id, location.pathname));
35
+ return topNavigation.find((item) => item.id === firstPart);
36
+ };
37
+
38
+ export const useNavigation = () => {
39
+ const { getPluginSidebar, sidebars } = useZudoku();
40
+ const navItem = useTopNavigationItem();
41
+ const path = navItem?.id;
42
+ const currentSidebar = path ? (sidebars[path] ?? []) : [];
43
+ const location = useLocation();
51
44
 
52
45
  return useSuspenseQuery({
53
46
  queryFn: async () => {
54
- const pluginSidebar = await getPluginSidebar(location.pathname);
47
+ const pluginSidebar = path
48
+ ? await getPluginSidebar(path)
49
+ : await getPluginSidebar(location.pathname);
55
50
 
56
51
  return {
57
- sidebar: [
58
- ...(currentSidebarItem ? currentSidebarItem[1] : []),
59
- ...pluginSidebar,
60
- ],
61
- topNavItem: currentTopNavItem,
52
+ items: [...currentSidebar, ...pluginSidebar],
53
+ currentTopNavItem: navItem,
62
54
  };
63
55
  },
64
- queryKey: ["navigation", location.pathname],
56
+ queryKey: ["navigation", path],
65
57
  });
66
58
  };
@@ -1,6 +1,7 @@
1
1
  import { useMDXComponents as useMDXComponentsImport } from "@mdx-js/react";
2
2
  import { Helmet } from "@zudoku/react-helmet-async";
3
3
  import { Link as LinkImport } from "react-router-dom";
4
+ import { useAuthState } from "../authentication/state.js";
4
5
  import { RouterError as RouterErrorImport } from "../errors/RouterError.js";
5
6
  import { ServerError as ServerErrorImport } from "../errors/ServerError.js";
6
7
  import { Button as ButtonImport } from "../ui/Button.js";
@@ -9,17 +10,25 @@ import {
9
10
  Bootstrap as BootstrapImport,
10
11
  BootstrapStatic as BootstrapStaticImport,
11
12
  } from "./Bootstrap.js";
13
+ import { ClientOnly as ClientOnlyImport } from "./ClientOnly.js";
12
14
  import { DevPortal as DevPortalImport } from "./DevPortal.js";
13
15
  import { Layout as LayoutImport } from "./Layout.js";
14
-
16
+ import { useZudoku as useZudokuImport } from "./context/ZudokuContext.js";
15
17
  export const useMDXComponents = /*@__PURE__*/ useMDXComponentsImport;
16
- export const Callout = /*@__PURE__*/ CalloutImport;
17
18
  export const DevPortal = /*@__PURE__*/ DevPortalImport;
18
19
  export const Layout = /*@__PURE__*/ LayoutImport;
19
- export const Link: typeof LinkImport = /*@__PURE__*/ LinkImport;
20
20
  export const RouterError = /*@__PURE__*/ RouterErrorImport;
21
21
  export const ServerError = /*@__PURE__*/ ServerErrorImport;
22
22
  export const Bootstrap = /*@__PURE__*/ BootstrapImport;
23
23
  export const BootstrapStatic = /*@__PURE__*/ BootstrapStaticImport;
24
- export const Button = /*@__PURE__*/ ButtonImport;
24
+
25
25
  export const Head = /*@__PURE__*/ Helmet;
26
+
27
+ export const useZudoku = /*@__PURE__*/ useZudokuImport;
28
+ export const useAuth = /*@__PURE__*/ useAuthState;
29
+ export const Zudoku = /*@__PURE__*/ DevPortalImport;
30
+
31
+ export const Callout = /*@__PURE__*/ CalloutImport;
32
+ export const ClientOnly = /*@__PURE__*/ ClientOnlyImport;
33
+ export const Button = /*@__PURE__*/ ButtonImport;
34
+ export const Link: typeof LinkImport = /*@__PURE__*/ LinkImport;
@@ -2,35 +2,35 @@ import { useRef } from "react";
2
2
 
3
3
  import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
4
4
  import { DrawerContent, DrawerTitle } from "../../ui/Drawer.js";
5
- import { useCurrentNavigation } from "../context/ZudokuContext.js";
5
+ import { useNavigation } from "../context/ZudokuContext.js";
6
6
  import { Slotlet } from "../SlotletProvider.js";
7
7
  import { SidebarItem } from "./SidebarItem.js";
8
8
  import { SidebarWrapper } from "./SidebarWrapper.js";
9
9
 
10
10
  export const Sidebar = () => {
11
11
  const navRef = useRef<HTMLDivElement | null>(null);
12
- const navigation = useCurrentNavigation();
12
+ const navigation = useNavigation();
13
13
 
14
14
  return (
15
15
  <>
16
16
  <SidebarWrapper
17
17
  ref={navRef}
18
- pushMainContent={navigation.data.sidebar.length > 0}
18
+ pushMainContent={navigation.data.items.length > 0}
19
19
  >
20
20
  <Slotlet name="zudoku-before-navigation" />
21
- {navigation.data.sidebar.map((item) => (
21
+ {navigation.data.items.map((item) => (
22
22
  <SidebarItem key={item.label} item={item} />
23
23
  ))}
24
24
  <Slotlet name="zudoku-after-navigation" />
25
25
  </SidebarWrapper>
26
26
  <DrawerContent
27
- className="lg:hidden h-screen left-0 p-6 w-[320px] rounded-none"
27
+ className="lg:hidden h-screen left-0 p-6 w-[320px] rounded-none overflow-auto"
28
28
  aria-describedby={undefined}
29
29
  >
30
30
  <VisuallyHidden>
31
31
  <DrawerTitle>Sidebar</DrawerTitle>
32
32
  </VisuallyHidden>
33
- {navigation.data.sidebar.map((item) => (
33
+ {navigation.data.items.map((item) => (
34
34
  <SidebarItem key={item.label} item={item} />
35
35
  ))}
36
36
  </DrawerContent>
@@ -1,10 +1,11 @@
1
1
  import * as Collapsible from "@radix-ui/react-collapsible";
2
2
  import { ChevronRightIcon } from "lucide-react";
3
3
  import { useEffect, useState } from "react";
4
- import { NavLink } from "react-router-dom";
4
+ import { NavLink, useMatch } from "react-router-dom";
5
5
  import type { SidebarItemCategory } from "../../../config/validators/SidebarSchema.js";
6
6
  import { cn } from "../../util/cn.js";
7
7
  import { joinPath } from "../../util/joinPath.js";
8
+ import { useTopNavigationItem } from "../context/ZudokuContext.js";
8
9
  import { navigationListItem, SidebarItem } from "./SidebarItem.js";
9
10
  import { useIsCategoryOpen } from "./utils.js";
10
11
 
@@ -15,6 +16,7 @@ export const SidebarCategory = ({
15
16
  category: SidebarItemCategory;
16
17
  level: number;
17
18
  }) => {
19
+ const topNavItem = useTopNavigationItem();
18
20
  const isCategoryOpen = useIsCategoryOpen(category);
19
21
  const [hasInteracted, setHasInteracted] = useState(false);
20
22
 
@@ -24,6 +26,7 @@ export const SidebarCategory = ({
24
26
  !isCollapsible || !isCollapsed || isCategoryOpen,
25
27
  );
26
28
  const [open, setOpen] = useState(isDefaultOpen);
29
+ const isActive = useMatch(joinPath(topNavItem?.id, category.link?.id));
27
30
 
28
31
  useEffect(() => {
29
32
  // this is triggered when an item from the sidebar is clicked
@@ -54,46 +57,54 @@ export const SidebarCategory = ({
54
57
 
55
58
  return (
56
59
  <Collapsible.Root
57
- className={cn("flex flex-col", level === 0 && "-mx-[--padding-nav-item]")}
60
+ className="flex flex-col"
58
61
  defaultOpen={isDefaultOpen}
59
62
  open={open}
60
63
  onOpenChange={() => setOpen(true)}
61
64
  >
62
65
  <Collapsible.Trigger className="group" asChild disabled={!isCollapsible}>
63
66
  <div
64
- className={cn(
65
- "text-start",
66
- navigationListItem({ isActive: false, isTopLevel: level === 0 }),
67
- isCollapsible
68
- ? "cursor-pointer"
69
- : "cursor-default hover:bg-transparent",
70
- )}
67
+ onClick={() => setHasInteracted(true)}
68
+ className={navigationListItem({
69
+ isActive: false,
70
+ isTopLevel: level === 0,
71
+ className: [
72
+ "text-start",
73
+ isCollapsible
74
+ ? "cursor-pointer"
75
+ : "cursor-default hover:bg-transparent",
76
+ ],
77
+ })}
71
78
  >
72
79
  {category.icon && (
73
80
  <category.icon
74
81
  size={16}
75
- className="align-[-0.125em] -translate-x-1"
82
+ className={cn(
83
+ "align-[-0.125em] -translate-x-1",
84
+ isActive && "text-primary",
85
+ )}
76
86
  />
77
87
  )}
78
88
  {category.link?.type === "doc" ? (
79
89
  <NavLink
80
- to={joinPath(category.link.id)}
90
+ to={joinPath(topNavItem?.id, category.link.id)}
81
91
  className="flex-1"
82
- onClick={() => setHasInteracted(true)}
92
+ onClick={() => {
93
+ // if it is the current path and closed then open it because there's no path change to trigger the open
94
+ if (isActive && !open) {
95
+ setOpen(true);
96
+ }
97
+ }}
83
98
  >
84
- {({ isActive }) => (
85
- <div
86
- className={cn(
87
- "flex items-center gap-2 justify-between w-full",
88
- isActive
89
- ? "text-primary font-medium"
90
- : "text-foreground/80",
91
- )}
92
- >
93
- <div className="truncate">{category.label}</div>
94
- {ToggleButton}
95
- </div>
96
- )}
99
+ <div
100
+ className={cn(
101
+ "flex items-center gap-2 justify-between w-full",
102
+ isActive ? "text-primary" : "text-foreground/80",
103
+ )}
104
+ >
105
+ <div className="truncate">{category.label}</div>
106
+ {ToggleButton}
107
+ </div>
97
108
  </NavLink>
98
109
  ) : (
99
110
  <div className="flex items-center justify-between w-full">
@@ -107,10 +118,9 @@ export const SidebarCategory = ({
107
118
  className={cn(
108
119
  // CollapsibleContent class is used to animate and it should only be applied when the user has triggered the toggle
109
120
  hasInteracted && "CollapsibleContent",
110
- "ms-[calc(var(--padding-nav-item)*1.125)]",
111
121
  )}
112
122
  >
113
- <ul className="mt-1 border-l ps-2">
123
+ <ul className="mt-1 border-l ms-0.5">
114
124
  {category.items.map((item) => (
115
125
  <SidebarItem
116
126
  key={
@@ -3,10 +3,10 @@ import { ExternalLinkIcon } from "lucide-react";
3
3
  import { NavLink, useSearchParams } from "react-router-dom";
4
4
 
5
5
  import type { SidebarItem as SidebarItemType } from "../../../config/validators/SidebarSchema.js";
6
- import { cn } from "../../util/cn.js";
7
6
  import { joinPath } from "../../util/joinPath.js";
8
7
  import { AnchorLink } from "../AnchorLink.js";
9
8
  import { useViewportAnchor } from "../context/ViewportAnchorContext.js";
9
+ import { useTopNavigationItem } from "../context/ZudokuContext.js";
10
10
  import { SidebarBadge } from "./SidebarBadge.js";
11
11
  import { SidebarCategory } from "./SidebarCategory.js";
12
12
 
@@ -15,7 +15,8 @@ export const navigationListItem = cva(
15
15
  {
16
16
  variants: {
17
17
  isTopLevel: {
18
- true: "font-semibold",
18
+ true: "font-medium -mx-[--padding-nav-item]",
19
+ false: "-mr-[--padding-nav-item] ml-[--padding-nav-item]",
19
20
  },
20
21
  isActive: {
21
22
  true: "text-primary font-medium",
@@ -26,6 +27,9 @@ export const navigationListItem = cva(
26
27
  false: "",
27
28
  },
28
29
  },
30
+ defaultVariants: {
31
+ isActive: false,
32
+ },
29
33
  },
30
34
  );
31
35
 
@@ -39,6 +43,7 @@ export const SidebarItem = ({
39
43
  basePath?: string;
40
44
  level?: number;
41
45
  }) => {
46
+ const topNavItem = useTopNavigationItem();
42
47
  const { activeAnchor } = useViewportAnchor();
43
48
  const [searchParams] = useSearchParams();
44
49
 
@@ -51,12 +56,12 @@ export const SidebarItem = ({
51
56
  className={({ isActive }) =>
52
57
  navigationListItem({ isActive, isTopLevel: level === 0 })
53
58
  }
54
- to={joinPath(item.id)}
59
+ to={joinPath(topNavItem?.id, item.id)}
55
60
  >
56
61
  {item.icon && <item.icon size={16} className="align-[-0.125em]" />}
57
62
  {item.badge ? (
58
63
  <>
59
- <span className="truncate" title={item.label}>
64
+ <span className="truncate flex-1" title={item.label}>
60
65
  {item.label}
61
66
  </span>
62
67
  <SidebarBadge {...item.badge} />
@@ -71,13 +76,11 @@ export const SidebarItem = ({
71
76
  <AnchorLink
72
77
  to={{ hash: item.href, search: searchParams.toString() }}
73
78
  {...{ [DATA_ANCHOR_ATTR]: item.href.slice(1) }}
74
- className={cn(
75
- "flex gap-2.5 justify-between",
76
- level === 0 && "-mx-[--padding-nav-item]",
77
- navigationListItem({
78
- isActive: item.href.slice(1) === activeAnchor,
79
- }),
80
- )}
79
+ className={navigationListItem({
80
+ isActive: item.href.slice(1) === activeAnchor,
81
+ isTopLevel: level === 0,
82
+ className: item.badge?.placement !== "start" && "justify-between",
83
+ })}
81
84
  >
82
85
  {item.badge ? (
83
86
  <>
@@ -92,7 +95,9 @@ export const SidebarItem = ({
92
95
  </AnchorLink>
93
96
  ) : !item.href.startsWith("http") ? (
94
97
  <NavLink
95
- className={cn("flex gap-2.5 justify-between", navigationListItem())}
98
+ className={navigationListItem({
99
+ className: item.badge?.placement !== "start" && "justify-between",
100
+ })}
96
101
  to={item.href}
97
102
  >
98
103
  {item.badge ? (
@@ -108,10 +113,7 @@ export const SidebarItem = ({
108
113
  </NavLink>
109
114
  ) : (
110
115
  <a
111
- className={cn(
112
- navigationListItem({ isTopLevel: level === 0 }),
113
- "block",
114
- )}
116
+ className={navigationListItem({ isTopLevel: level === 0 })}
115
117
  href={item.href}
116
118
  target="_blank"
117
119
  rel="noopener noreferrer"
@@ -119,8 +121,7 @@ export const SidebarItem = ({
119
121
  <span className="whitespace-normal">{item.label}</span>
120
122
  {/* This prevents that the icon would be positioned in its own line if the text fills a line entirely */}
121
123
  <span className="whitespace-nowrap">
122
- &nbsp;
123
- <ExternalLinkIcon className="inline ml-1" size={12} />
124
+ <ExternalLinkIcon className="inline -translate-y-0.5" size={12} />
124
125
  </span>
125
126
  </a>
126
127
  );
@@ -4,7 +4,7 @@ import type {
4
4
  SidebarItemCategory,
5
5
  } from "../../../config/validators/SidebarSchema.js";
6
6
  import { joinPath } from "../../util/joinPath.js";
7
- import { useCurrentNavigation } from "../context/ZudokuContext.js";
7
+ import { useTopNavigationItem, useZudoku } from "../context/ZudokuContext.js";
8
8
 
9
9
  export type TraverseCallback<T> = (
10
10
  item: SidebarItem,
@@ -42,12 +42,15 @@ export const traverseSidebarItem = <T>(
42
42
 
43
43
  export const useCurrentItem = () => {
44
44
  const location = useLocation();
45
- const nav = useCurrentNavigation();
46
-
47
- const currentSidebar = nav.data.sidebar;
45
+ const topNavItem = useTopNavigationItem();
46
+ const { sidebars } = useZudoku();
47
+ const currentSidebar = topNavItem?.id ? sidebars[topNavItem.id] : [];
48
48
 
49
49
  return traverseSidebar(currentSidebar, (item) => {
50
- if (item.type === "doc" && joinPath(item.id) === location.pathname) {
50
+ if (
51
+ item.type === "doc" &&
52
+ joinPath(topNavItem?.id, item.id) === location.pathname
53
+ ) {
51
54
  return item;
52
55
  }
53
56
  });
@@ -55,17 +58,18 @@ export const useCurrentItem = () => {
55
58
 
56
59
  export const useIsCategoryOpen = (category: SidebarItemCategory) => {
57
60
  const location = useLocation();
61
+ const topNavItem = useTopNavigationItem();
58
62
 
59
63
  return traverseSidebarItem(category, (item) => {
60
64
  if (item.type === "category" && item.link) {
61
- const categoryLinkPath = joinPath(item.link.id);
65
+ const categoryLinkPath = joinPath(topNavItem?.id, item.link.id);
62
66
  if (categoryLinkPath === location.pathname) {
63
67
  return true;
64
68
  }
65
69
  }
66
70
 
67
71
  if (item.type === "doc") {
68
- const docPath = joinPath(item.id);
72
+ const docPath = joinPath(topNavItem?.id, item.id);
69
73
  if (docPath === location.pathname) {
70
74
  return true;
71
75
  }
@@ -78,8 +82,9 @@ export const usePrevNext = (): {
78
82
  next?: { label: string; id: string };
79
83
  } => {
80
84
  const currentId = useLocation().pathname;
81
- const nav = useCurrentNavigation();
82
- const currentSidebar = nav.data.sidebar;
85
+ const { sidebars } = useZudoku();
86
+ const topNavItem = useTopNavigationItem();
87
+ const currentSidebar = topNavItem?.id ? sidebars[topNavItem.id] : [];
83
88
 
84
89
  let prev;
85
90
  let next;
@@ -89,9 +94,9 @@ export const usePrevNext = (): {
89
94
  traverseSidebar(currentSidebar, (item) => {
90
95
  const itemId =
91
96
  item.type === "doc"
92
- ? joinPath(item.id)
97
+ ? joinPath(topNavItem?.id, item.id)
93
98
  : item.type === "category" && item.link
94
- ? joinPath(item.link.id)
99
+ ? joinPath(topNavItem?.id, item.link.id)
95
100
  : undefined;
96
101
 
97
102
  if (!itemId) return;
@@ -1,7 +1,6 @@
1
1
  import { QueryClient } from "@tanstack/react-query";
2
2
  import { ReactNode } from "react";
3
3
  import type { SidebarConfig } from "../../config/validators/SidebarSchema.js";
4
- import { TopNavigationItem } from "../../config/validators/validate.js";
5
4
  import { type AuthenticationProvider } from "../authentication/authentication.js";
6
5
  import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
7
6
  import { Slotlets } from "../components/SlotletProvider.js";
@@ -61,7 +60,12 @@ export type ZudokuContextOptions = {
61
60
  metadata?: Metadata;
62
61
  page?: Page;
63
62
  authentication?: AuthenticationProvider;
64
- topNavigation?: TopNavigationItem[];
63
+ topNavigation?: Array<{
64
+ id: string;
65
+ label: string;
66
+ default?: string;
67
+ display?: "auth" | "anon" | "always";
68
+ }>;
65
69
  sidebars?: SidebarConfig;
66
70
  plugins?: DevPortalPlugin[];
67
71
  slotlets?: Slotlets;
@@ -73,7 +77,7 @@ export type ZudokuContextOptions = {
73
77
 
74
78
  export class DevPortalContext {
75
79
  public plugins: NonNullable<ZudokuContextOptions["plugins"]>;
76
- public sidebars: SidebarConfig;
80
+ public sidebars: NonNullable<ZudokuContextOptions["sidebars"]>;
77
81
  public topNavigation: NonNullable<ZudokuContextOptions["topNavigation"]>;
78
82
  public meta: ZudokuContextOptions["metadata"];
79
83
  public page: ZudokuContextOptions["page"];
@@ -11,8 +11,6 @@ export type DevPortalPlugin =
11
11
  | ApiIdentityPlugin
12
12
  | SearchProviderPlugin;
13
13
 
14
- export type { RouteObject };
15
-
16
14
  export interface NavigationPlugin {
17
15
  getRoutes: () => RouteObject[];
18
16
  getSidebar?: (path: string) => Promise<Sidebar>;
@@ -1,16 +1,29 @@
1
1
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
- export function ErrorAlert({ error }: { error: any }) {
3
- const message = error?.message ?? "Something went wrong";
4
- const stack = error?.stack;
2
+ import { DeveloperHint } from "../components/DeveloperHint.js";
3
+ import { ZudokuError } from "../util/invariant.js";
4
+
5
+ export function ErrorAlert({ error }: { error: unknown }) {
6
+ const message =
7
+ error instanceof Error ? error.message : "Something went wrong";
8
+ const hint = error instanceof ZudokuError ? error.developerHint : undefined;
9
+ const title =
10
+ error instanceof ZudokuError ? error.title : "Something went wrong";
11
+ const stack = error instanceof Error ? error.stack : undefined;
12
+ const cause = error instanceof Error ? error.cause : undefined;
5
13
 
6
14
  return (
7
15
  <div className="flex h-screen max-h-screen min-h-full items-center justify-center bg-primary-background px-4 py-16 lg:px-8">
8
16
  <div className="mx-auto max-w-[85%] sm:max-w-[50%]">
9
17
  <h1 className="text-4xl font-bold tracking-tight text-h1-text sm:text-5xl">
10
- Something went wrong
18
+ {title}
11
19
  </h1>
12
20
  <p className="mt-5 text-h1-text">{message}</p>
13
- {stack ? (
21
+ {hint && <DeveloperHint className="mb-4">{hint}</DeveloperHint>}
22
+ {cause instanceof Error ? (
23
+ <pre className="mt-5 max-h-[400px] w-full overflow-scroll rounded-md border border-input-border bg-input-background p-3 text-property-name-text text-red-700">
24
+ {cause.stack}
25
+ </pre>
26
+ ) : stack ? (
14
27
  <pre className="mt-5 max-h-[400px] w-full overflow-scroll rounded-md border border-input-border bg-input-background p-3 text-property-name-text text-red-700">
15
28
  {stack}
16
29
  </pre>
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import { ProseClasses } from "../../components/Markdown.js";
3
+ import { cn } from "../../util/cn.js";
4
+ import { useExposedProps } from "../../util/useExposedProps.js";
5
+ import type { CustomPageConfig } from "./index.js";
6
+
7
+ export const CustomPage = ({
8
+ element,
9
+ render,
10
+ prose = true,
11
+ }: Omit<CustomPageConfig, "path">) => {
12
+ const slotletProps = useExposedProps();
13
+ const content = render ? React.createElement(render, slotletProps) : element;
14
+
15
+ return (
16
+ <div className={cn(prose && ProseClasses, "max-w-full")}>{content}</div>
17
+ );
18
+ };
@@ -1,22 +1,24 @@
1
- import type { ReactNode } from "react";
1
+ import { type ComponentType, type ReactNode } from "react";
2
2
  import type { RouteObject } from "react-router-dom";
3
- import { ProseClasses } from "../../components/Markdown.js";
3
+ import { type ExposedComponentProps } from "../../components/SlotletProvider.js";
4
4
  import type { DevPortalPlugin, NavigationPlugin } from "../../core/plugins.js";
5
+ import { CustomPage } from "./CustomPage.js";
5
6
 
6
- type CustomPagesConfig = Array<{
7
+ export type CustomPageConfig = {
7
8
  path: string;
8
- element: ReactNode;
9
- }>;
9
+ prose?: boolean;
10
+ element?: ReactNode;
11
+ render?: ComponentType<ExposedComponentProps>;
12
+ };
10
13
 
11
14
  export const customPagesPlugin = (
12
- config: CustomPagesConfig,
15
+ config: CustomPageConfig[],
13
16
  ): DevPortalPlugin & NavigationPlugin => {
14
17
  return {
15
18
  getRoutes: (): RouteObject[] =>
16
- config.map(({ path, element }) => ({
19
+ config.map(({ path, ...props }) => ({
17
20
  path,
18
- // TODO: we should componentize prose pages
19
- element: <div className={ProseClasses + " max-w-full"}>{element}</div>,
21
+ element: <CustomPage {...props} />,
20
22
  })),
21
23
  };
22
24
  };
@@ -68,14 +68,16 @@ export const MdxPage = ({
68
68
  "max-w-full xl:w-full xl:max-w-prose flex-1 flex-shrink pt-[--padding-content-top] pb-[--padding-content-bottom]",
69
69
  )}
70
70
  >
71
- <header>
72
- {category && <CategoryHeading>{category}</CategoryHeading>}
73
- {title && (
74
- <Heading level={1} id={slugify(title)}>
75
- {title}
76
- </Heading>
77
- )}
78
- </header>
71
+ {(category || title) && (
72
+ <header>
73
+ {category && <CategoryHeading>{category}</CategoryHeading>}
74
+ {title && (
75
+ <Heading level={1} id={slugify(title)}>
76
+ {title}
77
+ </Heading>
78
+ )}
79
+ </header>
80
+ )}
79
81
  <MdxComponent
80
82
  components={{ ...useMDXComponents(), ...MarkdownHeadings }}
81
83
  />
@@ -0,0 +1,38 @@
1
+ import { type RouteObject } from "react-router-dom";
2
+
3
+ import {
4
+ MarkdownPluginDefaultOptions,
5
+ MarkdownPluginOptions,
6
+ } from "./index.js";
7
+
8
+ export const generateRoutes = (
9
+ markdownFiles: MarkdownPluginOptions["markdownFiles"],
10
+ filesPath: string,
11
+ defaultOptions?: MarkdownPluginDefaultOptions,
12
+ ): RouteObject[] =>
13
+ Object.entries(markdownFiles).flatMap(([file, importPromise]) => {
14
+ let rootDir = filesPath.split("**")[0];
15
+ rootDir = rootDir.replace("/**", "/");
16
+ const re = new RegExp(`^${rootDir}(.*).mdx?`);
17
+ const match = file.match(re);
18
+ const fsPath = match?.at(1);
19
+
20
+ if (!fsPath) return [];
21
+
22
+ return {
23
+ path: fsPath,
24
+ lazy: async () => {
25
+ const { MdxPage } = await import("./MdxPage.js");
26
+ const { default: Component, ...props } = await importPromise();
27
+ return {
28
+ element: (
29
+ <MdxPage
30
+ mdxComponent={Component}
31
+ {...props}
32
+ defaultOptions={defaultOptions}
33
+ />
34
+ ),
35
+ };
36
+ },
37
+ } satisfies RouteObject;
38
+ });