zudoku 0.26.0 → 0.27.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 (300) hide show
  1. package/dist/app/main.d.ts +1 -1
  2. package/dist/app/main.js +19 -7
  3. package/dist/app/main.js.map +1 -1
  4. package/dist/config/config.d.ts +1 -0
  5. package/dist/config/validators/InputSidebarSchema.d.ts +2 -2
  6. package/dist/config/validators/common.d.ts +78 -0
  7. package/dist/config/validators/common.js +6 -0
  8. package/dist/config/validators/common.js.map +1 -1
  9. package/dist/config/validators/validate.d.ts +34 -0
  10. package/dist/lib/authentication/providers/auth0.js +1 -1
  11. package/dist/lib/authentication/providers/auth0.js.map +1 -1
  12. package/dist/lib/authentication/providers/openid.d.ts +1 -1
  13. package/dist/lib/authentication/providers/openid.js +10 -6
  14. package/dist/lib/authentication/providers/openid.js.map +1 -1
  15. package/dist/lib/components/AnchorLink.js +5 -2
  16. package/dist/lib/components/AnchorLink.js.map +1 -1
  17. package/dist/lib/components/Autocomplete.d.ts +12 -0
  18. package/dist/lib/components/Autocomplete.js +47 -0
  19. package/dist/lib/components/Autocomplete.js.map +1 -0
  20. package/dist/lib/components/Header.js +4 -4
  21. package/dist/lib/components/Header.js.map +1 -1
  22. package/dist/lib/components/Heading.d.ts +1 -1
  23. package/dist/lib/components/Markdown.d.ts +2 -2
  24. package/dist/lib/components/Markdown.js +3 -1
  25. package/dist/lib/components/Markdown.js.map +1 -1
  26. package/dist/lib/components/StatusPage.d.ts +7 -0
  27. package/dist/lib/components/StatusPage.js +71 -0
  28. package/dist/lib/components/StatusPage.js.map +1 -0
  29. package/dist/lib/components/SyntaxHighlight.d.ts +2 -1
  30. package/dist/lib/components/SyntaxHighlight.js +2 -2
  31. package/dist/lib/components/SyntaxHighlight.js.map +1 -1
  32. package/dist/lib/components/ThemeSwitch.js +4 -4
  33. package/dist/lib/components/ThemeSwitch.js.map +1 -1
  34. package/dist/lib/components/cache.d.ts +6 -0
  35. package/dist/lib/components/cache.js +13 -0
  36. package/dist/lib/components/cache.js.map +1 -0
  37. package/dist/lib/components/context/ViewportAnchorContext.js +16 -4
  38. package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
  39. package/dist/lib/components/context/ZudokuContext.js +2 -1
  40. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  41. package/dist/lib/components/index.d.ts +9 -2
  42. package/dist/lib/components/index.js +5 -2
  43. package/dist/lib/components/index.js.map +1 -1
  44. package/dist/lib/core/RouteGuard.d.ts +1 -0
  45. package/dist/lib/core/RouteGuard.js +28 -0
  46. package/dist/lib/core/RouteGuard.js.map +1 -0
  47. package/dist/lib/core/ZudokuContext.d.ts +4 -2
  48. package/dist/lib/core/ZudokuContext.js +9 -7
  49. package/dist/lib/core/ZudokuContext.js.map +1 -1
  50. package/dist/lib/oas/graphql/circular.d.ts +3 -0
  51. package/dist/lib/oas/graphql/circular.js +27 -0
  52. package/dist/lib/oas/graphql/circular.js.map +1 -0
  53. package/dist/lib/oas/graphql/index.js +5 -6
  54. package/dist/lib/oas/graphql/index.js.map +1 -1
  55. package/dist/lib/oas/parser/dereference/index.d.ts +0 -1
  56. package/dist/lib/oas/parser/dereference/index.js +1 -1
  57. package/dist/lib/oas/parser/dereference/index.js.map +1 -1
  58. package/dist/lib/plugins/markdown/MdxPage.js +8 -2
  59. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  60. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  61. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  62. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  63. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  64. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.d.ts +3 -1
  65. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +3 -2
  66. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  67. package/dist/lib/plugins/openapi/Sidecar.js +3 -3
  68. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  69. package/dist/lib/plugins/openapi/index.js +4 -11
  70. package/dist/lib/plugins/openapi/index.js.map +1 -1
  71. package/dist/lib/plugins/openapi/interfaces.d.ts +7 -2
  72. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.d.ts +6 -0
  73. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js +12 -0
  74. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js.map +1 -0
  75. package/dist/lib/plugins/openapi/playground/Headers.js +67 -4
  76. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  77. package/dist/lib/plugins/openapi/playground/ParamsGrid.d.ts +5 -0
  78. package/dist/lib/plugins/openapi/playground/ParamsGrid.js +4 -0
  79. package/dist/lib/plugins/openapi/playground/ParamsGrid.js.map +1 -0
  80. package/dist/lib/plugins/openapi/playground/PathParams.js +4 -12
  81. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  82. package/dist/lib/plugins/openapi/playground/Playground.d.ts +18 -1
  83. package/dist/lib/plugins/openapi/playground/Playground.js +53 -40
  84. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  85. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +1 -1
  86. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
  87. package/dist/lib/plugins/openapi/playground/QueryParams.js +21 -30
  88. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  89. package/dist/lib/plugins/openapi/playground/SubmitButton.d.ts +7 -0
  90. package/dist/lib/plugins/openapi/playground/SubmitButton.js +22 -0
  91. package/dist/lib/plugins/openapi/playground/SubmitButton.js.map +1 -0
  92. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.d.ts +7 -0
  93. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.js +11 -0
  94. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.js.map +1 -0
  95. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.d.ts +8 -0
  96. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +95 -0
  97. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -0
  98. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.d.ts +7 -0
  99. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +16 -0
  100. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js.map +1 -0
  101. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.d.ts +10 -0
  102. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.js +32 -0
  103. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.js.map +1 -0
  104. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.test.d.ts +1 -0
  105. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.test.js +56 -0
  106. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.test.js.map +1 -0
  107. package/dist/lib/plugins/openapi/post-processors/removeExtensions.d.ts +2 -1
  108. package/dist/lib/plugins/openapi/post-processors/removeExtensions.js +5 -3
  109. package/dist/lib/plugins/openapi/post-processors/removeExtensions.js.map +1 -1
  110. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js +49 -0
  111. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js.map +1 -1
  112. package/dist/lib/plugins/openapi/post-processors/removeParameters.d.ts +10 -0
  113. package/dist/lib/plugins/openapi/post-processors/removeParameters.js +66 -0
  114. package/dist/lib/plugins/openapi/post-processors/removeParameters.js.map +1 -0
  115. package/dist/lib/plugins/openapi/post-processors/removeParameters.test.d.ts +1 -0
  116. package/dist/lib/plugins/openapi/post-processors/removeParameters.test.js +131 -0
  117. package/dist/lib/plugins/openapi/post-processors/removeParameters.test.js.map +1 -0
  118. package/dist/lib/plugins/openapi/schema/SchemaComponents.js +1 -1
  119. package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -1
  120. package/dist/lib/ui/Command.d.ts +9 -1
  121. package/dist/lib/ui/Command.js +5 -1
  122. package/dist/lib/ui/Command.js.map +1 -1
  123. package/dist/lib/ui/Select.js +2 -2
  124. package/dist/lib/ui/Select.js.map +1 -1
  125. package/dist/lib/util/MdxComponents.js +2 -2
  126. package/dist/lib/util/MdxComponents.js.map +1 -1
  127. package/dist/lib/util/joinUrl.d.ts +1 -0
  128. package/dist/lib/util/joinUrl.js +40 -0
  129. package/dist/lib/util/joinUrl.js.map +1 -0
  130. package/dist/lib/util/useScrollToAnchor.d.ts +1 -0
  131. package/dist/lib/util/useScrollToAnchor.js +26 -15
  132. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  133. package/dist/vite/build.js +10 -10
  134. package/dist/vite/build.js.map +1 -1
  135. package/dist/vite/config.js +4 -1
  136. package/dist/vite/config.js.map +1 -1
  137. package/dist/vite/dev-server.js +4 -1
  138. package/dist/vite/dev-server.js.map +1 -1
  139. package/dist/vite/plugin-api.d.ts +1 -1
  140. package/dist/vite/plugin-api.js +31 -7
  141. package/dist/vite/plugin-api.js.map +1 -1
  142. package/dist/vite/plugin-auth.js +4 -1
  143. package/dist/vite/plugin-auth.js.map +1 -1
  144. package/dist/vite/plugin-mdx.js +9 -4
  145. package/dist/vite/plugin-mdx.js.map +1 -1
  146. package/dist/vite/prerender.d.ts +2 -2
  147. package/dist/vite/prerender.js +5 -4
  148. package/dist/vite/prerender.js.map +1 -1
  149. package/dist/zuplo/enrich-with-zuplo.d.ts +5 -0
  150. package/dist/zuplo/enrich-with-zuplo.js +184 -0
  151. package/dist/zuplo/enrich-with-zuplo.js.map +1 -0
  152. package/dist/zuplo/env.d.ts +1 -0
  153. package/dist/zuplo/env.js +3 -0
  154. package/dist/zuplo/env.js.map +1 -1
  155. package/dist/zuplo/policy-types.d.ts +33 -0
  156. package/dist/zuplo/policy-types.js +8 -0
  157. package/dist/zuplo/policy-types.js.map +1 -0
  158. package/dist/zuplo/with-zuplo-processors.d.ts +3 -0
  159. package/dist/zuplo/with-zuplo-processors.js +26 -0
  160. package/dist/zuplo/with-zuplo-processors.js.map +1 -0
  161. package/dist/zuplo/with-zuplo.d.ts +1 -1
  162. package/dist/zuplo/with-zuplo.js +7 -26
  163. package/dist/zuplo/with-zuplo.js.map +1 -1
  164. package/lib/{AuthenticationPlugin-DNXBcsVN.js → AuthenticationPlugin-CO_YCd2x.js} +3 -3
  165. package/lib/{AuthenticationPlugin-DNXBcsVN.js.map → AuthenticationPlugin-CO_YCd2x.js.map} +1 -1
  166. package/lib/{Markdown-BrfrjEk_.js → Markdown-B8o9Qz4q.js} +1197 -1186
  167. package/lib/{Markdown-BrfrjEk_.js.map → Markdown-B8o9Qz4q.js.map} +1 -1
  168. package/lib/{MdxPage-LNZLj_A5.js → MdxPage-BxRt3Ly7.js} +63 -58
  169. package/lib/MdxPage-BxRt3Ly7.js.map +1 -0
  170. package/lib/OperationList-DH-zIgtq.js +5160 -0
  171. package/lib/OperationList-DH-zIgtq.js.map +1 -0
  172. package/lib/{Route-Pzk6qwIk.js → Route-DJ0ZlVq1.js} +3 -3
  173. package/lib/{Route-Pzk6qwIk.js.map → Route-DJ0ZlVq1.js.map} +1 -1
  174. package/lib/{Select-DkOpAG0c.js → Select-B7UXR0SB.js} +61 -61
  175. package/lib/Select-B7UXR0SB.js.map +1 -0
  176. package/lib/{SlotletProvider-DPbx9KdU.js → SlotletProvider-CtIp8rP3.js} +4 -4
  177. package/lib/{SlotletProvider-DPbx9KdU.js.map → SlotletProvider-CtIp8rP3.js.map} +1 -1
  178. package/lib/{Button-oroWHXAy.js → Spinner-BlzrEEk1.js} +15 -12
  179. package/lib/Spinner-BlzrEEk1.js.map +1 -0
  180. package/lib/{SyntaxHighlight-CJCSPG1F.js → SyntaxHighlight-C1w1QPdY.js} +300 -295
  181. package/lib/{SyntaxHighlight-CJCSPG1F.js.map → SyntaxHighlight-C1w1QPdY.js.map} +1 -1
  182. package/lib/{ZudokuContext-D3ayHjP-.js → ZudokuContext-8jts0fF3.js} +259 -248
  183. package/lib/ZudokuContext-8jts0fF3.js.map +1 -0
  184. package/lib/{chunk-SYFQ2XB5-KWlHsT7t.js → chunk-SYFQ2XB5-BPvC-soB.js} +6 -5
  185. package/lib/{chunk-SYFQ2XB5-KWlHsT7t.js.map → chunk-SYFQ2XB5-BPvC-soB.js.map} +1 -1
  186. package/lib/circular-Dgpd6AN-.js +15397 -0
  187. package/lib/circular-Dgpd6AN-.js.map +1 -0
  188. package/lib/{createServer-BcaswoFO.js → createServer-BV0tHzLK.js} +3450 -5577
  189. package/lib/createServer-BV0tHzLK.js.map +1 -0
  190. package/lib/{hook-DUyACbIK.js → hook-BG02esyv.js} +2 -2
  191. package/lib/{hook-DUyACbIK.js.map → hook-BG02esyv.js.map} +1 -1
  192. package/lib/index-B7mqiOei.js +509 -0
  193. package/lib/index-B7mqiOei.js.map +1 -0
  194. package/lib/index-DmqsUPcm.js +1915 -0
  195. package/lib/index-DmqsUPcm.js.map +1 -0
  196. package/lib/joinUrl-BTy9bvoK.js +20 -0
  197. package/lib/joinUrl-BTy9bvoK.js.map +1 -0
  198. package/lib/post-processors/removeExtensions.js +7 -7
  199. package/lib/post-processors/removeExtensions.js.map +1 -1
  200. package/lib/post-processors/removeParameters.js +48 -0
  201. package/lib/post-processors/removeParameters.js.map +1 -0
  202. package/lib/ui/ActionButton.js +10 -11
  203. package/lib/ui/ActionButton.js.map +1 -1
  204. package/lib/ui/Command.js +125 -13
  205. package/lib/ui/Command.js.map +1 -1
  206. package/lib/ui/Select.js +2 -2
  207. package/lib/ui/Select.js.map +1 -1
  208. package/lib/{useExposedProps-BBHR7aLM.js → useExposedProps-BLKFBylA.js} +2 -2
  209. package/lib/{useExposedProps-BBHR7aLM.js.map → useExposedProps-BLKFBylA.js.map} +1 -1
  210. package/lib/useScrollToAnchor-Bl6mz9_x.js +288 -0
  211. package/lib/useScrollToAnchor-Bl6mz9_x.js.map +1 -0
  212. package/lib/zudoku.auth-auth0.js +7 -9
  213. package/lib/zudoku.auth-auth0.js.map +1 -1
  214. package/lib/zudoku.auth-clerk.js +1 -1
  215. package/lib/zudoku.auth-openid.js +223 -219
  216. package/lib/zudoku.auth-openid.js.map +1 -1
  217. package/lib/zudoku.components.js +754 -992
  218. package/lib/zudoku.components.js.map +1 -1
  219. package/lib/zudoku.plugin-api-catalog.js +3 -3
  220. package/lib/zudoku.plugin-api-keys.js +5 -5
  221. package/lib/zudoku.plugin-custom-pages.js +2 -2
  222. package/lib/zudoku.plugin-markdown.js +1 -1
  223. package/lib/zudoku.plugin-openapi.js +4 -4
  224. package/lib/zudoku.plugin-redirect.js +1 -1
  225. package/package.json +2 -2
  226. package/src/app/main.css +50 -50
  227. package/src/app/main.tsx +26 -7
  228. package/src/lib/authentication/providers/auth0.tsx +1 -4
  229. package/src/lib/authentication/providers/openid.tsx +12 -5
  230. package/src/lib/components/AnchorLink.tsx +5 -2
  231. package/src/lib/components/Autocomplete.tsx +111 -0
  232. package/src/lib/components/Header.tsx +4 -4
  233. package/src/lib/components/Markdown.tsx +14 -15
  234. package/src/lib/components/StatusPage.tsx +91 -0
  235. package/src/lib/components/SyntaxHighlight.tsx +14 -0
  236. package/src/lib/components/ThemeSwitch.tsx +14 -15
  237. package/src/lib/components/cache.ts +15 -0
  238. package/src/lib/components/context/ViewportAnchorContext.tsx +20 -6
  239. package/src/lib/components/context/ZudokuContext.ts +3 -1
  240. package/src/lib/components/index.ts +9 -2
  241. package/src/lib/core/RouteGuard.tsx +35 -0
  242. package/src/lib/core/ZudokuContext.ts +9 -8
  243. package/src/lib/oas/graphql/circular.ts +29 -0
  244. package/src/lib/oas/graphql/index.ts +9 -9
  245. package/src/lib/oas/parser/dereference/index.ts +1 -2
  246. package/src/lib/plugins/markdown/MdxPage.tsx +9 -1
  247. package/src/lib/plugins/openapi/OperationListItem.tsx +0 -2
  248. package/src/lib/plugins/openapi/ParameterListItem.tsx +1 -0
  249. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +5 -0
  250. package/src/lib/plugins/openapi/Sidecar.tsx +4 -2
  251. package/src/lib/plugins/openapi/index.tsx +9 -15
  252. package/src/lib/plugins/openapi/interfaces.ts +10 -2
  253. package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +54 -0
  254. package/src/lib/plugins/openapi/playground/Headers.tsx +136 -39
  255. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +8 -0
  256. package/src/lib/plugins/openapi/playground/PathParams.tsx +34 -74
  257. package/src/lib/plugins/openapi/playground/Playground.tsx +219 -177
  258. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +1 -1
  259. package/src/lib/plugins/openapi/playground/QueryParams.tsx +90 -122
  260. package/src/lib/plugins/openapi/playground/SubmitButton.tsx +75 -0
  261. package/src/lib/plugins/openapi/playground/result-panel/RequestTab.tsx +73 -0
  262. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +210 -0
  263. package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +101 -0
  264. package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.test.ts +64 -0
  265. package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.ts +36 -0
  266. package/src/lib/plugins/openapi/post-processors/removeExtensions.test.ts +58 -0
  267. package/src/lib/plugins/openapi/post-processors/removeExtensions.ts +7 -4
  268. package/src/lib/plugins/openapi/post-processors/removeParameters.test.ts +148 -0
  269. package/src/lib/plugins/openapi/post-processors/removeParameters.ts +101 -0
  270. package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +1 -1
  271. package/src/lib/ui/Command.tsx +20 -0
  272. package/src/lib/ui/Select.tsx +1 -1
  273. package/src/lib/util/MdxComponents.tsx +2 -1
  274. package/src/lib/util/joinUrl.ts +57 -0
  275. package/src/lib/util/useScrollToAnchor.ts +32 -15
  276. package/dist/lib/plugins/openapi/playground/EnumSelector.d.ts +0 -8
  277. package/dist/lib/plugins/openapi/playground/EnumSelector.js +0 -21
  278. package/dist/lib/plugins/openapi/playground/EnumSelector.js.map +0 -1
  279. package/dist/lib/plugins/openapi/playground/ResponseTab.d.ts +0 -4
  280. package/dist/lib/plugins/openapi/playground/ResponseTab.js +0 -42
  281. package/dist/lib/plugins/openapi/playground/ResponseTab.js.map +0 -1
  282. package/lib/AnchorLink-_Vu02ceN.js +0 -34
  283. package/lib/AnchorLink-_Vu02ceN.js.map +0 -1
  284. package/lib/Button-oroWHXAy.js.map +0 -1
  285. package/lib/Command-D5DE0DD7.js +0 -611
  286. package/lib/Command-D5DE0DD7.js.map +0 -1
  287. package/lib/MdxPage-LNZLj_A5.js.map +0 -1
  288. package/lib/OperationList-PCwzTp1r.js +0 -5144
  289. package/lib/OperationList-PCwzTp1r.js.map +0 -1
  290. package/lib/Select-DkOpAG0c.js.map +0 -1
  291. package/lib/Spinner-C5gHXrVz.js +0 -7
  292. package/lib/Spinner-C5gHXrVz.js.map +0 -1
  293. package/lib/ZudokuContext-D3ayHjP-.js.map +0 -1
  294. package/lib/createServer-BcaswoFO.js.map +0 -1
  295. package/lib/index-CaILD1AV.js +0 -1292
  296. package/lib/index-CaILD1AV.js.map +0 -1
  297. package/lib/index-TaRXY2w1.js +0 -43
  298. package/lib/index-TaRXY2w1.js.map +0 -1
  299. package/src/lib/plugins/openapi/playground/EnumSelector.tsx +0 -86
  300. package/src/lib/plugins/openapi/playground/ResponseTab.tsx +0 -76
