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 +1 @@
1
- {"version":3,"file":"zudoku.plugin-markdown.js","sources":["../src/lib/plugins/markdown/resolver.ts","../src/lib/plugins/markdown/index.tsx"],"sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\nimport {\n ZudokuConfig,\n ZudokuDocsConfig,\n} from \"../../../config/validators/validate.js\";\n\nconst DEFAULT_DOCS_FILES = \"/pages/**/*.{md,mdx}\";\n\n// TODO: This should be dynamic based on the glob selector\nconst SUPPORTED_EXTENSIONS = [\".md\", \".mdx\"];\n\n/**\n * Utilities for resolving markdown file paths and routes\n */\nexport class DocResolver {\n constructor(private config: ZudokuConfig) {}\n\n fileMap = new Map<string, string>();\n\n /**\n * Gets the default docs config from the zudoku config\n */\n getDocsConfigs() {\n const docsConfigs: ZudokuDocsConfig[] = this.config.docs\n ? Array.isArray(this.config.docs)\n ? this.config.docs\n : [this.config.docs]\n : [{ files: DEFAULT_DOCS_FILES }];\n\n return docsConfigs;\n }\n\n /**\n * Resolves the first matching file system path for a given docId\n * @param docId - The docId to resolve\n * @returns\n */\n resolveFilePath(docId: string) {\n const docsConfigs = this.getDocsConfigs();\n let fsPath: string | undefined;\n\n docsConfigs.forEach(({ files: fileGlob }) => {\n if (fsPath) {\n return;\n }\n const rootDir = DocResolver.getRootDir(fileGlob);\n for (const ext of SUPPORTED_EXTENSIONS) {\n if (fsPath) {\n return;\n }\n const checkPath = path.join(rootDir, `${docId}${ext}`);\n if (fs.existsSync(checkPath)) {\n fsPath = checkPath;\n }\n }\n });\n\n return fsPath;\n }\n\n /**\n * Gets the root directory from a files glob\n */\n private static getRootDir(filesGlob: string) {\n let rootDir = filesGlob.split(\"**\")[0];\n if (!rootDir) {\n throw new Error(\"Invalid files glob. Must have '**' in the path.\");\n }\n rootDir = rootDir.replace(\"/**\", \"/\");\n return rootDir;\n }\n\n /**\n * Resolves the route path for a given file system path\n * @param options - The options to resolve the route path\n * @returns The string route path\n */\n static resolveRoutePath({\n filesGlob,\n fsPath,\n }: {\n filesGlob: string;\n fsPath: string;\n }) {\n const rootDir = this.getRootDir(filesGlob);\n const re = new RegExp(`^${rootDir}(.*).mdx?`);\n const match = fsPath.match(re);\n const routePath = match?.at(1);\n return routePath;\n }\n}\n","import type { Toc } from \"@stefanprobst/rehype-extract-toc\";\nimport type { MDXProps } from \"mdx/types.js\";\nimport { RouteObject } from \"react-router-dom\";\nimport { ZudokuDocsConfig } from \"../../../config/validators/validate.js\";\nimport type { DevPortalPlugin } from \"../../core/plugins.js\";\nimport { DocResolver } from \"./resolver.js\";\n\nexport interface MarkdownPluginOptions extends ZudokuDocsConfig {\n fileImports: Record<string, () => Promise<MDXImport>>;\n}\nexport type MarkdownPluginDefaultOptions = Pick<\n Frontmatter,\n \"toc\" | \"disablePager\"\n>;\n\nexport type Frontmatter = {\n title?: string;\n description?: string;\n category?: string;\n toc?: boolean;\n disablePager?: boolean;\n};\n\nexport type MDXImport = {\n tableOfContents: Toc;\n frontmatter: Frontmatter;\n default: (props: MDXProps) => JSX.Element;\n};\n\nexport const markdownPlugin = (\n options: MarkdownPluginOptions[],\n): DevPortalPlugin => ({\n getRoutes: () => {\n const routeMap = new Map<string, RouteObject>();\n options.forEach(({ fileImports, files, defaultOptions }) =>\n Object.entries(fileImports).flatMap(([file, importPromise]) => {\n const routePath = DocResolver.resolveRoutePath({\n filesGlob: files,\n fsPath: file,\n });\n\n if (!routePath) return [];\n\n if (routeMap.has(routePath)) {\n // eslint-disable-next-line no-console\n console.warn(\n `Duplicate route path found for ${routePath}. Skipping file at '${file}'.`,\n );\n return [];\n }\n\n const route: RouteObject = {\n path: routePath,\n lazy: async () => {\n const { MdxPage } = await import(\"./MdxPage.js\");\n const { default: Component, ...props } = await importPromise();\n return {\n element: (\n <MdxPage\n mdxComponent={Component}\n {...props}\n defaultOptions={defaultOptions}\n />\n ),\n };\n },\n };\n routeMap.set(routePath, route);\n }),\n );\n return [...routeMap.values()];\n },\n});\n"],"names":["DEFAULT_DOCS_FILES","SUPPORTED_EXTENSIONS","DocResolver","config","__publicField","docId","docsConfigs","fsPath","fileGlob","rootDir","ext","checkPath","path","fs","filesGlob","re","match","markdownPlugin","options","routeMap","fileImports","files","defaultOptions","file","importPromise","routePath","route","MdxPage","Component","props","jsx"],"mappings":";;;;;AAOA,MAAMA,IAAqB,wBAGrBC,IAAuB,CAAC,OAAO,MAAM;AAKpC,MAAMC,EAAY;AAAA,EACvB,YAAoBC,GAAsB;AAE1C,IAAAC,EAAA,qCAAc;AAFM,SAAA,SAAAD;AAAA,EAAuB;AAAA;AAAA;AAAA;AAAA,EAO3C,iBAAiB;AAOR,WANiC,KAAK,OAAO,OAChD,MAAM,QAAQ,KAAK,OAAO,IAAI,IAC5B,KAAK,OAAO,OACZ,CAAC,KAAK,OAAO,IAAI,IACnB,CAAC,EAAE,OAAOH,EAAA,CAAoB;AAAA,EAGpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgBK,GAAe;AACvB,UAAAC,IAAc,KAAK;AACrB,QAAAC;AAEJ,WAAAD,EAAY,QAAQ,CAAC,EAAE,OAAOE,QAAe;AAC3C,UAAID;AACF;AAEI,YAAAE,IAAUP,EAAY,WAAWM,CAAQ;AAC/C,iBAAWE,KAAOT,GAAsB;AACtC,YAAIM;AACF;AAEI,cAAAI,IAAYC,EAAK,KAAKH,GAAS,GAAGJ,CAAK,GAAGK,CAAG,EAAE;AACjD,QAAAG,EAAG,WAAWF,CAAS,MAChBJ,IAAAI;AAAA,MAEb;AAAA,IAAA,CACD,GAEMJ;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAe,WAAWO,GAAmB;AAC3C,QAAIL,IAAUK,EAAU,MAAM,IAAI,EAAE,CAAC;AACrC,QAAI,CAACL;AACG,YAAA,IAAI,MAAM,iDAAiD;AAEzD,WAAAA,IAAAA,EAAQ,QAAQ,OAAO,GAAG,GAC7BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,iBAAiB;AAAA,IACtB,WAAAK;AAAA,IACA,QAAAP;AAAA,EAAA,GAIC;AACK,UAAAE,IAAU,KAAK,WAAWK,CAAS,GACnCC,IAAK,IAAI,OAAO,IAAIN,CAAO,WAAW,GACtCO,IAAQT,EAAO,MAAMQ,CAAE;AAEtB,WADWC,KAAA,gBAAAA,EAAO,GAAG;AAAA,EAE9B;AACF;AC9Da,MAAAC,IAAiB,CAC5BC,OACqB;AAAA,EACrB,WAAW,MAAM;AACT,UAAAC,wBAAe;AACb,WAAAD,EAAA;AAAA,MAAQ,CAAC,EAAE,aAAAE,GAAa,OAAAC,GAAO,gBAAAC,QACrC,OAAO,QAAQF,CAAW,EAAE,QAAQ,CAAC,CAACG,GAAMC,CAAa,MAAM;AACvD,cAAAC,IAAYvB,EAAY,iBAAiB;AAAA,UAC7C,WAAWmB;AAAA,UACX,QAAQE;AAAA,QAAA,CACT;AAEG,YAAA,CAACE,EAAW,QAAO;AAEnB,YAAAN,EAAS,IAAIM,CAAS;AAEhB,yBAAA;AAAA,YACN,kCAAkCA,CAAS,uBAAuBF,CAAI;AAAA,UAAA,GAEjE;AAGT,cAAMG,IAAqB;AAAA,UACzB,MAAMD;AAAA,UACN,MAAM,YAAY;AAChB,kBAAM,EAAE,SAAAE,EAAA,IAAY,MAAM,OAAO,uBAAc,GACzC,EAAE,SAASC,GAAW,GAAGC,EAAM,IAAI,MAAML;AACxC,mBAAA;AAAA,cACL,SACEM,gBAAAA,EAAA;AAAA,gBAACH;AAAA,gBAAA;AAAA,kBACC,cAAcC;AAAA,kBACb,GAAGC;AAAA,kBACJ,gBAAAP;AAAA,gBAAA;AAAA,cACF;AAAA,YAAA;AAAA,UAGN;AAAA,QAAA;AAEO,QAAAH,EAAA,IAAIM,GAAWC,CAAK;AAAA,MAAA,CAC9B;AAAA,IAAA,GAEI,CAAC,GAAGP,EAAS,OAAA,CAAQ;AAAA,EAC9B;AACF;"}
1
+ {"version":3,"file":"zudoku.plugin-markdown.js","sources":["../src/lib/plugins/markdown/generateRoutes.tsx","../src/lib/plugins/markdown/index.tsx"],"sourcesContent":["import { type RouteObject } from \"react-router-dom\";\n\nimport {\n MarkdownPluginDefaultOptions,\n MarkdownPluginOptions,\n} from \"./index.js\";\n\nexport const generateRoutes = (\n markdownFiles: MarkdownPluginOptions[\"markdownFiles\"],\n filesPath: string,\n defaultOptions?: MarkdownPluginDefaultOptions,\n): RouteObject[] =>\n Object.entries(markdownFiles).flatMap(([file, importPromise]) => {\n let rootDir = filesPath.split(\"**\")[0];\n rootDir = rootDir.replace(\"/**\", \"/\");\n const re = new RegExp(`^${rootDir}(.*).mdx?`);\n const match = file.match(re);\n const fsPath = match?.at(1);\n\n if (!fsPath) return [];\n\n return {\n path: fsPath,\n lazy: async () => {\n const { MdxPage } = await import(\"./MdxPage.js\");\n const { default: Component, ...props } = await importPromise();\n return {\n element: (\n <MdxPage\n mdxComponent={Component}\n {...props}\n defaultOptions={defaultOptions}\n />\n ),\n };\n },\n } satisfies RouteObject;\n });\n","import type { Toc } from \"@stefanprobst/rehype-extract-toc\";\nimport type { MDXProps } from \"mdx/types.js\";\nimport type { DevPortalPlugin } from \"../../core/plugins.js\";\nimport { generateRoutes } from \"./generateRoutes.js\";\n\nexport type MarkdownPluginOptions = {\n markdownFiles: Record<string, () => Promise<MDXImport>>;\n defaultOptions?: MarkdownPluginDefaultOptions;\n filesPath: string;\n};\nexport type MarkdownPluginDefaultOptions = Pick<\n Frontmatter,\n \"toc\" | \"disablePager\"\n>;\n\nexport type Frontmatter = {\n title?: string;\n description?: string;\n category?: string;\n toc?: boolean;\n disablePager?: boolean;\n};\n\nexport type MDXImport = {\n tableOfContents: Toc;\n frontmatter: Frontmatter;\n default: (props: MDXProps) => JSX.Element;\n};\n\nexport const markdownPlugin = ({\n markdownFiles,\n defaultOptions,\n filesPath,\n}: MarkdownPluginOptions): DevPortalPlugin => ({\n getRoutes: () => generateRoutes(markdownFiles, filesPath, defaultOptions),\n});\n"],"names":["generateRoutes","markdownFiles","filesPath","defaultOptions","file","importPromise","rootDir","re","match","fsPath","MdxPage","Component","props","jsx","markdownPlugin"],"mappings":";AAOO,MAAMA,IAAiB,CAC5BC,GACAC,GACAC,MAEA,OAAO,QAAQF,CAAa,EAAE,QAAQ,CAAC,CAACG,GAAMC,CAAa,MAAM;AAC/D,MAAIC,IAAUJ,EAAU,MAAM,IAAI,EAAE,CAAC;AAC3B,EAAAI,IAAAA,EAAQ,QAAQ,OAAO,GAAG;AACpC,QAAMC,IAAK,IAAI,OAAO,IAAID,CAAO,WAAW,GACtCE,IAAQJ,EAAK,MAAMG,CAAE,GACrBE,IAASD,KAAA,gBAAAA,EAAO,GAAG;AAErB,SAACC,IAEE;AAAA,IACL,MAAMA;AAAA,IACN,MAAM,YAAY;AAChB,YAAM,EAAE,SAAAC,EAAA,IAAY,MAAM,OAAO,uBAAc,GACzC,EAAE,SAASC,GAAW,GAAGC,EAAM,IAAI,MAAMP;AACxC,aAAA;AAAA,QACL,SACEQ,gBAAAA,EAAA;AAAA,UAACH;AAAA,UAAA;AAAA,YACC,cAAcC;AAAA,YACb,GAAGC;AAAA,YACJ,gBAAAT;AAAA,UAAA;AAAA,QACF;AAAA,MAAA;AAAA,IAGN;AAAA,EAAA,IAhBkB;AAkBtB,CAAC,GCRUW,IAAiB,CAAC;AAAA,EAC7B,eAAAb;AAAA,EACA,gBAAAE;AAAA,EACA,WAAAD;AACF,OAA+C;AAAA,EAC7C,WAAW,MAAMF,EAAeC,GAAeC,GAAWC,CAAc;AAC1E;"}
@@ -1,14 +1,14 @@
1
1
  import "./jsx-runtime-B6kdoens.js";
