zudoku 0.0.0-e2f3304 → 0.0.0-e4b82fa

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 (289) hide show
  1. package/README.md +121 -0
  2. package/dist/app/main.js +5 -7
  3. package/dist/app/main.js.map +1 -1
  4. package/dist/cli/dev/handler.js +2 -2
  5. package/dist/cli/dev/handler.js.map +1 -1
  6. package/dist/config/validators/InputSidebarSchema.d.ts +19 -4
  7. package/dist/config/validators/InputSidebarSchema.js +7 -28
  8. package/dist/config/validators/InputSidebarSchema.js.map +1 -1
  9. package/dist/config/validators/SidebarSchema.js +5 -6
  10. package/dist/config/validators/SidebarSchema.js.map +1 -1
  11. package/dist/config/validators/validate.d.ts +55 -29
  12. package/dist/config/validators/validate.js +10 -2
  13. package/dist/config/validators/validate.js.map +1 -1
  14. package/dist/index.d.ts +1 -0
  15. package/dist/lib/authentication/hook.d.ts +1 -1
  16. package/dist/lib/authentication/hook.js +1 -1
  17. package/dist/lib/authentication/hook.js.map +1 -1
  18. package/dist/lib/authentication/state.d.ts +16 -0
  19. package/dist/lib/authentication/state.js +5 -0
  20. package/dist/lib/authentication/state.js.map +1 -1
  21. package/dist/lib/components/Banner.js +7 -1
  22. package/dist/lib/components/Banner.js.map +1 -1
  23. package/dist/lib/components/ErrorPage.js +1 -2
  24. package/dist/lib/components/ErrorPage.js.map +1 -1
  25. package/dist/lib/components/Header.js +1 -1
  26. package/dist/lib/components/Header.js.map +1 -1
  27. package/dist/lib/components/InlineCode.d.ts +2 -1
  28. package/dist/lib/components/InlineCode.js +9 -1
  29. package/dist/lib/components/InlineCode.js.map +1 -1
  30. package/dist/lib/components/Layout.js +1 -1
  31. package/dist/lib/components/Layout.js.map +1 -1
  32. package/dist/lib/components/MobileTopNavigation.js +5 -1
  33. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  34. package/dist/lib/components/Search.js +1 -1
  35. package/dist/lib/components/Search.js.map +1 -1
  36. package/dist/lib/components/SlotletProvider.d.ts +9 -2
  37. package/dist/lib/components/SlotletProvider.js +4 -2
  38. package/dist/lib/components/SlotletProvider.js.map +1 -1
  39. package/dist/lib/components/TopNavigation.d.ts +3 -0
  40. package/dist/lib/components/TopNavigation.js +9 -1
  41. package/dist/lib/components/TopNavigation.js.map +1 -1
  42. package/dist/lib/components/context/ZudokuContext.d.ts +2 -0
  43. package/dist/lib/components/index.d.ts +3 -1
  44. package/dist/lib/components/index.js +2 -0
  45. package/dist/lib/components/index.js.map +1 -1
  46. package/dist/lib/components/navigation/Sidebar.js +1 -1
  47. package/dist/lib/components/navigation/Sidebar.js.map +1 -1
  48. package/dist/lib/components/navigation/SidebarCategory.js +18 -7
  49. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -1
  50. package/dist/lib/components/navigation/SidebarItem.js +12 -5
  51. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  52. package/dist/lib/core/DevPortalContext.d.ts +2 -1
  53. package/dist/lib/core/DevPortalContext.js.map +1 -1
  54. package/dist/lib/oas/graphql/index.d.ts +2 -1
  55. package/dist/lib/oas/graphql/index.js +23 -15
  56. package/dist/lib/oas/graphql/index.js.map +1 -1
  57. package/dist/lib/oas/parser/index.d.ts +1 -0
  58. package/dist/lib/oas/parser/index.js.map +1 -1
  59. package/dist/lib/plugins/custom-pages/CustomPage.d.ts +2 -0
  60. package/dist/lib/plugins/custom-pages/CustomPage.js +11 -0
  61. package/dist/lib/plugins/custom-pages/CustomPage.js.map +1 -0
  62. package/dist/lib/plugins/custom-pages/index.d.ts +10 -0
  63. package/dist/lib/plugins/custom-pages/index.js +11 -0
  64. package/dist/lib/plugins/custom-pages/index.js.map +1 -0
  65. package/dist/lib/plugins/markdown/MdxPage.js +2 -2
  66. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  67. package/dist/lib/plugins/openapi/Endpoint.d.ts +1 -3
  68. package/dist/lib/plugins/openapi/Endpoint.js +46 -8
  69. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  70. package/dist/lib/plugins/openapi/OperationList.js +2 -2
  71. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  72. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.d.ts +2 -1
  73. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +2 -2
  74. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  75. package/dist/lib/plugins/openapi/Route.d.ts +1 -1
  76. package/dist/lib/plugins/openapi/Route.js +1 -1
  77. package/dist/lib/plugins/openapi/Route.js.map +1 -1
  78. package/dist/lib/plugins/openapi/Sidecar.js +15 -4
  79. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  80. package/dist/lib/plugins/openapi/SimpleSelect.d.ts +2 -1
  81. package/dist/lib/plugins/openapi/SimpleSelect.js +1 -1
  82. package/dist/lib/plugins/openapi/SimpleSelect.js.map +1 -1
  83. package/dist/lib/plugins/openapi/client/createMemoryClient.js +1 -1
  84. package/dist/lib/plugins/openapi/client/createMemoryClient.js.map +1 -1
  85. package/dist/lib/plugins/openapi/client/createWorkerClient.js +1 -1
  86. package/dist/lib/plugins/openapi/client/createWorkerClient.js.map +1 -1
  87. package/dist/lib/plugins/openapi/graphql/gql.d.ts +10 -2
  88. package/dist/lib/plugins/openapi/graphql/gql.js +2 -1
  89. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  90. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +26 -0
  91. package/dist/lib/plugins/openapi/graphql/graphql.js +87 -0
  92. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  93. package/dist/lib/plugins/openapi/index.js +1 -1
  94. package/dist/lib/plugins/openapi/index.js.map +1 -1
  95. package/dist/lib/plugins/openapi/playground/Playground.d.ts +2 -1
  96. package/dist/lib/plugins/openapi/playground/Playground.js +14 -5
  97. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  98. package/dist/lib/plugins/openapi/playground/ResponseTab.js +1 -1
  99. package/dist/lib/plugins/openapi/playground/ResponseTab.js.map +1 -1
  100. package/dist/lib/plugins/openapi/playground/createUrl.js +3 -1
  101. package/dist/lib/plugins/openapi/playground/createUrl.js.map +1 -1
  102. package/dist/lib/ui/Callout.d.ts +36 -35
  103. package/dist/lib/ui/Callout.js.map +1 -1
  104. package/dist/lib/ui/Drawer.d.ts +8 -10
  105. package/dist/lib/ui/Drawer.js.map +1 -1
  106. package/dist/lib/util/useExposedProps.d.ts +2 -0
  107. package/dist/lib/util/useExposedProps.js +8 -0
  108. package/dist/lib/util/useExposedProps.js.map +1 -0
  109. package/dist/vite/config.js +14 -12
  110. package/dist/vite/config.js.map +1 -1
  111. package/dist/vite/dev-server.d.ts +6 -1
  112. package/dist/vite/dev-server.js +13 -3
  113. package/dist/vite/dev-server.js.map +1 -1
  114. package/dist/vite/plugin-api.js +4 -7
  115. package/dist/vite/plugin-api.js.map +1 -1
  116. package/dist/vite/plugin-component.js +0 -1
  117. package/dist/vite/plugin-component.js.map +1 -1
  118. package/dist/vite/plugin-custom-pages.d.ts +4 -0
  119. package/dist/vite/plugin-custom-pages.js +30 -0
  120. package/dist/vite/plugin-custom-pages.js.map +1 -0
  121. package/dist/vite/plugin-frontmatter.d.ts +2 -0
  122. package/dist/vite/plugin-frontmatter.js +30 -0
  123. package/dist/vite/plugin-frontmatter.js.map +1 -0
  124. package/dist/vite/plugin-mdx.js +17 -0
  125. package/dist/vite/plugin-mdx.js.map +1 -1
  126. package/dist/vite/plugin-search.d.ts +3 -0
  127. package/dist/vite/plugin-search.js +26 -0
  128. package/dist/vite/plugin-search.js.map +1 -0
  129. package/dist/vite/plugin-sidebar.js +14 -1
  130. package/dist/vite/plugin-sidebar.js.map +1 -1
  131. package/dist/vite/plugin.js +6 -2
  132. package/dist/vite/plugin.js.map +1 -1
  133. package/lib/{AuthenticationPlugin-Bx9FK124.js → AuthenticationPlugin-D1nhROkO.js} +3 -3
  134. package/lib/{AuthenticationPlugin-Bx9FK124.js.map → AuthenticationPlugin-D1nhROkO.js.map} +1 -1
  135. package/lib/{CategoryHeading-XnFqN2lJ.js → CategoryHeading-B7TlPvuN.js} +2 -2
  136. package/lib/{CategoryHeading-XnFqN2lJ.js.map → CategoryHeading-B7TlPvuN.js.map} +1 -1
  137. package/lib/{DeveloperHint-FBb2uXJe.js → DeveloperHint-B_QCCafl.js} +2 -2
  138. package/lib/{DeveloperHint-FBb2uXJe.js.map → DeveloperHint-B_QCCafl.js.map} +1 -1
  139. package/lib/ErrorPage-DqqAV1dr.js +16 -0
  140. package/lib/ErrorPage-DqqAV1dr.js.map +1 -0
  141. package/lib/Input-DrR6P3mq.js +2229 -0
  142. package/lib/Input-DrR6P3mq.js.map +1 -0
  143. package/lib/{Markdown-B4aR03g6.js → Markdown-Y__eLG0x.js} +1448 -1286
  144. package/lib/Markdown-Y__eLG0x.js.map +1 -0
  145. package/lib/{MdxPage-BZyQsH8Z.js → MdxPage-B4FcPIi5.js} +25 -25
  146. package/lib/MdxPage-B4FcPIi5.js.map +1 -0
  147. package/lib/{OperationList-CCPePX04.js → OperationList-C2jKCTwZ.js} +149 -108
  148. package/lib/OperationList-C2jKCTwZ.js.map +1 -0
  149. package/lib/{Route-BAHaMyCu.js → Route-DWXcQzRK.js} +3 -4
  150. package/lib/Route-DWXcQzRK.js.map +1 -0
  151. package/lib/SidebarBadge-C0iWtji9.js +503 -0
  152. package/lib/SidebarBadge-C0iWtji9.js.map +1 -0
  153. package/lib/{SlotletProvider-DJMaOUDs.js → SlotletProvider-DmqWiVFt.js} +43 -42
  154. package/lib/{SlotletProvider-DJMaOUDs.js.map → SlotletProvider-DmqWiVFt.js.map} +1 -1
  155. package/lib/ZudokuContext-MRoel6bS.js +1173 -0
  156. package/lib/ZudokuContext-MRoel6bS.js.map +1 -0
  157. package/lib/assets/{index-B9EWVYfo.js → index-B_Jk_Yzp.js} +968 -938
  158. package/lib/assets/index-B_Jk_Yzp.js.map +1 -0
  159. package/lib/assets/{worker-TYRbYl6N.js → worker-Bf8vjASY.js} +6768 -4410
  160. package/lib/assets/worker-Bf8vjASY.js.map +1 -0
  161. package/lib/{index-CTNlSS40.js → index-Ch8VO_uX.js} +598 -545
  162. package/lib/index-Ch8VO_uX.js.map +1 -0
  163. package/lib/{index-CLd8ycZz.js → index-CkwDvuPt.js} +947 -917
  164. package/lib/index-CkwDvuPt.js.map +1 -0
  165. package/lib/index-D06ATMgg.js +2094 -0
  166. package/lib/index-D06ATMgg.js.map +1 -0
  167. package/lib/{index-BG0g4WW0.js → index-DFN92YJN.js} +748 -737
  168. package/lib/index-DFN92YJN.js.map +1 -0
  169. package/lib/index-DJqnphbT.js +35 -0
  170. package/lib/index-DJqnphbT.js.map +1 -0
  171. package/lib/{index-BOyE1gXD.js → index-Dso_b0di.js} +1970 -1690
  172. package/lib/index-Dso_b0di.js.map +1 -0
  173. package/lib/{index-WPT1ueCM.js → index-bYAqbdYl.js} +3 -3
  174. package/lib/{index-WPT1ueCM.js.map → index-bYAqbdYl.js.map} +1 -1
  175. package/lib/router-BsfSoK2j.js +3024 -0
  176. package/lib/router-BsfSoK2j.js.map +1 -0
  177. package/lib/state-CsuHT8ZO.js +183 -0
  178. package/lib/state-CsuHT8ZO.js.map +1 -0
  179. package/lib/urql-core-KJnLL26g.js +1455 -0
  180. package/lib/urql-core-KJnLL26g.js.map +1 -0
  181. package/lib/useExposedProps-BWwxa-Hg.js +9 -0
  182. package/lib/useExposedProps-BWwxa-Hg.js.map +1 -0
  183. package/lib/{utils-ByIc_KIM.js → utils-C8S3st2k.js} +4 -4
  184. package/lib/utils-C8S3st2k.js.map +1 -0
  185. package/lib/zudoku.auth-auth0.js +1 -1
  186. package/lib/zudoku.auth-clerk.js +2 -2
  187. package/lib/zudoku.auth-openid.js +363 -350
  188. package/lib/zudoku.auth-openid.js.map +1 -1
  189. package/lib/zudoku.components.js +1768 -1687
  190. package/lib/zudoku.components.js.map +1 -1
  191. package/lib/zudoku.openapi-worker.js +4671 -4383
  192. package/lib/zudoku.openapi-worker.js.map +1 -1
  193. package/lib/zudoku.plugin-api-keys.js +7 -7
  194. package/lib/zudoku.plugin-custom-pages.js +21 -0
  195. package/lib/zudoku.plugin-custom-pages.js.map +1 -0
  196. package/lib/zudoku.plugin-markdown.js +1 -1
  197. package/lib/zudoku.plugin-openapi.js +8 -8
  198. package/lib/zudoku.plugin-redirect.js +1 -1
  199. package/package.json +71 -69
  200. package/src/app/main.css +0 -1
  201. package/src/app/main.tsx +5 -7
  202. package/src/lib/authentication/hook.ts +1 -1
  203. package/src/lib/authentication/state.ts +17 -0
  204. package/src/lib/components/Banner.tsx +12 -2
  205. package/src/lib/components/ErrorPage.tsx +0 -2
  206. package/src/lib/components/Header.tsx +4 -2
  207. package/src/lib/components/InlineCode.tsx +10 -0
  208. package/src/lib/components/Layout.tsx +2 -1
  209. package/src/lib/components/MobileTopNavigation.tsx +10 -2
  210. package/src/lib/components/Search.tsx +1 -1
  211. package/src/lib/components/SlotletProvider.tsx +27 -4
  212. package/src/lib/components/TopNavigation.tsx +14 -1
  213. package/src/lib/components/index.ts +3 -1
  214. package/src/lib/components/navigation/Sidebar.tsx +1 -1
  215. package/src/lib/components/navigation/SidebarCategory.tsx +34 -26
  216. package/src/lib/components/navigation/SidebarItem.tsx +16 -17
  217. package/src/lib/core/DevPortalContext.ts +7 -2
  218. package/src/lib/oas/graphql/index.ts +35 -23
  219. package/src/lib/oas/parser/index.ts +1 -0
  220. package/src/lib/plugins/custom-pages/CustomPage.tsx +18 -0
  221. package/src/lib/plugins/custom-pages/index.tsx +24 -0
  222. package/src/lib/plugins/markdown/MdxPage.tsx +11 -9
  223. package/src/lib/plugins/openapi/Endpoint.tsx +86 -22
  224. package/src/lib/plugins/openapi/OperationList.tsx +4 -2
  225. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +3 -0
  226. package/src/lib/plugins/openapi/Route.tsx +1 -2
  227. package/src/lib/plugins/openapi/Sidecar.tsx +18 -3
  228. package/src/lib/plugins/openapi/SimpleSelect.tsx +10 -2
  229. package/src/lib/plugins/openapi/client/createMemoryClient.ts +1 -6
  230. package/src/lib/plugins/openapi/client/createWorkerClient.ts +1 -6
  231. package/src/lib/plugins/openapi/graphql/gql.ts +11 -3
  232. package/src/lib/plugins/openapi/graphql/graphql.ts +113 -1
  233. package/src/lib/plugins/openapi/index.tsx +1 -5
  234. package/src/lib/plugins/openapi/playground/Playground.tsx +40 -5
  235. package/src/lib/plugins/openapi/playground/ResponseTab.tsx +0 -1
  236. package/src/lib/plugins/openapi/playground/createUrl.ts +6 -1
  237. package/src/lib/ui/Callout.tsx +7 -6
  238. package/src/lib/ui/Drawer.tsx +38 -36
  239. package/src/lib/util/useExposedProps.tsx +10 -0
  240. package/dist/lib/plugins/custom-page/index.d.ts +0 -8
  241. package/dist/lib/plugins/custom-page/index.js +0 -12
  242. package/dist/lib/plugins/custom-page/index.js.map +0 -1
  243. package/dist/lib/plugins/openapi/playground/Editor.d.ts +0 -1
  244. package/dist/lib/plugins/openapi/playground/Editor.js +0 -5
  245. package/dist/lib/plugins/openapi/playground/Editor.js.map +0 -1
  246. package/dist/lib/plugins/openapi/util/urql.d.ts +0 -7
  247. package/dist/lib/plugins/openapi/util/urql.js +0 -8
  248. package/dist/lib/plugins/openapi/util/urql.js.map +0 -1
  249. package/dist/lib/util/slugify.d.ts +0 -2
  250. package/dist/lib/util/slugify.js +0 -3
  251. package/dist/lib/util/slugify.js.map +0 -1
  252. package/dist/vite/plugin-icons.d.ts +0 -3
  253. package/dist/vite/plugin-icons.js +0 -47
  254. package/dist/vite/plugin-icons.js.map +0 -1
  255. package/lib/ErrorPage-knunPbKI.js +0 -18
  256. package/lib/ErrorPage-knunPbKI.js.map +0 -1
  257. package/lib/Input-BR5ia3Yq.js +0 -2198
  258. package/lib/Input-BR5ia3Yq.js.map +0 -1
  259. package/lib/Markdown-B4aR03g6.js.map +0 -1
  260. package/lib/MdxPage-BZyQsH8Z.js.map +0 -1
  261. package/lib/OperationList-CCPePX04.js.map +0 -1
  262. package/lib/Route-BAHaMyCu.js.map +0 -1
  263. package/lib/SidebarBadge-BaUICQiz.js +0 -498
  264. package/lib/SidebarBadge-BaUICQiz.js.map +0 -1
  265. package/lib/ZudokuContext-cr-pTRY1.js +0 -1084
  266. package/lib/ZudokuContext-cr-pTRY1.js.map +0 -1
  267. package/lib/_commonjsHelpers-BkfeUUK-.js +0 -29
  268. package/lib/_commonjsHelpers-BkfeUUK-.js.map +0 -1
  269. package/lib/assets/index-B9EWVYfo.js.map +0 -1
  270. package/lib/assets/worker-TYRbYl6N.js.map +0 -1
  271. package/lib/index-BG0g4WW0.js.map +0 -1
  272. package/lib/index-BOyE1gXD.js.map +0 -1
  273. package/lib/index-CLd8ycZz.js.map +0 -1
  274. package/lib/index-CTNlSS40.js.map +0 -1
  275. package/lib/router-D2p7Olpn.js +0 -2971
  276. package/lib/router-D2p7Olpn.js.map +0 -1
  277. package/lib/slugify-DbLhpSPt.js +0 -28
  278. package/lib/slugify-DbLhpSPt.js.map +0 -1
  279. package/lib/state-BUM4jc0J.js +0 -288
  280. package/lib/state-BUM4jc0J.js.map +0 -1
  281. package/lib/urql-YhcsXYy8.js +0 -1591
  282. package/lib/urql-YhcsXYy8.js.map +0 -1
  283. package/lib/utils-ByIc_KIM.js.map +0 -1
  284. package/lib/zudoku.plugin-custom-page.js +0 -13
  285. package/lib/zudoku.plugin-custom-page.js.map +0 -1
  286. package/src/lib/plugins/custom-page/index.tsx +0 -22
  287. package/src/lib/plugins/openapi/playground/Editor.tsx +0 -4
  288. package/src/lib/plugins/openapi/util/urql.ts +0 -8
  289. package/src/lib/util/slugify.ts +0 -3