@@ -41,6 +41,7 @@ type SyntaxHighlightProps = {
41
41
  copyable?: boolean;
42
42
  showLanguageIndicator?: boolean;
43
43
  language?: string;
44
+ title?: string;
44
45
  } & Omit<HighlightProps, "children" | "language">;
45
46
 
46
47
  const remapLang = {
@@ -50,6 +51,7 @@ const remapLang = {
50
51
  export const SyntaxHighlight = ({
51
52
  copyable = true,
52
53
  language = "plain",
54
+ title,
53
55
  ...props
54
56
  }: SyntaxHighlightProps) => {
55
57
  const { resolvedTheme } = useTheme();
@@ -70,12 +72,18 @@ export const SyntaxHighlight = ({
70
72
  <ClientOnly
71
73
  fallback={
72
74
  <div className="relative group">
75
+ {title && (
76
+ <div className="text-xs text-muted-foreground absolute top-2 font-mono border-b w-full pb-2 px-4 ">
77
+ {title}
78
+ </div>
79
+ )}
73
80
  <pre
74
81
  className={cn(
75
82
  "relative scrollbar overflow-x-auto",
76
83
  props.className,
77
84
  props.noBackground ? "!bg-transparent" : themeColorClasses,
78
85
  props.wrapLines && "whitespace-pre-wrap break-words",
86
+ title && "pt-10",
79
87
  )}
80
88
  >
81
89
  {props.code}
@@ -95,6 +103,11 @@ export const SyntaxHighlight = ({
95
103
  >
96
104
  {({ className, style, tokens, getLineProps, getTokenProps }) => (
97
105
  <div className="relative group">
106
+ {title && (
107
+ <div className="text-xs text-muted-foreground absolute top-2 font-mono border-b w-full pb-2 px-4 ">
108
+ {title}
109
+ </div>
110
+ )}
98
111
  <pre
99
112
  className={cn(
100
113
  "relative scrollbar overflow-x-auto",
@@ -102,6 +115,7 @@ export const SyntaxHighlight = ({
102
115
  props.className,
103
116
  props.noBackground && "!bg-transparent",
104
117
  props.wrapLines && "whitespace-pre-wrap break-words",
118
+ title && "pt-10",
105
119
  )}
106
120
  style={style}
107
121
  >
@@ -1,26 +1,25 @@
1
1
  import { MoonStarIcon, SunIcon } from "lucide-react";
2
2
  import { useTheme } from "next-themes";
3
3
  import { Button } from "zudoku/ui/Button.js";
4
- import { ClientOnly } from "./ClientOnly.js";
4
+ import { cn } from "../util/cn.js";
5
5
 
6
6
  export const ThemeSwitch = () => {
7
7
  const { resolvedTheme, setTheme } = useTheme();
8
8
  const ThemeIcon = resolvedTheme === "dark" ? MoonStarIcon : SunIcon;
9
9
 
10
10
  return (
11
- <ClientOnly>
12
- <Button
13
- variant="ghost"
14
- aria-label={
15
- resolvedTheme === "dark"
16
- ? "Switch to light mode"
17
- : "Switch to dark mode"
18
- }
19
- className="p-2.5 -m-2.5 rounded-full"
20
- onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
21
- >
22
- <ThemeIcon size={18} />
23
- </Button>
24
- </ClientOnly>
11
+ <Button
12
+ variant="ghost"
13
+ size="icon"
14
+ aria-label={
15
+ resolvedTheme === "dark"
16
+ ? "Switch to light mode"
17
+ : "Switch to dark mode"
18
+ }
19
+ className={cn(resolvedTheme ? "opacity-100" : "opacity-0")}
20
+ onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
21
+ >
22
+ <ThemeIcon size={18} />
23
+ </Button>
25
24
  );
26
25
  };
@@ -0,0 +1,15 @@
1
+ import { useQueryClient } from "@tanstack/react-query";
2
+
3
+ export const CACHE_KEYS = Object.freeze({
4
+ API_IDENTITIES: ["api-identities"],
5
+ });
6
+
7
+ export const useCache = () => {
8
+ const queryClient = useQueryClient();
9
+
10
+ return {
11
+ invalidateCache: async (key: keyof typeof CACHE_KEYS) => {
12
+ await queryClient.invalidateQueries({ queryKey: CACHE_KEYS[key] });
13
+ },
14
+ };
15
+ };
@@ -63,6 +63,7 @@ export const ViewportAnchorProvider = ({
63
63
  const [activeAnchor, setActiveAnchor] = useState("");
64
64
  const observerRef = useRef<IntersectionObserver | null>(null);
65
65
  const registeredElements = useRef(new Set<HTMLElement>());
66
+ const pendingElements = useRef(new Set<HTMLElement>());
66
67
 
67
68
  useEffect(() => {
68
69
  observerRef.current = new IntersectionObserver(
@@ -81,6 +82,13 @@ export const ViewportAnchorProvider = ({
81
82
  },
82
83
  );
83
84
 
85
+ // Process any elements that tried to register before observer was ready
86
+ pendingElements.current.forEach((element) => {
87
+ registeredElements.current.add(element);
88
+ observerRef.current?.observe(element);
89
+ });
90
+ pendingElements.current.clear();
91
+
84
92
  return () => observerRef.current?.disconnect();
85
93
  }, []);
86
94
 
@@ -114,14 +122,22 @@ export const ViewportAnchorProvider = ({
114
122
  const observeFns = useMemo(() => {
115
123
  return {
116
124
  observe: (element: HTMLElement | null) => {
117
- if (!element || !observerRef.current) return;
125
+ if (!element) return;
126
+
127
+ if (!observerRef.current) {
128
+ pendingElements.current.add(element);
129
+ return;
130
+ }
131
+
118
132
  registeredElements.current.add(element);
119
133
  observerRef.current.observe(element);
120
134
  },
121
135
  unobserve: (element: HTMLElement | null) => {
122
- if (!element || !observerRef.current) return;
136
+ if (!element) return;
137
+
138
+ pendingElements.current.delete(element);
123
139
  registeredElements.current.delete(element);
124
- observerRef.current.unobserve(element);
140
+ observerRef.current?.unobserve(element);
125
141
  },
126
142
  };
127
143
  }, []);
@@ -132,8 +148,6 @@ export const ViewportAnchorProvider = ({
132
148
  );
133
149
 
134
150
  return (
135
- <ViewportAnchorContext.Provider value={value}>
136
- {children}
137
- </ViewportAnchorContext.Provider>
151
+ <ViewportAnchorContext value={value}>{children}</ViewportAnchorContext>
138
152
  );
139
153
  };
@@ -3,6 +3,7 @@ import { createContext, useContext } from "react";
3
3
  import { matchPath, useLocation } from "react-router";
4
4
  import { ZudokuContext } from "../../core/ZudokuContext.js";
5
5
  import { joinPath } from "../../util/joinPath.js";
6
+ import { CACHE_KEYS } from "../cache.js";
6
7
  import { traverseSidebar } from "../navigation/utils.js";
7
8
 
8
9
  export const ZudokuReactContext = createContext<ZudokuContext | undefined>(
@@ -21,9 +22,10 @@ export const useZudoku = () => {
21
22
 
22
23
  export const useApiIdentities = () => {
23
24
  const { getApiIdentities } = useZudoku();
25
+
24
26
  return useQuery({
25
27
  queryFn: getApiIdentities,
26
- queryKey: ["api-identities"],
28
+ queryKey: [CACHE_KEYS.API_IDENTITIES],
27
29
  });
28
30
  };
29
31
 
@@ -6,16 +6,21 @@ import { RouterError as RouterErrorImport } from "../errors/RouterError.js";
6
6
  import { ServerError as ServerErrorImport } from "../errors/ServerError.js";
7
7
  import { Button as ButtonImport } from "../ui/Button.js";
8
8
  import { Callout as CalloutImport } from "../ui/Callout.js";
9
- import { Spinner as SpinnerImport } from "./Spinner.js";
10
- import { Markdown as MarkdownImport } from "./Markdown.js";
11
9
  import {
12
10
  Bootstrap as BootstrapImport,
13
11
  BootstrapStatic as BootstrapStaticImport,
14
12
  } from "./Bootstrap.js";
15
13
  import { ClientOnly as ClientOnlyImport } from "./ClientOnly.js";
16
14
  import { Layout as LayoutImport } from "./Layout.js";
15
+ import { Markdown as MarkdownImport } from "./Markdown.js";
16
+ import { Spinner as SpinnerImport } from "./Spinner.js";
17
17
  import { Zudoku as ZudokuImport } from "./Zudoku.js";
18
+ import {
19
+ CACHE_KEYS as CACHE_KEYS_IMPORT,
20
+ useCache as useCacheImport,
21
+ } from "./cache.js";
18
22
  import { useZudoku as useZudokuImport } from "./context/ZudokuContext.js";
23
+
19
24
  export const useMDXComponents = /*@__PURE__*/ useMDXComponentsImport;
20
25
  export const Layout = /*@__PURE__*/ LayoutImport;
21
26
  export const RouterError = /*@__PURE__*/ RouterErrorImport;
@@ -27,6 +32,8 @@ export const Head = /*@__PURE__*/ Helmet;
27
32
 
28
33
  export const useZudoku = /*@__PURE__*/ useZudokuImport;
29
34
  export const useAuth = /*@__PURE__*/ useAuthImport;
35
+ export const useCache = /*@__PURE__*/ useCacheImport;
36
+ export const CACHE_KEYS = /*@__PURE__*/ CACHE_KEYS_IMPORT;
30
37
  export const Zudoku = /*@__PURE__*/ ZudokuImport;
31
38
 
32
39
  export const Callout = /*@__PURE__*/ CalloutImport;
@@ -0,0 +1,35 @@
1
+ import { useEffect } from "react";
2
+ import { matchPath, Outlet, useLocation } from "react-router";
3
+ import { useAuth } from "../authentication/hook.js";
4
+ import { useZudoku } from "../components/context/ZudokuContext.js";
5
+ import { ZudokuError } from "../util/invariant.js";
6
+
7
+ export const RouteGuard = () => {
8
+ const auth = useAuth();
9
+ const zudoku = useZudoku();
10
+ const location = useLocation();
11
+
12
+ const isProtected = zudoku.options.protectedRoutes?.some((path) =>
13
+ matchPath({ path, end: true }, location.pathname),
14
+ );
15
+
16
+ useEffect(() => {
17
+ if (isProtected && !auth.isAuthenticated) {
18
+ void zudoku.authentication?.signIn();
19
+ }
20
+ }, [isProtected, auth.isAuthenticated, zudoku.authentication]);
21
+
22
+ if (isProtected && !auth.isAuthenticated) {
23
+ return null;
24
+ }
25
+
26
+ if (isProtected && !auth.isAuthEnabled) {
27
+ throw new ZudokuError("Authentication is not enabled", {
28
+ title: "Authentication is not enabled",
29
+ developerHint:
30
+ "To use protectedRoutes you need authentication to be enabled",
31
+ });
32
+ }
33
+
34
+ return <Outlet />;
35
+ };
@@ -65,6 +65,7 @@ export type ZudokuContextOptions = {
65
65
  components?: MdxComponentsType;
66
66
  };
67
67
  overrides?: ComponentsContextType;
68
+ protectedRoutes?: string[];
68
69
  };
69
70
 
70
71
  export class ZudokuContext {
@@ -74,16 +75,16 @@ export class ZudokuContext {
74
75
  public meta: ZudokuContextOptions["metadata"];
75
76
  public page: ZudokuContextOptions["page"];
76
77
  public authentication?: ZudokuContextOptions["authentication"];
77
- private navigationPlugins: NavigationPlugin[];
78
+ private readonly navigationPlugins: NavigationPlugin[];
78
79
 
79
- constructor(config: ZudokuContextOptions) {
80
- this.plugins = config.plugins ?? [];
81
- this.topNavigation = config.topNavigation ?? [];
82
- this.sidebars = config.sidebars ?? {};
80
+ constructor(public readonly options: ZudokuContextOptions) {
81
+ this.plugins = options.plugins ?? [];
82
+ this.topNavigation = options.topNavigation ?? [];
83
+ this.sidebars = options.sidebars ?? {};
83
84
  this.navigationPlugins = this.plugins.filter(isNavigationPlugin);
84
- this.authentication = config.authentication;
85
- this.meta = config.metadata;
86
- this.page = config.page;
85
+ this.authentication = options.authentication;
86
+ this.meta = options.metadata;
87
+ this.page = options.page;
87
88
  }
88
89
 
89
90
  initialize = async (): Promise<void> => {
@@ -0,0 +1,29 @@
1
+ import { GraphQLJSON } from "graphql-type-json";
2
+ import { GraphQLScalarType } from "graphql/index.js";
3
+
4
+ export const CIRCULAR_REF = "$[Circular Reference]";
5
+ const handleCircularRefs = (obj: any, visited = new WeakSet()): any => {
6
+ if (obj === CIRCULAR_REF) return CIRCULAR_REF;
7
+ if (obj === null || typeof obj !== "object") return obj;
8
+
9
+ if (visited.has(obj)) return CIRCULAR_REF;
10
+
11
+ visited.add(obj);
12
+
13
+ if (Array.isArray(obj)) {
14
+ return obj.map((item) => handleCircularRefs(item, visited));
15
+ }
16
+
17
+ const result: Record<string, any> = {};
18
+ for (const [key, value] of Object.entries(obj)) {
19
+ result[key] = handleCircularRefs(value, visited);
20
+ }
21
+ return result;
22
+ };
23
+ export const GraphQLJSONSchema = new GraphQLScalarType({
24
+ ...GraphQLJSON,
25
+ name: "JSONSchema",
26
+ description: "OpenAPI schema scalar type that handles circular references",
27
+ serialize: (value: unknown) =>
28
+ GraphQLJSON.serialize(handleCircularRefs(value)),
29
+ });
@@ -1,16 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import SchemaBuilder from "@pothos/core";
3
3
  import {
4
- slugifyWithCounter,
5
4
  type CountableSlugify,
5
+ slugifyWithCounter,
6
6
  } from "@sindresorhus/slugify";
7
7
  import { GraphQLJSON, GraphQLJSONObject } from "graphql-type-json";
8
8
  import { createYoga, type YogaServerOptions } from "graphql-yoga";
9
9
  import {
10
- HttpMethods,
11
- validate,
12
10
  type EncodingObject,
13
11
  type ExampleObject,
12
+ HttpMethods,
14
13
  type OpenAPIDocument,
15
14
  type OperationObject,
16
15
  type ParameterObject,
@@ -18,7 +17,9 @@ import {
18
17
  type SchemaObject,
19
18
  type ServerObject,
20
19
  type TagObject,
20
+ validate,
21
21
  } from "../parser/index.js";
22
+ import { GraphQLJSONSchema } from "./circular.js";
22
23
 
23
24
  export type {
24
25
  EncodingObject,
@@ -45,10 +46,7 @@ export const createOperationSlug = (
45
46
  ) => {
46
47
  const summary =
47
48
  (operation.summary ?? "") +
48
- (operation.operationId
49
- ? "-" +
50
- operation.operationId.slice(0, operation.summary ? Infinity : Infinity)
51
- : "");
49
+ (operation.operationId ? "-" + operation.operationId : "");
52
50
 
53
51
  return slugify(
54
52
  (tag ? tag + "-" : "") +
@@ -65,6 +63,7 @@ const builder = new SchemaBuilder<{
65
63
  Scalars: {
66
64
  JSON: any;
67
65
  JSONObject: any;
66
+ JSONSchema: any;
68
67
  };
69
68
  Context: {
70
69
  schema: OpenAPIDocument;
@@ -74,6 +73,7 @@ const builder = new SchemaBuilder<{
74
73
 
75
74
  const JSONScalar = builder.addScalarType("JSON", GraphQLJSON);
76
75
  const JSONObjectScalar = builder.addScalarType("JSONObject", GraphQLJSONObject);
76
+ const JSONSchemaScalar = builder.addScalarType("JSONSchema", GraphQLJSONSchema);
77
77
 
78
78
  const getAllTags = (schema: OpenAPIDocument): TagObject[] => {
79
79
  const tags = schema.tags ?? [];
@@ -238,7 +238,7 @@ const ParameterItem = builder
238
238
  })),
239
239
  nullable: true,
240
240
  }),
241
- schema: t.expose("schema", { type: JSONScalar, nullable: true }),
241
+ schema: t.expose("schema", { type: JSONSchemaScalar, nullable: true }),
242
242
  }),
243
243
  });
244
244
 
@@ -252,7 +252,7 @@ const MediaTypeItem = builder
252
252
  .implement({
253
253
  fields: (t) => ({
254
254
  mediaType: t.exposeString("mediaType"),
255
- schema: t.expose("schema", { type: JSONScalar, nullable: true }),
255
+ schema: t.expose("schema", { type: JSONSchemaScalar, nullable: true }),
256
256
  examples: t.expose("examples", { type: [ExampleItem], nullable: true }),
257
257
  encoding: t.expose("encoding", { type: [EncodingItem], nullable: true }),
258
258
  }),
@@ -1,10 +1,9 @@
1
1
  import type { JSONSchema4, JSONSchema6 } from "json-schema";
2
+ import { CIRCULAR_REF } from "../../graphql/circular.js";
2
3
  import { resolveLocalRef } from "./resolveRef.js";
3
4
 
4
5
  export type JSONSchema = JSONSchema4 | JSONSchema6;
5
6
 
6
- export const CIRCULAR_REF = "$[Circular Reference]";
7
-
8
7
  type CustomResolver = (ref: string) => Promise<JSONSchema | undefined>;
9
8
 
10
9
  const cache = new Map<JSONSchema, JSONSchema>();
@@ -2,7 +2,7 @@ import { useMDXComponents } from "@mdx-js/react";
2
2
  import slugify from "@sindresorhus/slugify";
3
3
  import { Helmet } from "@zudoku/react-helmet-async";
4
4
  import { type PropsWithChildren, useEffect } from "react";
5
- import { Link } from "react-router";
5
+ import { Link, useHref } from "react-router";
6
6
  import { CategoryHeading } from "../../components/CategoryHeading.js";
7
7
  import { Heading } from "../../components/Heading.js";
8
8
  import { ProseClasses } from "../../components/Markdown.js";
@@ -51,6 +51,13 @@ export const MdxPage = ({
51
51
  }
52
52
  >) => {
53
53
  const categoryTitle = useCurrentItem()?.categoryLabel;
54
+ let canonicalUrl = null;
55
+ const path = useHref("");
56
+ if (typeof window !== "undefined") {
57
+ const domain = window.location.origin;
58
+ canonicalUrl = `${domain}${path}`;
59
+ }
60
+
54
61
  const title = frontmatter.title;
55
62
  const category = frontmatter.category ?? categoryTitle;
56
63
  const hideToc = frontmatter.toc === false || defaultOptions?.toc === false;
@@ -87,6 +94,7 @@ export const MdxPage = ({
87
94
  <Helmet>
88
95
  <title>{pageTitle}</title>
89
96
  {excerpt && <meta name="description" content={excerpt} />}
97
+ {canonicalUrl && <link rel="canonical" href={canonicalUrl} />}
90
98
  </Helmet>
91
99
  <div
92
100
  className={cn(
@@ -95,7 +95,6 @@ export const OperationListItem = ({
95
95
  level={3}
96
96
  className="capitalize"
97
97
  id={`${operation.slug}/request-body`}
98
- registerSidebarAnchor
99
98
  >
100
99
  Request Body
101
100
  </Heading>
@@ -108,7 +107,6 @@ export const OperationListItem = ({
108
107
  level={3}
109
108
  className="capitalize mt-8 pt-8 border-t"
110
109
  id={`${operation.slug}/responses`}
111
- registerSidebarAnchor
112
110
  >
113
111
  Responses
114
112
  </Heading>
@@ -54,6 +54,7 @@ export const ParameterListItem = ({
54
54
  </Badge>
55
55
  )}
56
56
  {parameter.required && <Badge variant="outline">required</Badge>}
57
+ {parameter.style === "form" && <Badge variant="secondary">form</Badge>}
57
58
  </div>
58
59
  {parameter.description && (
59
60
  <Markdown
@@ -1,20 +1,24 @@
1
1
  import type { OperationListItemResult } from "./OperationList.js";
2
2
  import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
3
+ import { Content } from "./SidecarExamples.js";
3
4
 
4
5
  export const PlaygroundDialogWrapper = ({
5
6
  server,
6
7
  servers,
7
8
  operation,
9
+ examples,
8
10
  }: {
9
11
  server: string;
10
12
  servers?: string[];
11
13
  operation: OperationListItemResult;
14
+ examples?: Content;
12
15
  }) => {
13
16
  const headers = operation.parameters
14
17
  ?.filter((p) => p.in === "header")
15
18
  .map((p) => ({
16
19
  name: p.name,
17
20
  defaultValue: p.examples?.find((x) => x.value)?.value ?? "",
21
+ defaultActive: false,
18
22
  }));
19
23
  const queryParams = operation.parameters
20
24
  ?.filter((p) => p.in === "query")
@@ -39,6 +43,7 @@ export const PlaygroundDialogWrapper = ({
39
43
  headers={headers}
40
44
  queryParams={queryParams}
41
45
  pathParams={pathParams}
46
+ examples={examples}
42
47
  />
43
48
  );
44
49
  };
@@ -99,7 +99,7 @@ export const Sidecar = ({
99
99
  selectedResponse?: string;
100
100
  onSelectResponse: (response: string) => void;
101
101
  }) => {
102
- const { input, type } = useOasConfig();
102
+ const { input, type, options } = useOasConfig();
103
103
  const query = useCreateQuery(GetServerQuery, { input, type });
104
104
  const result = useSuspenseQuery(query);
105
105
 
@@ -109,7 +109,8 @@ export const Sidecar = ({
109
109
  const [, startTransition] = useTransition();
110
110
  const [selectedExample, setSelectedExample] = useState<unknown>();
111
111
 
112
- const selectedLang = searchParams.get("lang") ?? "shell";
112
+ const selectedLang =
113
+ searchParams.get("lang") ?? options?.examplesDefaultLanguage ?? "shell";
113
114
 
114
115
  const requestBodyContent = operation.requestBody?.content;
115
116
 
@@ -198,6 +199,7 @@ export const Sidecar = ({
198
199
  server={result.data.schema.url}
199
200
  servers={result.data.schema.servers.map((server) => server.url)}
200
201
  operation={operation}
202
+ examples={requestBodyContent ?? undefined}
201
203
  />
202
204
  )}
203
205
  </SidecarBox.Head>
@@ -2,7 +2,6 @@ import { matchPath } from "react-router";
2
2
  import { type ZudokuPlugin } from "../../core/plugins.js";
3
3
  import { graphql } from "./graphql/index.js";
4
4
 
5
- import { useQuery } from "@tanstack/react-query";
6
5
  import { CirclePlayIcon, LogInIcon } from "lucide-react";
7
6
  import type { SidebarItem } from "../../../config/validators/SidebarSchema.js";
8
7
  import { useAuth } from "../../authentication/hook.js";
@@ -81,18 +80,15 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
81
80
  method,
82
81
  url,
83
82
  ...props
84
- }: Partial<PlaygroundContentProps> & { requireAuth: boolean }) => {
83
+ }: Partial<PlaygroundContentProps> &
84
+ Pick<PlaygroundContentProps, "server"> & {
85
+ requireAuth: boolean;
86
+ }) => {
85
87
  const auth = useAuth();
86
- // We don't have the GraphQL context here
87
- const serverQuery = useQuery({
88
- queryFn: () =>
89
- client.fetch(GetCategoriesQuery, {
90
- type: config.type,
91
- input: config.input,
92
- }),
93
- enabled: !server,
94
- queryKey: ["playground-server"],
95
- });
88
+
89
+ if (!server) {
90
+ throw new Error("Server is required");
91
+ }
96
92
 
97
93
  if (requireAuth && !auth.isAuthenticated) {
98
94
  return (
@@ -110,9 +106,7 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
110
106
  <PlaygroundDialog
111
107
  url={url ?? "/"}
112
108
  method={method ?? "get"}
113
- server={
114
- server ?? serverQuery.data?.schema.url ?? "https://example.com"
115
- }
109
+ server={server}
116
110
  {...props}
117
111
  >
118
112
  <Button className="gap-2 items-center" variant="outline">
@@ -20,7 +20,14 @@ export type OasPluginConfig = {
20
20
  server?: string;
21
21
  navigationId?: string;
22
22
  skipPreload?: boolean;
23
- } & OasSource;
23
+ } & OasPluginConfigOptions &
24
+ OasSource;
25
+
26
+ export type OasPluginConfigOptions = {
27
+ options?: {
28
+ examplesDefaultLanguage?: string;
29
+ };
30
+ };
24
31
 
25
32
  export type OasPluginContext = {
26
33
  server?: string;
@@ -28,4 +35,5 @@ export type OasPluginContext = {
28
35
  skipPreload?: boolean;
29
36
  version?: string;
30
37
  versions: Record<string, string>;
31
- } & ContextOasSource;
38
+ } & ContextOasSource &
39
+ OasPluginConfigOptions;
@@ -0,0 +1,54 @@
1
+ import { Button } from "zudoku/ui/Button.js";
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuGroup,
6
+ DropdownMenuItem,
7
+ DropdownMenuLabel,
8
+ DropdownMenuSeparator,
9
+ DropdownMenuTrigger,
10
+ } from "zudoku/ui/DropdownMenu.js";
11
+ import { Content, Example } from "../SidecarExamples.js";
12
+
13
+ const ExamplesDropdown = ({
14
+ examples,
15
+ onSelect,
16
+ }: {
17
+ examples: Content;
18
+ onSelect: (example: Example) => void;
19
+ }) => {
20
+ return (
21
+ <div className="flex flex-col gap-2 mt-2 items-end">
22
+ <DropdownMenu>
23
+ <DropdownMenuTrigger asChild>
24
+ <Button variant="outline">Use Example</Button>
25
+ </DropdownMenuTrigger>
26
+ <DropdownMenuContent className="w-56">
27
+ {examples.map((example) => {
28
+ return (
29
+ <div key={example.mediaType}>
30
+ <DropdownMenuLabel>{example.mediaType}</DropdownMenuLabel>
31
+ <DropdownMenuSeparator />
32
+ <DropdownMenuGroup>
33
+ {example.examples?.map((example) => {
34
+ return (
35
+ <DropdownMenuItem
36
+ key={example.name}
37
+ onSelect={() => onSelect(example)}
38
+ className="line-clamp-1"
39
+ >
40
+ {example.summary ?? example.name}
41
+ </DropdownMenuItem>
42
+ );
43
+ })}
44
+ </DropdownMenuGroup>
45
+ </div>
46
+ );
47
+ })}
48
+ </DropdownMenuContent>
49
+ </DropdownMenu>
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default ExamplesDropdown;