2
- import { o as s } from "./index-BIl-R3aH.js";
3
- import "./ZudokuContext-DSipF8sq.js";
2
+ import { o as s } from "./index-Bs9roz8y.js";
3
+ import "./ZudokuContext-DEoP3GGJ.js";
4
4
  import "lucide-react";
5
5
  import "zudoku/openapi-worker";
6
- import "./index-Dssw7Gff.js";
7
- import "./ErrorPage-CUz-Zzmx.js";
8
- import "./Markdown-C-0TaxoY.js";
6
+ import "./index-CBr6BM_4.js";
7
+ import "./Markdown-DM4zv3MA.js";
8
+ import "./joinPath-B7kNnUX4.js";
9
9
  import "./urql-core-KJnLL26g.js";
10
- import "./router-Oe6YmY6B.js";
11
- import "./index-SrtqdZ3j.js";
10
+ import "./router-BsfSoK2j.js";
11
+ import "./index-CRo94sKK.js";
12
12
  export {
13
13
  s as openApiPlugin
14
14
  };
@@ -1,4 +1,4 @@
1
- import { r as o } from "./router-Oe6YmY6B.js";
1
+ import { r as o } from "./router-BsfSoK2j.js";
2
2
  const a = (r) => ({
3
3
  getRoutes: () => r.redirects.map(({ from: e, to: t }) => ({
4
4
  path: e,
@@ -1,11 +1,7 @@
1
1
  import { j as n } from "./jsx-runtime-B6kdoens.js";
2
- import { useSyncExternalStore as a, lazy as i } from "react";
3
- const o = () => () => {
4
- }, c = (e) => a(
5
- o,
6
- () => "client",
7
- () => "server"
8
- ) === "client" ? e.children : null, l = {
2
+ import { lazy as a } from "react";
3
+ import { C as i } from "./ClientOnly-CVN6leDu.js";
4
+ const o = {
9
5
  theme: {
10
6
  components: {
11
7
  AIChatPageWrapper: {
@@ -23,29 +19,29 @@ const o = () => () => {
23
19
  }
24
20
  }
25
21
  }
26
- }, p = {}, u = i(() => import("./InkeepCustomTrigger-CE5-K5ex.js")), h = ({
22
+ }, p = {}, c = a(() => import("./InkeepCustomTrigger-CE5-K5ex.js")), l = ({
27
23
  prefilledQuery: e,
28
24
  isOpen: r,
29
25
  onClose: t,
30
26
  settings: s
31
27
  }) => /* @__PURE__ */ n.jsx(
32
- u,
28
+ c,
33
29
  {
34
30
  isOpen: r,
35
31
  onClose: t,
36
- baseSettings: { ...l, ...s },
32
+ baseSettings: { ...o, ...s },
37
33
  aiChatSettings: p,
38
34
  searchSettings: {
39
35
  prefilledQuery: e || void 0
40
36
  }
41
37
  }
42
- ), g = (e) => ({
38
+ ), d = (e) => ({
43
39
  renderSearch: ({
44
40
  isOpen: r,
45
41
  onClose: t
46
- }) => /* @__PURE__ */ n.jsx(c, { children: /* @__PURE__ */ n.jsx(h, { isOpen: r, onClose: t, settings: e }) })
42
+ }) => /* @__PURE__ */ n.jsx(i, { children: /* @__PURE__ */ n.jsx(l, { isOpen: r, onClose: t, settings: e }) })
47
43
  });
48
44
  export {
49
- g as inkeepSearchPlugin
45
+ d as inkeepSearchPlugin
50
46
  };
51
47
  //# sourceMappingURL=zudoku.plugin-search-inkeep.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-search-inkeep.js","sources":["../src/lib/components/ClientOnly.tsx","../src/lib/plugins/search-inkeep/inkeep.ts","../src/lib/plugins/search-inkeep/index.tsx"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\n\nconst noop = () => () => {};\n\nexport const ClientOnly = (props: { children: React.ReactNode }) => {\n const value = useSyncExternalStore(\n noop,\n () => \"client\",\n () => \"server\",\n );\n\n return value === \"client\" ? props.children : null;\n};\n","import {\n InkeepAIChatSettings,\n InkeepModalSettings,\n InkeepSearchSettings,\n} from \"@inkeep/widgets\";\n\nconst baseSettings = {\n theme: {\n components: {\n AIChatPageWrapper: {\n defaultProps: {\n size: \"shrink-vertically\",\n variant: \"no-shadow\",\n },\n },\n SearchBarTrigger: {\n defaultProps: {\n size: \"expand\",\n variant: \"subtle\", // Choose from 'emphasized' or 'subtle'\n },\n },\n },\n },\n} as const;\n\nconst modalSettings: InkeepModalSettings = {};\n\nconst searchSettings: InkeepSearchSettings = {};\n\nconst aiChatSettings: InkeepAIChatSettings = {};\n\nexport { aiChatSettings, baseSettings, modalSettings, searchSettings };\n","import type { InkeepWidgetBaseSettings } from \"@inkeep/widgets\";\nimport { lazy } from \"react\";\nimport { ClientOnly } from \"../../components/ClientOnly.js\";\nimport type { DevPortalPlugin } from \"../../core/plugins.js\";\nimport { aiChatSettings, baseSettings } from \"./inkeep.js\";\n\ntype PickedPluginInkeepBaseSettings =\n | \"apiKey\"\n | \"integrationId\"\n | \"organizationId\"\n | \"primaryBrandColor\"\n | \"organizationDisplayName\";\n\ntype PluginInkeepBaseSettings = Pick<\n InkeepWidgetBaseSettings,\n PickedPluginInkeepBaseSettings\n>;\n\nconst Inkeep = lazy(() => import(\"./InkeepCustomTrigger.js\"));\n\nconst InkeepSearch = ({\n prefilledQuery,\n isOpen,\n onClose,\n settings,\n}: {\n isOpen: boolean;\n onClose: () => void;\n prefilledQuery?: string | null;\n settings: PluginInkeepBaseSettings;\n}) => {\n return (\n <Inkeep\n isOpen={isOpen}\n onClose={onClose}\n baseSettings={{ ...baseSettings, ...settings }}\n aiChatSettings={aiChatSettings}\n searchSettings={{\n prefilledQuery: prefilledQuery || undefined,\n }}\n />\n );\n};\n\nexport const inkeepSearchPlugin = (\n settings: PluginInkeepBaseSettings,\n): DevPortalPlugin => {\n return {\n renderSearch: ({\n isOpen,\n onClose,\n }: {\n isOpen: boolean;\n onClose: () => void;\n }) => {\n return (\n <ClientOnly>\n <InkeepSearch isOpen={isOpen} onClose={onClose} settings={settings} />\n </ClientOnly>\n );\n },\n };\n};\n"],"names":["noop","ClientOnly","props","useSyncExternalStore","baseSettings","aiChatSettings","Inkeep","lazy","InkeepSearch","prefilledQuery","isOpen","onClose","settings","jsx","inkeepSearchPlugin"],"mappings":";;AAEA,MAAMA,IAAO,MAAM,MAAM;AAAC,GAEbC,IAAa,CAACC,MACXC;AAAA,EACZH;AAAA,EACA,MAAM;AAAA,EACN,MAAM;AAAA,MAGS,WAAWE,EAAM,WAAW,MCLzCE,IAAe;AAAA,EACnB,OAAO;AAAA,IACL,YAAY;AAAA,MACV,mBAAmB;AAAA,QACjB,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,kBAAkB;AAAA,QAChB,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,GAMMC,IAAuC,CAAC,GCXxCC,IAASC,EAAK,MAAM,OAAO,mCAA0B,CAAC,GAEtDC,IAAe,CAAC;AAAA,EACpB,gBAAAC;AAAA,EACA,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,UAAAC;AACF,MAOIC,gBAAAA,EAAA;AAAA,EAACP;AAAA,EAAA;AAAA,IACC,QAAAI;AAAA,IACA,SAAAC;AAAA,IACA,cAAc,EAAE,GAAGP,GAAc,GAAGQ,EAAS;AAAA,IAC7C,gBAAAP;AAAA,IACA,gBAAgB;AAAA,MACd,gBAAgBI,KAAkB;AAAA,IACpC;AAAA,EAAA;AAAA,GAKOK,IAAqB,CAChCF,OAEO;AAAA,EACL,cAAc,CAAC;AAAA,IACb,QAAAF;AAAA,IACA,SAAAC;AAAA,EAAA,4BAMGV,GACC,EAAA,UAAAY,gBAAAA,EAAAA,IAACL,KAAa,QAAAE,GAAgB,SAAAC,GAAkB,UAAAC,EAAoB,CAAA,EACtE,CAAA;AAEJ;"}
1
+ {"version":3,"file":"zudoku.plugin-search-inkeep.js","sources":["../src/lib/plugins/search-inkeep/inkeep.ts","../src/lib/plugins/search-inkeep/index.tsx"],"sourcesContent":["import {\n InkeepAIChatSettings,\n InkeepModalSettings,\n InkeepSearchSettings,\n} from \"@inkeep/widgets\";\n\nconst baseSettings = {\n theme: {\n components: {\n AIChatPageWrapper: {\n defaultProps: {\n size: \"shrink-vertically\",\n variant: \"no-shadow\",\n },\n },\n SearchBarTrigger: {\n defaultProps: {\n size: \"expand\",\n variant: \"subtle\", // Choose from 'emphasized' or 'subtle'\n },\n },\n },\n },\n} as const;\n\nconst modalSettings: InkeepModalSettings = {};\n\nconst searchSettings: InkeepSearchSettings = {};\n\nconst aiChatSettings: InkeepAIChatSettings = {};\n\nexport { aiChatSettings, baseSettings, modalSettings, searchSettings };\n","import type { InkeepWidgetBaseSettings } from \"@inkeep/widgets\";\nimport { lazy } from \"react\";\nimport { ClientOnly } from \"../../components/ClientOnly.js\";\nimport type { DevPortalPlugin } from \"../../core/plugins.js\";\nimport { aiChatSettings, baseSettings } from \"./inkeep.js\";\n\ntype PickedPluginInkeepBaseSettings =\n | \"apiKey\"\n | \"integrationId\"\n | \"organizationId\"\n | \"primaryBrandColor\"\n | \"organizationDisplayName\";\n\ntype PluginInkeepBaseSettings = Pick<\n InkeepWidgetBaseSettings,\n PickedPluginInkeepBaseSettings\n>;\n\nconst Inkeep = lazy(() => import(\"./InkeepCustomTrigger.js\"));\n\nconst InkeepSearch = ({\n prefilledQuery,\n isOpen,\n onClose,\n settings,\n}: {\n isOpen: boolean;\n onClose: () => void;\n prefilledQuery?: string | null;\n settings: PluginInkeepBaseSettings;\n}) => {\n return (\n <Inkeep\n isOpen={isOpen}\n onClose={onClose}\n baseSettings={{ ...baseSettings, ...settings }}\n aiChatSettings={aiChatSettings}\n searchSettings={{\n prefilledQuery: prefilledQuery || undefined,\n }}\n />\n );\n};\n\nexport const inkeepSearchPlugin = (\n settings: PluginInkeepBaseSettings,\n): DevPortalPlugin => {\n return {\n renderSearch: ({\n isOpen,\n onClose,\n }: {\n isOpen: boolean;\n onClose: () => void;\n }) => {\n return (\n <ClientOnly>\n <InkeepSearch isOpen={isOpen} onClose={onClose} settings={settings} />\n </ClientOnly>\n );\n },\n };\n};\n"],"names":["baseSettings","aiChatSettings","Inkeep","lazy","InkeepSearch","prefilledQuery","isOpen","onClose","settings","jsx","inkeepSearchPlugin","ClientOnly"],"mappings":";;;AAMA,MAAMA,IAAe;AAAA,EACnB,OAAO;AAAA,IACL,YAAY;AAAA,MACV,mBAAmB;AAAA,QACjB,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,kBAAkB;AAAA,QAChB,cAAc;AAAA,UACZ,MAAM;AAAA,UACN,SAAS;AAAA;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF,GAMMC,IAAuC,CAAC,GCXxCC,IAASC,EAAK,MAAM,OAAO,mCAA0B,CAAC,GAEtDC,IAAe,CAAC;AAAA,EACpB,gBAAAC;AAAA,EACA,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,UAAAC;AACF,MAOIC,gBAAAA,EAAA;AAAA,EAACP;AAAA,EAAA;AAAA,IACC,QAAAI;AAAA,IACA,SAAAC;AAAA,IACA,cAAc,EAAE,GAAGP,GAAc,GAAGQ,EAAS;AAAA,IAC7C,gBAAAP;AAAA,IACA,gBAAgB;AAAA,MACd,gBAAgBI,KAAkB;AAAA,IACpC;AAAA,EAAA;AAAA,GAKOK,IAAqB,CAChCF,OAEO;AAAA,EACL,cAAc,CAAC;AAAA,IACb,QAAAF;AAAA,IACA,SAAAC;AAAA,EAAA,4BAMGI,GACC,EAAA,UAAAF,gBAAAA,EAAAA,IAACL,KAAa,QAAAE,GAAgB,SAAAC,GAAkB,UAAAC,EAAoB,CAAA,EACtE,CAAA;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.0.0-f3858d6",
3
+ "version": "0.0.0-f417aae",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -20,10 +20,8 @@ if (root.childElementCount > 0) {
20
20
  }
21
21
 
22
22
  async function hydrateLazyRoutes(routes: RouteObject[]) {
23
- const path = window.location.pathname;
24
- const lazyMatches = matchRoutes(routes, path, config.basePath)?.filter(
25
- (m) => m.route.lazy,
26
- );
23
+ const path = window.location.pathname.slice(config.basePath?.length ?? 0);
24
+ const lazyMatches = matchRoutes(routes, path)?.filter((m) => m.route.lazy);
27
25
 
28
26
  if (lazyMatches?.length) {
29
27
  await Promise.all(
@@ -96,10 +96,6 @@ export const render = async ({
96
96
 
97
97
  const [htmlStart, htmlEnd] = template.split("<!--app-html-->");
98
98
 
99
- if (!htmlStart) {
100
- throw new Error("No <!--app-html--> found in template");
101
- }
102
-
103
99
  response.write(
104
100
  htmlStart.replace(
105
101
  "<!--app-helmet-->",
package/src/app/main.css CHANGED
@@ -103,10 +103,6 @@
103
103
  @apply h-full overscroll-none;
104
104
  }
105
105
 
106
- details summary {
107
- @apply cursor-pointer;
108
- }
109
-
110
106
  #root {
111
107
  @apply min-h-screen w-full;
112
108
  }
@@ -146,7 +142,6 @@
146
142
  .CollapsibleContent {
147
143
  --easing: cubic-bezier(0.4, 0, 0.2, 1);
148
144
  --slide-offset: -0.75rem;
149
- @apply overflow-hidden;
150
145
  }
151
146
  .CollapsibleContent[data-state="open"] {
152
147
  animation: slideDown 300ms var(--easing);
package/src/app/main.tsx CHANGED
@@ -1,17 +1,19 @@
1
- import { type RouteObject } from "react-router-dom";
1
+ import { redirect, type RouteObject } from "react-router-dom";
2
2
  import { configuredApiKeysPlugin } from "virtual:zudoku-api-keys-plugin";
3
3
  import { configuredApiPlugins } from "virtual:zudoku-api-plugins";
4
4
  import { configuredAuthProvider } from "virtual:zudoku-auth";
5
5
  import { configuredCustomPagesPlugin } from "virtual:zudoku-custom-pages-plugin";
6
6
  import { configuredDocsPlugins } from "virtual:zudoku-docs-plugins";
7
7
  import { configuredRedirectPlugin } from "virtual:zudoku-redirect-plugin";
8
+ import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
8
9
  import { configuredSidebar } from "virtual:zudoku-sidebar";
9
10
  import "virtual:zudoku-theme.css";
10
11
  import { DevPortal, Layout, RouterError } from "zudoku/components";
11
12
  import { isNavigationPlugin } from "zudoku/internal";
12
- import { inkeepSearchPlugin } from "zudoku/plugins/search-inkeep";
13
13
  import type { ZudokuConfig } from "../config/config.js";
14
+ import { traverseSidebar } from "../lib/components/navigation/utils.js";
14
15
  import type { ZudokuContextOptions } from "../lib/core/DevPortalContext.js";
16
+ import { joinPath } from "../lib/util/joinPath.js";
15
17
 
16
18
  export const convertZudokuConfigToOptions = (
17
19
  config: ZudokuConfig,
@@ -51,11 +53,9 @@ export const convertZudokuConfigToOptions = (
51
53
  mdx: config.mdx,
52
54
  authentication: configuredAuthProvider,
53
55
  plugins: [
54
- ...(config.search?.type === "inkeep"
55
- ? [inkeepSearchPlugin(config.search)]
56
- : []),
57
56
  ...configuredDocsPlugins,
58
57
  ...configuredApiPlugins,
58
+ ...(configuredSearchPlugin ? [configuredSearchPlugin] : []),
59
59
  ...(configuredRedirectPlugin ? [configuredRedirectPlugin] : []),
60
60
  ...(configuredApiKeysPlugin ? [configuredApiKeysPlugin] : []),
61
61
  ...(configuredCustomPagesPlugin ? [configuredCustomPagesPlugin] : []),
@@ -67,7 +67,9 @@ export const convertZudokuConfigToOptions = (
67
67
  };
68
68
  };
69
69
 
70
- export const getRoutesByOptions = (options: ZudokuContextOptions) => {
70
+ export const getRoutesByConfig = (config: ZudokuConfig): RouteObject[] => {
71
+ const options = convertZudokuConfigToOptions(config);
72
+
71
73
  const allPlugins = [
72
74
  ...(options.plugins ? options.plugins : []),
73
75
  ...(options.authentication?.getAuthenticationPlugin
@@ -75,8 +77,27 @@ export const getRoutesByOptions = (options: ZudokuContextOptions) => {
75
77
  : []),
76
78
  ];
77
79
 
80
+ const topNavRedirects =
81
+ options.topNavigation?.flatMap((topNavItem) => {
82
+ if (!options.sidebars?.[topNavItem.id]) return [];
83
+
84
+ const first =
85
+ topNavItem.default ??
86
+ traverseSidebar(options.sidebars[topNavItem.id], (item) => {
87
+ if (item.type === "doc") return joinPath(topNavItem.id, item.id);
88
+ });
89
+
90
+ if (!first) return [];
91
+
92
+ return {
93
+ path: topNavItem.id,
94
+ loader: () => redirect(joinPath(first)),
95
+ } satisfies RouteObject;
96
+ }) ?? [];
97
+
78
98
  const routes = allPlugins
79
99
  .flatMap((plugin) => (isNavigationPlugin(plugin) ? plugin.getRoutes() : []))
100
+ .concat(topNavRedirects)
80
101
  .concat({
81
102
  path: "*",
82
103
  loader: () => {
@@ -84,13 +105,6 @@ export const getRoutesByOptions = (options: ZudokuContextOptions) => {
84
105
  },
85
106
  });
86
107
 
87
- return routes;
88
- };
89
-
90
- export const getRoutesByConfig = (config: ZudokuConfig): RouteObject[] => {
91
- const options = convertZudokuConfigToOptions(config);
92
- const routes = getRoutesByOptions(options);
93
-
94
108
  return [
95
109
  {
96
110
  element: (
@@ -16,7 +16,7 @@ if (!root) {
16
16
  themeToggle();
17
17
 
18
18
  const apiUrl = root.getAttribute("data-api-url");
19
- const pageTitle = document.getElementsByTagName("title")[0]!.innerText;
19
+ const pageTitle = document.getElementsByTagName("title")[0].innerText;
20
20
  const logoUrl = root.getAttribute("data-logo-url");
21
21
 
22
22
  // IMPORTANT: This component must not contain tailwind classes
@@ -1,59 +1,28 @@
1
- import logger from "loglevel";
2
- import { useEffect, useRef, useState } from "react";
3
- import { useNavigate } from "react-router-dom";
4
- import { DeveloperHint } from "../../components/DeveloperHint.js";
5
- import { ErrorPage } from "../../components/ErrorPage.js";
6
- import { Spinner } from "../../components/Spinner.js";
7
- import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
1
+ import { useSuspenseQuery } from "@tanstack/react-query";
2
+ import { Navigate } from "react-router-dom";
3
+ import { ZudokuError } from "../../util/invariant.js";
8
4
 
9
5
  export function CallbackHandler({
10
6
  handleCallback,
11
7
  }: {
12
8
  handleCallback: () => Promise<string>;
13
9
  }) {
14
- const [error, setError] = useState<Error | null>(null);
15
- const navigate = useNavigate();
16
- // Deal with double mount in dev mode which will break
17
- // the OAuth flow because you can only use the code once
18
- const didInitialize = useRef(false);
10
+ const executeCallback = useSuspenseQuery({
11
+ retry: false,
12
+ queryKey: ["oauth-callback"],
13
+ queryFn: async () => {
14
+ try {
15
+ return await handleCallback();
16
+ } catch (error) {
17
+ throw new ZudokuError("Could not validate user", {
18
+ cause: error,
19
+ title: "Authentication Error",
20
+ developerHint:
21
+ "Check the configuration of your authorization provider and ensure all settings such as the callback URL are configured correctly.",
22
+ });
23
+ }
24
+ },
25
+ });
19
26
 
20
- useEffect(() => {
21
- if (didInitialize.current) {
22
- return;
23
- }
24
- didInitialize.current = true;
25
- handleCallback()
26
- .then((redirect) => {
27
- navigate(redirect);
28
- })
29
- .catch((err) => {
30
- logger.error(err);
31
- setError(err);
32
- });
33
- }, [navigate, handleCallback]);
34
-
35
- if (error) {
36
- return (
37
- <ErrorPage
38
- category="Error"
39
- title="Authentication Error"
40
- message={
41
- <>
42
- <DeveloperHint className="mb-4">
43
- Check the configuration of your authorization provider and ensure
44
- all settings such as the callback URL are configured correctly.
45
- </DeveloperHint>
46
- An error occurred while authorizing the user.
47
- <SyntaxHighlight code={error.toString()} language="plain" />
48
- </>
49
- }
50
- />
51
- );
52
- }
53
-
54
- return (
55
- <div className="grid h-full place-items-center">
56
- <Spinner />
57
- </div>
58
- );
27
+ return <Navigate to={executeCallback.data} />;
59
28
  }
@@ -10,7 +10,7 @@ export const useAuth = () => {
10
10
  isAuthEnabled,
11
11
  isPending: authState.isPending,
12
12
  profile: authState.profile,
13
- isAuthenticated: authState.profile,
13
+ isAuthenticated: Boolean(authState.profile),
14
14
 
15
15
  login: async () => {
16
16
  if (!isAuthEnabled) {
@@ -70,22 +70,14 @@ export const Header = memo(function HeaderInner() {
70
70
  {page?.logo && (
71
71
  <>
72
72
  <img
73
- src={
74
- /https?:\/\//.test(page.logo.src.light)
75
- ? page.logo.src.light
76
- : import.meta.env.BASE_URL + page.logo.src.light
77
- }
73
+ src={page.logo.src.light}
78
74
  alt={page.logo.alt ?? page.pageTitle}
79
75
  style={{ width: page.logo.width }}
80
76
  className={cn("h-10", isDark && "hidden")}
81
77
  loading="lazy"
82
78
  />
83
79
  <img
84
- src={
85
- /https?:\/\//.test(page.logo.src.dark)
86
- ? page.logo.src.dark
87
- : import.meta.env.BASE_URL + page.logo.src.dark
88
- }
80
+ src={page.logo.src.dark}
89
81
  alt={page.logo.alt ?? page.pageTitle}
90
82
  style={{ width: page.logo.width }}
91
83
  className={cn("h-10", !isDark && "hidden")}
@@ -99,8 +91,8 @@ export const Header = memo(function HeaderInner() {
99
91
  </div>
100
92
  </Link>
101
93
  </div>
102
- <div className="grid grid-cols-2 md:grid-cols-[--sidecar-grid-cols] items-center gap-8">
103
- <div className="w-full justify-center hidden md:flex ">
94
+ <div className="grid grid-cols-1 lg:grid-cols-[--sidecar-grid-cols] items-center gap-8">
95
+ <div className="w-full justify-center hidden lg:flex">
104
96
  <Search />
105
97
  </div>
106
98
 
@@ -2,6 +2,7 @@ import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
2
2
  import { cx } from "class-variance-authority";
3
3
  import { MenuIcon } from "lucide-react";
4
4
  import { NavLink } from "react-router-dom";
5
+ import { useAuth } from "../authentication/hook.js";
5
6
  import {
6
7
  Drawer,
7
8
  DrawerClose,
@@ -11,9 +12,12 @@ import {
11
12
  } from "../ui/Drawer.js";
12
13
  import { useZudoku } from "./context/ZudokuContext.js";
13
14
  import { Search } from "./Search.js";
15
+ import { isHiddenItem } from "./TopNavigation.js";
14
16
 
15
17
  export const MobileTopNavigation = () => {
16
18
  const { topNavigation } = useZudoku();
19
+ const { isAuthenticated } = useAuth();
20
+
17
21
  return (
18
22
  <Drawer direction="right">
19
23
  <div className="flex lg:hidden justify-self-end">
@@ -22,15 +26,17 @@ export const MobileTopNavigation = () => {
22
26
  </DrawerTrigger>
23
27
  </div>
24
28
  <DrawerContent
25
- className="lg:hidden h-screen right-0 left-auto w-[320px] rounded-none"
29
+ className="lg:hidden h-screen right-0 left-auto w-[320px] rounded-none overflow-auto"
26
30
  aria-describedby={undefined}
27
31
  >
28
32
  <VisuallyHidden>
29
33
  <DrawerTitle>Navigation</DrawerTitle>
30
34
  </VisuallyHidden>
31
- <Search />
35
+ <div className="flex p-4">
36
+ <Search />
37
+ </div>
32
38
  <ul className="flex flex-col items-center gap-4 p-4">
33
- {topNavigation.map((item) => (
39
+ {topNavigation.filter(isHiddenItem(isAuthenticated)).map((item) => (
34
40
  <li key={item.label}>
35
41
  <NavLink
36
42
  className={({ isActive }) =>
@@ -39,7 +39,7 @@ export const Search = () => {
39
39
  <button
40
40
  type="button"
41
41
  onClick={() => setIsOpen(true)}
42
- className="flex items-center border border-input hover:bg-accent hover:text-accent-foreground p-4 relative h-8 justify-start rounded-lg bg-background text-sm text-muted-foreground shadow-none w-40 sm:w-72"
42
+ className="flex items-center border border-input hover:bg-accent hover:text-accent-foreground p-4 relative h-8 justify-start rounded-lg bg-background text-sm text-muted-foreground shadow-none w-full sm:w-72"
43
43
  >
44
44
  <div className="flex items-center gap-2 flex-grow">
45
45
  <SearchIcon size={14} />
@@ -5,10 +5,16 @@ import React, {
5
5
  useContext,
6
6
  } from "react";
7
7
  import { isValidElementType } from "react-is";
8
- import { useLocation } from "react-router-dom";
8
+ import {
9
+ type Location,
10
+ type NavigateFunction,
11
+ type SetURLSearchParams,
12
+ } from "react-router-dom";
13
+ import { useExposedProps } from "../util/useExposedProps.js";
14
+
9
15
  export type Slotlets = Record<
10
16
  string,
11
- ReactNode | ReactElement | ComponentType<SlotletComponentProps>
17
+ ReactNode | ReactElement | ComponentType<ExposedComponentProps>
12
18
  >;
13
19
 
14
20
  const SlotletContext = React.createContext<Slotlets | undefined>({});
@@ -27,19 +33,20 @@ export const SlotletProvider = ({
27
33
  );
28
34
  };
29
35
 
30
- export type SlotletComponentProps = {
36
+ export type ExposedComponentProps = {
31
37
  location: Location;
38
+ navigate: NavigateFunction;
39
+ searchParams: URLSearchParams;
40
+ setSearchParams: SetURLSearchParams;
32
41
  };
33
42
 
34
43
  export const Slotlet = ({ name }: { name: string }) => {
35
44
  const context = useContext(SlotletContext);
36
45
  const componentOrElement = context?.[name];
37
- const location = useLocation();
46
+ const slotletProps = useExposedProps();
38
47
 
39
48
  if (isValidElementType(componentOrElement)) {
40
- return React.createElement(componentOrElement, {
41
- location,
42
- });
49
+ return React.createElement(componentOrElement, slotletProps);
43
50
  }
44
51
 
45
52
  return componentOrElement as ReactNode;
@@ -1,13 +1,23 @@
1
1
  import { cx } from "class-variance-authority";
2
- import { Suspense } from "react";
3
- import { Link } from "react-router-dom";
4
- import { TopNavigationItem } from "../../config/validators/validate.js";
5
- import { joinPath } from "../util/joinPath.js";
6
- import { useCurrentNavigation, useZudoku } from "./context/ZudokuContext.js";
7
- import { traverseSidebar } from "./navigation/utils.js";
2
+ import { NavLink } from "react-router-dom";
3
+
4
+ import { useAuth } from "../authentication/hook.js";
5
+ import { useZudoku } from "./context/ZudokuContext.js";
6
+
7
+ export const isHiddenItem =
8
+ (isAuthenticated?: boolean) =>
9
+ (item: { display?: "auth" | "anon" | "always" }) => {
10
+ return (
11
+ (item.display === "auth" && isAuthenticated) ||
12
+ (item.display === "anon" && !isAuthenticated) ||
13
+ !item.display ||
14
+ item.display === "always"
15
+ );
16
+ };
8
17
 
9
18
  export const TopNavigation = () => {
10
19
  const { topNavigation } = useZudoku();
20
+ const { isAuthenticated } = useAuth();
11
21
 
12
22
  // Hide top nav if there is only one item
13
23
  if (topNavigation.length <= 1) {
@@ -15,57 +25,26 @@ export const TopNavigation = () => {
15
25
  }
16
26
 
17
27
  return (
18
- <Suspense>
19
- <nav className="hidden lg:block border-b text-sm px-12 h-[--top-nav-height]">
20
- <ul className="flex flex-row items-center gap-8">
21
- {topNavigation.map((item) => (
22
- <li key={item.id}>
23
- <TopNavItem {...item} />
24
- </li>
25
- ))}
26
- </ul>
27
- </nav>
28
- </Suspense>
29
- );
30
- };
31
-
32
- const TopNavItem = ({ id, label, default: defaultLink }: TopNavigationItem) => {
33
- const { sidebars } = useZudoku();
34
- const nav = useCurrentNavigation();
35
- const currentSidebar = sidebars[id];
36
-
37
- // TODO: This is a bit of a hack to get the first link in the sidebar
38
- // We should really process this when we load the config so we can validate
39
- // that the sidebar is actually set. In this case we just fall back to linking
40
- // to the id if we can't resolve a sidebar.
41
- const first =
42
- defaultLink ??
43
- (currentSidebar
44
- ? traverseSidebar(currentSidebar, (item) => {
45
- if (item.type === "doc") return joinPath(item.id);
46
- })
47
- : joinPath(id));
48
-
49
- if (!first) {
50
- throw new Error(
51
- `No links found in top navigation for top navigation '${id}'. Check that the sidebar isn't empty or that a default link set.`,
52
- );
53
- }
54
-
55
- // Manually set the active sidebar based on our logic of what is active
56
- const isActive = nav.data.topNavItem?.id === id;
57
-
58
- return (
59
- <Link
60
- className={cx(
61
- "block py-3.5 font-medium -mb-px border-b-2",
62
- isActive
63
- ? "border-primary text-foreground"
64
- : "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
65
- )}
66
- to={first}
67
- >
68
- {label}
69
- </Link>
28
+ <nav className="hidden lg:block border-b text-sm px-12 h-[--top-nav-height]">
29
+ <ul className="flex flex-row items-center gap-8">
30
+ {topNavigation.filter(isHiddenItem(isAuthenticated)).map((item) => (
31
+ <li key={item.label}>
32
+ <NavLink
33
+ className={({ isActive }) =>
34
+ cx(
35
+ "block py-3.5 font-medium -mb-px border-b-2",
36
+ isActive
37
+ ? "border-primary text-foreground"
38
+ : "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
39
+ )
40
+ }
41
+ to={item.id}
42
+ >
43
+ {item.label}
44
+ </NavLink>
45
+ </li>
46
+ ))}
47
+ </ul>
48
+ </nav>
70
49
  );
71
50
  };