@@ -24,7 +24,7 @@ export const Sidebar = () => {
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>
@@ -1,7 +1,7 @@
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";
@@ -26,6 +26,7 @@ export const SidebarCategory = ({
26
26
  !isCollapsible || !isCollapsed || isCategoryOpen,
27
27
  );
28
28
  const [open, setOpen] = useState(isDefaultOpen);
29
+ const isActive = useMatch(joinPath(topNavItem?.id, category.link?.id));
29
30
 
30
31
  useEffect(() => {
31
32
  // this is triggered when an item from the sidebar is clicked
@@ -56,46 +57,54 @@ export const SidebarCategory = ({
56
57
 
57
58
  return (
58
59
  <Collapsible.Root
59
- className={cn("flex flex-col", level === 0 && "-mx-[--padding-nav-item]")}
60
+ className="flex flex-col"
60
61
  defaultOpen={isDefaultOpen}
61
62
  open={open}
62
63
  onOpenChange={() => setOpen(true)}
63
64
  >
64
65
  <Collapsible.Trigger className="group" asChild disabled={!isCollapsible}>
65
66
  <div
66
- className={cn(
67
- "text-start",
68
- navigationListItem({ isActive: false, isTopLevel: level === 0 }),
69
- isCollapsible
70
- ? "cursor-pointer"
71
- : "cursor-default hover:bg-transparent",
72
- )}
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
+ })}
73
78
  >
74
79
  {category.icon && (
75
80
  <category.icon
76
81
  size={16}
77
- className="align-[-0.125em] -translate-x-1"
82
+ className={cn(
83
+ "align-[-0.125em] -translate-x-1",
84
+ isActive && "text-primary",
85
+ )}
78
86
  />
79
87
  )}
80
88
  {category.link?.type === "doc" ? (
81
89
  <NavLink
82
90
  to={joinPath(topNavItem?.id, category.link.id)}
83
91
  className="flex-1"
84
- 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
+ }}
85
98
  >
86
- {({ isActive }) => (
87
- <div
88
- className={cn(
89
- "flex items-center gap-2 justify-between w-full",
90
- isActive
91
- ? "text-primary font-medium"
92
- : "text-foreground/80",
93
- )}
94
- >
95
- <div className="truncate">{category.label}</div>
96
- {ToggleButton}
97
- </div>
98
- )}
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>
99
108
  </NavLink>
100
109
  ) : (
101
110
  <div className="flex items-center justify-between w-full">
@@ -109,10 +118,9 @@ export const SidebarCategory = ({
109
118
  className={cn(
110
119
  // CollapsibleContent class is used to animate and it should only be applied when the user has triggered the toggle
111
120
  hasInteracted && "CollapsibleContent",
112
- "ms-[calc(var(--padding-nav-item)*1.125)]",
113
121
  )}
114
122
  >
115
- <ul className="mt-1 border-l ps-2">
123
+ <ul className="mt-1 border-l ms-0.5">
116
124
  {category.items.map((item) => (
117
125
  <SidebarItem
118
126
  key={
@@ -3,7 +3,6 @@ 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";
@@ -16,7 +15,8 @@ export const navigationListItem = cva(
16
15
  {
17
16
  variants: {
18
17
  isTopLevel: {
19
- true: "font-semibold",
18
+ true: "font-medium -mx-[--padding-nav-item]",
19
+ false: "-mr-[--padding-nav-item] ml-[--padding-nav-item]",
20
20
  },
21
21
  isActive: {
22
22
  true: "text-primary font-medium",
@@ -27,6 +27,9 @@ export const navigationListItem = cva(
27
27
  false: "",
28
28
  },
29
29
  },
30
+ defaultVariants: {
31
+ isActive: false,
32
+ },
30
33
  },
31
34
  );
32
35
 
@@ -58,7 +61,7 @@ export const SidebarItem = ({
58
61
  {item.icon && <item.icon size={16} className="align-[-0.125em]" />}
59
62
  {item.badge ? (
60
63
  <>
61
- <span className="truncate" title={item.label}>
64
+ <span className="truncate flex-1" title={item.label}>
62
65
  {item.label}
63
66
  </span>
64
67
  <SidebarBadge {...item.badge} />
@@ -73,13 +76,11 @@ export const SidebarItem = ({
73
76
  <AnchorLink
74
77
  to={{ hash: item.href, search: searchParams.toString() }}
75
78
  {...{ [DATA_ANCHOR_ATTR]: item.href.slice(1) }}
76
- className={cn(
77
- "flex gap-2.5 justify-between",
78
- level === 0 && "-mx-[--padding-nav-item]",
79
- navigationListItem({
80
- isActive: item.href.slice(1) === activeAnchor,
81
- }),
82
- )}
79
+ className={navigationListItem({
80
+ isActive: item.href.slice(1) === activeAnchor,
81
+ isTopLevel: level === 0,
82
+ className: item.badge?.placement !== "start" && "justify-between",
83
+ })}
83
84
  >
84
85
  {item.badge ? (
85
86
  <>
@@ -94,7 +95,9 @@ export const SidebarItem = ({
94
95
  </AnchorLink>
95
96
  ) : !item.href.startsWith("http") ? (
96
97
  <NavLink
97
- className={cn("flex gap-2.5 justify-between", navigationListItem())}
98
+ className={navigationListItem({
99
+ className: item.badge?.placement !== "start" && "justify-between",
100
+ })}
98
101
  to={item.href}
99
102
  >
100
103
  {item.badge ? (
@@ -110,10 +113,7 @@ export const SidebarItem = ({
110
113
  </NavLink>
111
114
  ) : (
112
115
  <a
113
- className={cn(
114
- navigationListItem({ isTopLevel: level === 0 }),
115
- "block",
116
- )}
116
+ className={navigationListItem({ isTopLevel: level === 0 })}
117
117
  href={item.href}
118
118
  target="_blank"
119
119
  rel="noopener noreferrer"
@@ -121,8 +121,7 @@ export const SidebarItem = ({
121
121
  <span className="whitespace-normal">{item.label}</span>
122
122
  {/* This prevents that the icon would be positioned in its own line if the text fills a line entirely */}
123
123
  <span className="whitespace-nowrap">
124
- &nbsp;
125
- <ExternalLinkIcon className="inline ml-1" size={12} />
124
+ <ExternalLinkIcon className="inline -translate-y-0.5" size={12} />
126
125
  </span>
127
126
  </a>
128
127
  );
@@ -51,7 +51,7 @@ type Page = Partial<{
51
51
  };
52
52
  banner?: {
53
53
  message: ReactNode;
54
- color?: "note" | "tip" | "info" | "caution" | "danger";
54
+ color?: "note" | "tip" | "info" | "caution" | "danger" | (string & {});
55
55
  dismissible?: boolean;
56
56
  };
57
57
  }>;
@@ -60,7 +60,12 @@ export type ZudokuContextOptions = {
60
60
  metadata?: Metadata;
61
61
  page?: Page;
62
62
  authentication?: AuthenticationProvider;
63
- topNavigation?: Array<{ id: string; label: string; default?: string }>;
63
+ topNavigation?: Array<{
64
+ id: string;
65
+ label: string;
66
+ default?: string;
67
+ display?: "auth" | "anon" | "always";
68
+ }>;
64
69
  sidebars?: SidebarConfig;
65
70
  plugins?: DevPortalPlugin[];
66
71
  slotlets?: Slotlets;
@@ -1,10 +1,13 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import SchemaBuilder from "@pothos/core";
3
+ import {
4
+ slugifyWithCounter,
5
+ type CountableSlugify,
6
+ } from "@sindresorhus/slugify";
3
7
  import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
4
8
  import { createYoga, type YogaServerOptions } from "graphql-yoga";
5
9
  import { LRUCache } from "lru-cache";
6
10
  import hashit from "object-hash";
7
- import slugify from "../../util/slugify.js";
8
11
  import {
9
12
  HttpMethods,
10
13
  validate,
@@ -15,6 +18,7 @@ import {
15
18
  type ParameterObject,
16
19
  type PathsObject,
17
20
  type SchemaObject,
21
+ type ServerObject,
18
22
  type TagObject,
19
23
  } from "../parser/index.js";
20
24
 
@@ -35,7 +39,12 @@ type OperationLike = {
35
39
  path: string;
36
40
  method: string;
37
41
  };
38
- export const slugifyOperation = (operation: OperationLike, tag?: string) => {
42
+
43
+ export const createOperationSlug = (
44
+ slugify: CountableSlugify,
45
+ operation: OperationLike,
46
+ tag?: string,
47
+ ) => {
39
48
  const summary =
40
49
  (operation.summary ?? "") +
41
50
  (operation.operationId
@@ -46,13 +55,13 @@ export const slugifyOperation = (operation: OperationLike, tag?: string) => {
46
55
  return slugify(
47
56
  (tag ? tag + "-" : "") +
48
57
  (summary || `${operation.method}-${operation.path}`),
49
- { lower: true, trim: true },
50
58
  );
51
59
  };
52
60
 
53
- const cache = new LRUCache<string, Promise<OpenAPIDocument>>({
61
+ const cache = new LRUCache<string, OpenAPIDocument>({
54
62
  ttl: 60 * 10 * 1000,
55
63
  ttlAutopurge: true,
64
+ fetchMethod: (_key, _oldValue, { context }) => validate(context as string),
56
65
  });
57
66
 
58
67
  const builder = new SchemaBuilder<{
@@ -88,6 +97,8 @@ const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
88
97
  };
89
98
 
90
99
  const getAllOperations = (paths?: PathsObject, tag?: string) => {
100
+ const slugify = slugifyWithCounter();
101
+
91
102
  return Object.entries(paths ?? {}).flatMap(([path, value]) =>
92
103
  HttpMethods.flatMap((method) => {
93
104
  if (!value?.[method]) return [];
@@ -109,21 +120,20 @@ const getAllOperations = (paths?: PathsObject, tag?: string) => {
109
120
  ...operationParameters,
110
121
  ];
111
122
 
123
+ const slugData = {
124
+ summary: operation.summary,
125
+ operationId: operation.operationId,
126
+ path,
127
+ method,
128
+ };
129
+
112
130
  return {
113
131
  ...operation,
114
132
  method,
115
133
  path,
116
134
  parameters,
117
135
  tags: operation.tags ?? [],
118
- slug: slugifyOperation(
119
- {
120
- summary: operation.summary,
121
- operationId: operation.operationId,
122
- path,
123
- method,
124
- },
125
- tag,
126
- ),
136
+ slug: createOperationSlug(slugify, slugData, tag),
127
137
  };
128
138
  }),
129
139
  );
@@ -150,6 +160,13 @@ const SchemaTag = builder.objectRef<TagObject>("SchemaTag").implement({
150
160
  }),
151
161
  });
152
162
 
163
+ const ServerItem = builder.objectRef<ServerObject>("Server").implement({
164
+ fields: (t) => ({
165
+ url: t.exposeString("url"),
166
+ description: t.exposeString("description", { nullable: true }),
167
+ }),
168
+ });
169
+
153
170
  const PathItem = builder
154
171
  .objectRef<{
155
172
  path: string;
@@ -363,6 +380,10 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
363
380
  fields: (t) => ({
364
381
  openapi: t.string({ resolve: (root) => root.openapi }),
365
382
  url: t.string({ resolve: (root) => root.servers?.at(0)?.url ?? "/" }),
383
+ servers: t.field({
384
+ type: [ServerItem],
385
+ resolve: (root) => root.servers ?? [],
386
+ }),
366
387
  title: t.string({ resolve: (root) => root.info.title }),
367
388
  version: t.string({ resolve: (root) => root.info.version }),
368
389
  description: t.string({
@@ -409,16 +430,7 @@ const Schema = builder.objectRef<OpenAPIDocument>("Schema").implement({
409
430
 
410
431
  const loadOpenAPISchema = async (input: NonNullable<unknown>) => {
411
432
  const hash = hashit(input);
412
-
413
- if (cache.has(hash)) {
414
- return cache.get(hash)!;
415
- }
416
-
417
- const schema = validate(input);
418
-
419
- cache.set(hash, schema);
420
-
421
- return schema;
433
+ return await cache.forceFetch(hash, { context: input });
422
434
  };
423
435
 
424
436
  const SchemaSource = builder.enumType("SchemaType", {
@@ -20,6 +20,7 @@ export type TagObject = DeepOmitReference<OpenAPIV3_1.TagObject>;
20
20
  export type ExampleObject = DeepOmitReference<OpenAPIV3_1.ExampleObject>;
21
21
  export type EncodingObject = DeepOmitReference<OpenAPIV3_1.EncodingObject>;
22
22
  export type SchemaObject = DeepOmitReference<OpenAPIV3_1.SchemaObject>;
23
+ export type ServerObject = DeepOmitReference<OpenAPIV3_1.ServerObject>;
23
24
 
24
25
  export const HttpMethods = Object.values(OpenAPIV3.HttpMethods);
25
26
 
@@ -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
+ };
@@ -0,0 +1,24 @@
1
+ import { type ComponentType, type ReactNode } from "react";
2
+ import type { RouteObject } from "react-router-dom";
3
+ import { type ExposedComponentProps } from "../../components/SlotletProvider.js";
4
+ import type { DevPortalPlugin, NavigationPlugin } from "../../core/plugins.js";
5
+ import { CustomPage } from "./CustomPage.js";
6
+
7
+ export type CustomPageConfig = {
8
+ path: string;
9
+ prose?: boolean;
10
+ element?: ReactNode;
11
+ render?: ComponentType<ExposedComponentProps>;
12
+ };
13
+
14
+ export const customPagesPlugin = (
15
+ config: CustomPageConfig[],
16
+ ): DevPortalPlugin & NavigationPlugin => {
17
+ return {
18
+ getRoutes: (): RouteObject[] =>
19
+ config.map(({ path, ...props }) => ({
20
+ path,
21
+ element: <CustomPage {...props} />,
22
+ })),
23
+ };
24
+ };
@@ -1,4 +1,5 @@
1
1
  import { useMDXComponents } from "@mdx-js/react";
2
+ import slugify from "@sindresorhus/slugify";
2
3
  import { Helmet } from "@zudoku/react-helmet-async";
3
4
  import { type PropsWithChildren } from "react";
4
5
  import { Link } from "react-router-dom";
@@ -11,7 +12,6 @@ import {
11
12
  } from "../../components/navigation/utils.js";
12
13
  import type { MdxComponentsType } from "../../util/MdxComponents.js";
13
14
  import { cn } from "../../util/cn.js";
14
- import slugify from "../../util/slugify.js";
15
15
  import { Toc } from "./Toc.js";
16
16
  import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
17
17
 
@@ -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, { lower: true })}>
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
  />
@@ -1,31 +1,95 @@
1
1
  import { CheckIcon, CopyIcon } from "lucide-react";
2
- import { useState } from "react";
2
+ import { useState, useTransition } from "react";
3
+ import { useQuery } from "urql";
4
+ import { useSelectedServerStore } from "../../authentication/state.js";
3
5
  import { InlineCode } from "../../components/InlineCode.js";
6
+ import { Button } from "../../ui/Button.js";
7
+ import { useOasConfig } from "./context.js";
8
+ import { graphql } from "./graphql/index.js";
9
+ import { SimpleSelect } from "./SimpleSelect.js";
4
10
 
5
- export const Endpoint = ({ url }: { url: string }) => {
11
+ const ServersQuery = graphql(/* GraphQL */ `
12
+ query ServersQuery($input: JSON!, $type: SchemaType!) {
13
+ schema(input: $input, type: $type) {
14
+ url
15
+ servers {
16
+ url
17
+ }
18
+ }
19
+ }
20
+ `);
21
+
22
+ const CopyButton = ({ url }: { url: string }) => {
6
23
  const [isCopied, setIsCopied] = useState(false);
7
24
 
8
25
  return (
9
- <div className="my-4 flex items-center justify-end gap-2 text-sm">
10
- <span className="font-medium">Endpoint:</span>
11
- <InlineCode className="p-1.5 flex gap-2.5 items-center text-xs">
12
- {url}
13
- <button
14
- onClick={() => {
15
- void navigator.clipboard.writeText(url).then(() => {
16
- setIsCopied(true);
17
- setTimeout(() => setIsCopied(false), 2000);
18
- });
19
- }}
20
- type="button"
21
- >
22
- {isCopied ? (
23
- <CheckIcon className="text-green-600" size={14} />
24
- ) : (
25
- <CopyIcon size={14} strokeWidth={1.3} />
26
- )}
27
- </button>
28
- </InlineCode>
26
+ <Button
27
+ onClick={() => {
28
+ void navigator.clipboard.writeText(url).then(() => {
29
+ setIsCopied(true);
30
+ setTimeout(() => setIsCopied(false), 2000);
31
+ });
32
+ }}
33
+ variant="ghost"
34
+ size="icon"
35
+ >
36
+ {isCopied ? (
37
+ <CheckIcon className="text-green-600" size={14} />
38
+ ) : (
39
+ <CopyIcon size={14} strokeWidth={1.3} />
40
+ )}
41
+ </Button>
42
+ );
43
+ };
44
+
45
+ const context = { suspense: true } as const;
46
+
47
+ export const Endpoint = () => {
48
+ const [result] = useQuery({
49
+ query: ServersQuery,
50
+ variables: useOasConfig(),
51
+ context,
52
+ });
53
+ const [, startTransition] = useTransition();
54
+ const { selectedServer, setSelectedServer } = useSelectedServerStore();
55
+
56
+ if (!result.data) return null;
57
+
58
+ const { servers } = result.data.schema;
59
+
60
+ if (servers.length === 1) {
61
+ return (
62
+ <div className="flex items-center gap-2">
63
+ <span className="font-medium text-sm">Endpoint:</span>
64
+ <InlineCode className="text-xs px-2 py-1.5" selectOnClick>
65
+ {servers[0].url}
66
+ </InlineCode>
67
+ <CopyButton url={servers[0].url} />
68
+ </div>
69
+ );
70
+ }
71
+
72
+ return (
73
+ <div className="flex flex-wrap items-center gap-2">
74
+ <span className="font-medium text-sm">
75
+ {servers.length > 1 ? "Endpoints" : "Endpoint"}:
76
+ </span>
77
+
78
+ <SimpleSelect
79
+ className="font-mono text-xs bg-border/50 dark:bg-border/70 py-1.5 max-w-[450px] truncate"
80
+ onChange={(e) =>
81
+ startTransition(() => {
82
+ setSelectedServer(e.target.value);
83
+ })
84
+ }
85
+ value={selectedServer ?? result.data.schema.url}
86
+ showChevrons={servers.length > 1}
87
+ options={servers.map((server) => ({
88
+ value: server.url,
89
+ label: server.url,
90
+ }))}
91
+ />
92
+ <CopyButton url={selectedServer ?? result.data.schema.url} />
29
93
  </div>
30
94
  );
31
95
  };
@@ -1,4 +1,5 @@
1
1
  import { ResultOf } from "@graphql-typed-document-node/core";
2
+ import { useQuery } from "urql";
2
3
  import { CategoryHeading } from "../../components/CategoryHeading.js";
3
4
  import { DeveloperHint } from "../../components/DeveloperHint.js";
4
5
  import { ErrorPage } from "../../components/ErrorPage.js";
@@ -12,7 +13,6 @@ import { OperationListItem } from "./OperationListItem.js";
12
13
  import StaggeredRender from "./StaggeredRender.js";
13
14
  import { useOasConfig } from "./context.js";
14
15
  import { graphql } from "./graphql/index.js";
15
- import { useQuery } from "./util/urql.js";
16
16
 
17
17
  export const OperationsFragment = graphql(/* GraphQL */ `
18
18
  fragment OperationsFragment on OperationItem {
@@ -133,7 +133,9 @@ export const OperationList = () => {
133
133
  <Markdown content={result.data.schema.description ?? ""} />
134
134
  </div>
135
135
  <hr />
136
- <Endpoint url={result.data.schema.url} />
136
+ <div className="my-4 flex justify-end">
137
+ <Endpoint />
138
+ </div>
137
139
 
138
140
  {result.data.schema.tags
139
141
  .filter((tag) => tag.operations.length > 0)
@@ -3,9 +3,11 @@ import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
3
3
 
4
4
  export const PlaygroundDialogWrapper = ({
5
5
  server,
6
+ servers,
6
7
  operation,
7
8
  }: {
8
9
  server: string;
10
+ servers?: string[];
9
11
  operation: OperationListItemResult;
10
12
  }) => {
11
13
  const headers = operation.parameters
@@ -29,6 +31,7 @@ export const PlaygroundDialogWrapper = ({
29
31
  return (
30
32
  <PlaygroundDialog
31
33
  server={server}
34
+ servers={servers}
32
35
  method={operation.method}
33
36
  url={operation.path}
34
37
  headers={headers}
@@ -1,9 +1,8 @@
1
1
  import { Outlet } from "react-router-dom";
2
+ import { Provider, Client as UrqlClient } from "urql";
2
3
  import { OasConfigProvider } from "./context.js";
3
4
  import { OasPluginConfig } from "./interfaces.js";
4
5
 
5
- import { Provider, Client as UrqlClient } from "./util/urql.js";
6
-
7
6
  export function OpenApiRoute({
8
7
  config,
9
8
  client,