zudoku 0.47.2 → 0.48.1

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 (265) hide show
  1. package/dist/app/ZuploBuildConfig.d.ts +4 -114
  2. package/dist/app/ZuploBuildConfig.js +1 -1
  3. package/dist/app/ZuploBuildConfig.js.map +1 -1
  4. package/dist/app/demo.js +4 -3
  5. package/dist/app/demo.js.map +1 -1
  6. package/dist/app/main.js +5 -5
  7. package/dist/app/main.js.map +1 -1
  8. package/dist/app/standalone.js +4 -3
  9. package/dist/app/standalone.js.map +1 -1
  10. package/dist/config/loader.js +1 -2
  11. package/dist/config/loader.js.map +1 -1
  12. package/dist/config/validators/BuildSchema.d.ts +20 -44
  13. package/dist/config/validators/BuildSchema.js +3 -14
  14. package/dist/config/validators/BuildSchema.js.map +1 -1
  15. package/dist/config/validators/InputNavigationSchema.d.ts +7217 -0
  16. package/dist/config/validators/InputNavigationSchema.js +74 -0
  17. package/dist/config/validators/InputNavigationSchema.js.map +1 -0
  18. package/dist/config/validators/InputNavigationSchema.test-d.d.ts +1 -0
  19. package/dist/config/validators/InputNavigationSchema.test-d.js +146 -0
  20. package/dist/config/validators/InputNavigationSchema.test-d.js.map +1 -0
  21. package/dist/config/validators/NavigationSchema.d.ts +44 -0
  22. package/dist/config/validators/NavigationSchema.js +95 -0
  23. package/dist/config/validators/NavigationSchema.js.map +1 -0
  24. package/dist/config/validators/icon-types.d.ts +2 -1
  25. package/dist/config/validators/icon-types.js +1775 -1
  26. package/dist/config/validators/icon-types.js.map +1 -1
  27. package/dist/config/validators/validate.d.ts +442 -5780
  28. package/dist/config/validators/validate.js +37 -55
  29. package/dist/config/validators/validate.js.map +1 -1
  30. package/dist/config/validators/validate.test.js +0 -2
  31. package/dist/config/validators/validate.test.js.map +1 -1
  32. package/dist/flat-config.d.ts +320 -0
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js.map +1 -1
  35. package/dist/lib/components/BuildCheck.js +1 -1
  36. package/dist/lib/components/BuildCheck.js.map +1 -1
  37. package/dist/lib/components/Header.js +10 -9
  38. package/dist/lib/components/Header.js.map +1 -1
  39. package/dist/lib/components/Heading.d.ts +6 -2
  40. package/dist/lib/components/Heading.js +2 -2
  41. package/dist/lib/components/Heading.js.map +1 -1
  42. package/dist/lib/components/Main.js +4 -4
  43. package/dist/lib/components/Main.js.map +1 -1
  44. package/dist/lib/components/MobileTopNavigation.js +5 -5
  45. package/dist/lib/components/MobileTopNavigation.js.map +1 -1
  46. package/dist/lib/components/PageProgress.d.ts +1 -0
  47. package/dist/lib/components/PageProgress.js +20 -0
  48. package/dist/lib/components/PageProgress.js.map +1 -0
  49. package/dist/lib/components/TopNavigation.d.ts +2 -3
  50. package/dist/lib/components/TopNavigation.js +45 -44
  51. package/dist/lib/components/TopNavigation.js.map +1 -1
  52. package/dist/lib/components/context/ZudokuContext.d.ts +3 -7
  53. package/dist/lib/components/context/ZudokuContext.js +44 -26
  54. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  55. package/dist/lib/components/navigation/Navigation.d.ts +5 -0
  56. package/dist/lib/components/navigation/Navigation.js +12 -0
  57. package/dist/lib/components/navigation/Navigation.js.map +1 -0
  58. package/dist/lib/components/navigation/{SidebarBadge.d.ts → NavigationBadge.d.ts} +1 -1
  59. package/dist/lib/components/navigation/{SidebarBadge.js → NavigationBadge.js} +2 -2
  60. package/dist/lib/components/navigation/NavigationBadge.js.map +1 -0
  61. package/dist/lib/components/navigation/NavigationCategory.d.ts +5 -0
  62. package/dist/lib/components/navigation/{SidebarCategory.js → NavigationCategory.js} +14 -12
  63. package/dist/lib/components/navigation/NavigationCategory.js.map +1 -0
  64. package/dist/lib/components/navigation/NavigationItem.d.ts +6 -0
  65. package/dist/lib/components/navigation/NavigationItem.js +47 -0
  66. package/dist/lib/components/navigation/NavigationItem.js.map +1 -0
  67. package/dist/lib/components/navigation/{SidebarWrapper.d.ts → NavigationWrapper.d.ts} +1 -1
  68. package/dist/lib/components/navigation/{SidebarWrapper.js → NavigationWrapper.js} +3 -3
  69. package/dist/lib/components/navigation/NavigationWrapper.js.map +1 -0
  70. package/dist/lib/components/navigation/utils.d.ts +7 -9
  71. package/dist/lib/components/navigation/utils.js +17 -17
  72. package/dist/lib/components/navigation/utils.js.map +1 -1
  73. package/dist/lib/core/ZudokuContext.d.ts +6 -8
  74. package/dist/lib/core/ZudokuContext.js +5 -7
  75. package/dist/lib/core/ZudokuContext.js.map +1 -1
  76. package/dist/lib/core/plugins.d.ts +2 -2
  77. package/dist/lib/hooks/useEvent.test.js +5 -3
  78. package/dist/lib/hooks/useEvent.test.js.map +1 -1
  79. package/dist/lib/plugins/api-catalog/Catalog.d.ts +1 -1
  80. package/dist/lib/plugins/api-catalog/index.d.ts +6 -6
  81. package/dist/lib/plugins/api-catalog/index.js +10 -10
  82. package/dist/lib/plugins/api-catalog/index.js.map +1 -1
  83. package/dist/lib/plugins/api-keys/SettingsApiKeys.js +4 -4
  84. package/dist/lib/plugins/api-keys/SettingsApiKeys.js.map +1 -1
  85. package/dist/lib/plugins/custom-pages/index.d.ts +3 -2
  86. package/dist/lib/plugins/custom-pages/index.js +15 -8
  87. package/dist/lib/plugins/custom-pages/index.js.map +1 -1
  88. package/dist/lib/plugins/markdown/MdxPage.js +6 -3
  89. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  90. package/dist/lib/plugins/markdown/index.d.ts +2 -1
  91. package/dist/lib/plugins/markdown/index.js +9 -26
  92. package/dist/lib/plugins/markdown/index.js.map +1 -1
  93. package/dist/lib/plugins/openapi/OperationList.js +1 -1
  94. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  95. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  96. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  97. package/dist/lib/plugins/openapi/SchemaList.js +1 -1
  98. package/dist/lib/plugins/openapi/SchemaList.js.map +1 -1
  99. package/dist/lib/plugins/openapi/graphql/gql.d.ts +1 -1
  100. package/dist/lib/plugins/openapi/graphql/gql.js +1 -1
  101. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  102. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +3 -3
  103. package/dist/lib/plugins/openapi/graphql/graphql.js +2 -2
  104. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  105. package/dist/lib/plugins/openapi/index.d.ts +3 -3
  106. package/dist/lib/plugins/openapi/index.js +9 -9
  107. package/dist/lib/plugins/openapi/index.js.map +1 -1
  108. package/dist/lib/plugins/openapi/interfaces.d.ts +3 -3
  109. package/dist/lib/plugins/openapi/util/createNavigationCategory.d.ts +9 -0
  110. package/dist/lib/plugins/openapi/util/{createSidebarCategory.js → createNavigationCategory.js} +5 -4
  111. package/dist/lib/plugins/openapi/util/createNavigationCategory.js.map +1 -0
  112. package/dist/lib/plugins/openapi/util/getRoutes.js +2 -2
  113. package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
  114. package/dist/lib/plugins/openapi/util/methodColorMap.d.ts +1 -1
  115. package/dist/lib/util/useScrollToAnchor.js +3 -3
  116. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  117. package/dist/vite/api/SchemaManager.d.ts +4 -4
  118. package/dist/vite/api/SchemaManager.js +19 -19
  119. package/dist/vite/api/SchemaManager.js.map +1 -1
  120. package/dist/vite/api/SchemaManager.test.js +4 -4
  121. package/dist/vite/api/SchemaManager.test.js.map +1 -1
  122. package/dist/vite/config.js +1 -0
  123. package/dist/vite/config.js.map +1 -1
  124. package/dist/vite/plugin-api.js +8 -8
  125. package/dist/vite/plugin-api.js.map +1 -1
  126. package/dist/vite/plugin-config-reload.js +2 -0
  127. package/dist/vite/plugin-config-reload.js.map +1 -1
  128. package/dist/vite/plugin-custom-pages.js +2 -2
  129. package/dist/vite/plugin-custom-pages.js.map +1 -1
  130. package/dist/vite/plugin-docs.js +61 -59
  131. package/dist/vite/plugin-docs.js.map +1 -1
  132. package/dist/vite/{plugin-sidebar.d.ts → plugin-navigation.d.ts} +1 -1
  133. package/dist/vite/{plugin-sidebar.js → plugin-navigation.js} +20 -19
  134. package/dist/vite/plugin-navigation.js.map +1 -0
  135. package/dist/vite/plugin.js +2 -2
  136. package/dist/vite/plugin.js.map +1 -1
  137. package/dist/vite/shadcn-registry.d.ts +4 -4
  138. package/lib/{Markdown-C5j8kKSX.js → Markdown-DCAIYXF5.js} +480 -475
  139. package/lib/Markdown-DCAIYXF5.js.map +1 -0
  140. package/lib/{MdxPage-CKTMf1cR.js → MdxPage-Cf9YXWoC.js} +30 -30
  141. package/lib/MdxPage-Cf9YXWoC.js.map +1 -0
  142. package/lib/OasProvider-JMVTfG6_.js +35 -0
  143. package/lib/OasProvider-JMVTfG6_.js.map +1 -0
  144. package/lib/{OperationList-BJAKaG5p.js → OperationList-m4tFCI4S.js} +8 -8
  145. package/lib/{OperationList-BJAKaG5p.js.map → OperationList-m4tFCI4S.js.map} +1 -1
  146. package/lib/{RouteGuard-B7GVW4oL.js → RouteGuard-gV7nvzi7.js} +2 -2
  147. package/lib/{RouteGuard-B7GVW4oL.js.map → RouteGuard-gV7nvzi7.js.map} +1 -1
  148. package/lib/{SchemaList-Dgc0A8x5.js → SchemaList-_wRy4aQ0.js} +20 -20
  149. package/lib/SchemaList-_wRy4aQ0.js.map +1 -0
  150. package/lib/{SchemaView-DVx-jFN4.js → SchemaView-CRl_cQYH.js} +3 -3
  151. package/lib/{SchemaView-DVx-jFN4.js.map → SchemaView-CRl_cQYH.js.map} +1 -1
  152. package/lib/{SignUp-CRIKdWt9.js → SignUp-B6w5AwHM.js} +2 -2
  153. package/lib/{SignUp-CRIKdWt9.js.map → SignUp-B6w5AwHM.js.map} +1 -1
  154. package/lib/{Slot-ITby_hMb.js → Slot-BkYrj_uC.js} +4 -4
  155. package/lib/{Slot-ITby_hMb.js.map → Slot-BkYrj_uC.js.map} +1 -1
  156. package/lib/{SyntaxHighlight-CqKHkyEy.js → SyntaxHighlight-CH9OUJre.js} +2 -2
  157. package/lib/{SyntaxHighlight-CqKHkyEy.js.map → SyntaxHighlight-CH9OUJre.js.map} +1 -1
  158. package/lib/{Toc-Csq3UNtW.js → Toc-DRxqEsFc.js} +2 -2
  159. package/lib/{Toc-Csq3UNtW.js.map → Toc-DRxqEsFc.js.map} +1 -1
  160. package/lib/{circular-BP4OrHFK.js → circular-wJaV4vh_.js} +2 -2
  161. package/lib/{circular-BP4OrHFK.js.map → circular-wJaV4vh_.js.map} +1 -1
  162. package/lib/{createServer-zu4cDiPe.js → createServer-DN5AJLcN.js} +3 -3
  163. package/lib/{createServer-zu4cDiPe.js.map → createServer-DN5AJLcN.js.map} +1 -1
  164. package/lib/{errors-CPPSp5F4.js → errors-D_5vKvUq.js} +3 -3
  165. package/lib/{errors-CPPSp5F4.js.map → errors-D_5vKvUq.js.map} +1 -1
  166. package/lib/hook-CHXroBFt.js +1503 -0
  167. package/lib/hook-CHXroBFt.js.map +1 -0
  168. package/lib/{index-jWXxqkni.js → index-DJVaRmzI.js} +63 -62
  169. package/lib/index-DJVaRmzI.js.map +1 -0
  170. package/lib/{mutation-C1XCQTQL.js → mutation-BpcyTgWI.js} +2 -2
  171. package/lib/{mutation-C1XCQTQL.js.map → mutation-BpcyTgWI.js.map} +1 -1
  172. package/lib/ui/SyntaxHighlight.js +2 -2
  173. package/lib/{useMutation-BKvPttRn.js → useMutation-N4ockVKi.js} +3 -3
  174. package/lib/{useMutation-BKvPttRn.js.map → useMutation-N4ockVKi.js.map} +1 -1
  175. package/lib/zudoku.auth-auth0.js +1 -1
  176. package/lib/zudoku.auth-azureb2c.js +2 -2
  177. package/lib/zudoku.auth-clerk.js +2 -2
  178. package/lib/zudoku.auth-openid.js +2 -2
  179. package/lib/zudoku.components.js +2756 -3424
  180. package/lib/zudoku.components.js.map +1 -1
  181. package/lib/zudoku.hooks.js +2 -2
  182. package/lib/zudoku.plugin-api-catalog.js +44 -44
  183. package/lib/zudoku.plugin-api-catalog.js.map +1 -1
  184. package/lib/zudoku.plugin-api-keys.js +42 -39
  185. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  186. package/lib/zudoku.plugin-custom-pages.js +22 -16
  187. package/lib/zudoku.plugin-custom-pages.js.map +1 -1
  188. package/lib/zudoku.plugin-markdown.js +20 -69
  189. package/lib/zudoku.plugin-markdown.js.map +1 -1
  190. package/lib/zudoku.plugin-openapi.js +5 -5
  191. package/lib/zudoku.plugin-search-pagefind.js +2 -2
  192. package/lib/zudoku.plugins.js.map +1 -1
  193. package/package.json +12 -9
  194. package/src/app/ZuploBuildConfig.ts +1 -1
  195. package/src/app/demo.tsx +4 -3
  196. package/src/app/main.css +5 -0
  197. package/src/app/main.tsx +6 -5
  198. package/src/app/standalone.tsx +4 -3
  199. package/src/lib/components/BuildCheck.tsx +1 -1
  200. package/src/lib/components/Header.tsx +5 -3
  201. package/src/lib/components/Heading.tsx +7 -3
  202. package/src/lib/components/Main.tsx +8 -8
  203. package/src/lib/components/MobileTopNavigation.tsx +12 -11
  204. package/src/lib/components/PageProgress.tsx +28 -0
  205. package/src/lib/components/TopNavigation.tsx +57 -66
  206. package/src/lib/components/context/ZudokuContext.ts +50 -32
  207. package/src/lib/components/navigation/{Sidebar.tsx → Navigation.tsx} +18 -16
  208. package/src/lib/components/navigation/{SidebarBadge.tsx → NavigationBadge.tsx} +1 -1
  209. package/src/lib/components/navigation/{SidebarCategory.tsx → NavigationCategory.tsx} +16 -14
  210. package/src/lib/components/navigation/{SidebarItem.tsx → NavigationItem.tsx} +23 -17
  211. package/src/lib/components/navigation/{SidebarWrapper.tsx → NavigationWrapper.tsx} +2 -2
  212. package/src/lib/components/navigation/utils.ts +27 -28
  213. package/src/lib/core/ZudokuContext.ts +9 -15
  214. package/src/lib/core/plugins.ts +2 -2
  215. package/src/lib/hooks/useEvent.test.tsx +7 -5
  216. package/src/lib/plugins/api-catalog/Catalog.tsx +1 -1
  217. package/src/lib/plugins/api-catalog/index.tsx +19 -19
  218. package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +6 -5
  219. package/src/lib/plugins/custom-pages/index.tsx +20 -11
  220. package/src/lib/plugins/markdown/MdxPage.tsx +6 -3
  221. package/src/lib/plugins/markdown/index.tsx +19 -40
  222. package/src/lib/plugins/openapi/OperationList.tsx +1 -1
  223. package/src/lib/plugins/openapi/OperationListItem.tsx +1 -1
  224. package/src/lib/plugins/openapi/SchemaList.tsx +2 -2
  225. package/src/lib/plugins/openapi/graphql/gql.ts +5 -5
  226. package/src/lib/plugins/openapi/graphql/graphql.ts +6 -6
  227. package/src/lib/plugins/openapi/index.tsx +11 -11
  228. package/src/lib/plugins/openapi/interfaces.ts +3 -3
  229. package/src/lib/plugins/openapi/util/{createSidebarCategory.tsx → createNavigationCategory.tsx} +6 -5
  230. package/src/lib/plugins/openapi/util/getRoutes.tsx +2 -2
  231. package/src/lib/plugins/openapi/util/methodColorMap.tsx +1 -1
  232. package/src/lib/util/useScrollToAnchor.ts +3 -3
  233. package/dist/config/validators/InputSidebarSchema.d.ts +0 -220
  234. package/dist/config/validators/InputSidebarSchema.js +0 -63
  235. package/dist/config/validators/InputSidebarSchema.js.map +0 -1
  236. package/dist/config/validators/SidebarSchema.d.ts +0 -46
  237. package/dist/config/validators/SidebarSchema.js +0 -118
  238. package/dist/config/validators/SidebarSchema.js.map +0 -1
  239. package/dist/lib/components/navigation/Sidebar.d.ts +0 -5
  240. package/dist/lib/components/navigation/Sidebar.js +0 -10
  241. package/dist/lib/components/navigation/Sidebar.js.map +0 -1
  242. package/dist/lib/components/navigation/SidebarBadge.js.map +0 -1
  243. package/dist/lib/components/navigation/SidebarCategory.d.ts +0 -5
  244. package/dist/lib/components/navigation/SidebarCategory.js.map +0 -1
  245. package/dist/lib/components/navigation/SidebarItem.d.ts +0 -6
  246. package/dist/lib/components/navigation/SidebarItem.js +0 -44
  247. package/dist/lib/components/navigation/SidebarItem.js.map +0 -1
  248. package/dist/lib/components/navigation/SidebarWrapper.js.map +0 -1
  249. package/dist/lib/plugins/markdown/resolver.d.ts +0 -32
  250. package/dist/lib/plugins/markdown/resolver.js +0 -46
  251. package/dist/lib/plugins/markdown/resolver.js.map +0 -1
  252. package/dist/lib/plugins/openapi/util/createSidebarCategory.d.ts +0 -9
  253. package/dist/lib/plugins/openapi/util/createSidebarCategory.js.map +0 -1
  254. package/dist/vite/plugin-sidebar.js.map +0 -1
  255. package/lib/Markdown-C5j8kKSX.js.map +0 -1
  256. package/lib/MdxPage-CKTMf1cR.js.map +0 -1
  257. package/lib/OasProvider-_GzmsbMg.js +0 -33
  258. package/lib/OasProvider-_GzmsbMg.js.map +0 -1
  259. package/lib/SchemaList-Dgc0A8x5.js.map +0 -1
  260. package/lib/hook-7wZANGJP.js +0 -1483
  261. package/lib/hook-7wZANGJP.js.map +0 -1
  262. package/lib/index-jWXxqkni.js.map +0 -1
  263. package/lib/joinPath-B7kNnUX4.js +0 -8
  264. package/lib/joinPath-B7kNnUX4.js.map +0 -1
  265. package/src/lib/plugins/markdown/resolver.ts +0 -59
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.47.2",
3
+ "version": "0.48.1",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -192,6 +192,7 @@
192
192
  "fast-equals": "5.2.2",
193
193
  "framer-motion": "^12.12.2",
194
194
  "glob": "11.0.2",
195
+ "glob-parent": "6.0.2",
195
196
  "graphql": "16.11.0",
196
197
  "graphql-type-json": "0.3.2",
197
198
  "graphql-yoga": "5.13.5",
@@ -200,6 +201,7 @@
200
201
  "hast-util-to-string": "3.0.1",
201
202
  "html-url-attributes": "^3.0.1",
202
203
  "http-terminator": "3.2.0",
204
+ "json-schema-to-typescript-lite": "14.1.0",
203
205
  "loglevel": "1.9.2",
204
206
  "lru-cache": "11.0.2",
205
207
  "lucide-react": "0.488.0",
@@ -245,8 +247,8 @@
245
247
  "vite": "6.3.5",
246
248
  "yaml": "2.8.0",
247
249
  "yargs": "17.7.2",
248
- "zod": "3.25.51",
249
- "zod-validation-error": "3.4.1",
250
+ "zod": "3.25.56",
251
+ "zod-to-ts": "1.2.0",
250
252
  "zustand": "5.0.5"
251
253
  },
252
254
  "devDependencies": {
@@ -257,6 +259,7 @@
257
259
  "@testing-library/react": "16.3.0",
258
260
  "@types/estree": "1.0.7",
259
261
  "@types/express": "5.0.2",
262
+ "@types/glob-parent": "5.1.3",
260
263
  "@types/har-format": "1.2.16",
261
264
  "@types/hast": "^3.0.4",
262
265
  "@types/json-schema": "7.0.15",
@@ -275,18 +278,18 @@
275
278
  "react": "19.1.0",
276
279
  "react-dom": "19.1.0",
277
280
  "rollup-plugin-visualizer": "5.14.0",
281
+ "tsx": "4.19.4",
278
282
  "typescript": "5.8.3",
279
283
  "vitest": "3.2.3"
280
284
  },
281
285
  "peerDependencies": {
282
- "react": ">=19",
283
- "react-dom": ">=19",
284
286
  "@azure/msal-browser": "^4.13.0",
285
287
  "@clerk/clerk-js": "^5.63.1",
286
288
  "@sentry/react": "^9.12.0",
287
- "@supabase/supabase-js": "^2.49.4"
289
+ "@supabase/supabase-js": "^2.49.4",
290
+ "react": ">=19",
291
+ "react-dom": ">=19"
288
292
  },
289
- "optionalDependencies": {},
290
293
  "peerDependenciesMeta": {
291
294
  "@azure/msal-browser": {
292
295
  "optional": true
@@ -305,12 +308,12 @@
305
308
  "build": "tsc --project tsconfig.app.json",
306
309
  "build:dev": "esbuild './src/**/*.ts' --format=esm --platform=node --target=node22 --outdir=dist --splitting --log-level=warning",
307
310
  "build:vite": "vite build",
308
- "generate:types": "node scripts/generate-types.js",
311
+ "generate:types": "tsx scripts/generate-types.js && tsx scripts/generate-flat-config.js",
309
312
  "build:standalone:vite": "vite build --mode standalone --config vite.standalone.config.ts",
310
313
  "build:standalone:html": "cp ./src/app/standalone.html ./standalone/standalone.html && cp ./src/app/demo.html ./standalone/demo.html && cp ./src/app/demo-cdn.html ./standalone/index.html && cp standalone/zudoku.css standalone/style.css",
311
314
  "clean": "tsc --build --clean",
312
315
  "codegen": "graphql-codegen --config ./src/codegen.ts",
313
- "test": "vitest run"
316
+ "test": "vitest run --typecheck"
314
317
  },
315
318
  "module": "./dist/index.js",
316
319
  "types": "./dist/index.d.ts"
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v4";
2
2
 
3
3
  export const EntitlementsSchema = z.object({
4
4
  devPortalZuploBranding: z.boolean(),
package/src/app/demo.tsx CHANGED
@@ -36,9 +36,10 @@ const config = {
36
36
  message: <DemoAnnouncement />,
37
37
  },
38
38
  },
39
- topNavigation: [
39
+ navigation: [
40
40
  {
41
- id: "/",
41
+ type: "link",
42
+ to: "/",
42
43
  label: "API Reference",
43
44
  },
44
45
  ],
@@ -47,7 +48,7 @@ const config = {
47
48
  openApiPlugin({
48
49
  type: "url",
49
50
  input: apiUrl,
50
- navigationId: "/",
51
+ path: "/",
51
52
  }),
52
53
  ],
53
54
  } satisfies ZudokuConfig;
package/src/app/main.css CHANGED
@@ -63,6 +63,11 @@
63
63
  @apply h-full overscroll-none;
64
64
  }
65
65
 
66
+ ::view-transition-old(root),
67
+ ::view-transition-new(root) {
68
+ animation: none;
69
+ }
70
+
66
71
  details summary {
67
72
  @apply cursor-pointer;
68
73
  }
package/src/app/main.tsx CHANGED
@@ -6,11 +6,11 @@ import {
6
6
  } from "virtual:zudoku-api-plugins";
7
7
  import { configuredAuthProvider } from "virtual:zudoku-auth";
8
8
  import { configuredCustomPagesPlugin } from "virtual:zudoku-custom-pages-plugin";
9
- import { configuredDocsPlugins } from "virtual:zudoku-docs-plugins";
9
+ import { configuredDocsPlugin } from "virtual:zudoku-docs-plugin";
10
+ import { configuredNavigation } from "virtual:zudoku-navigation";
10
11
  import { configuredRedirectPlugin } from "virtual:zudoku-redirect-plugin";
11
12
  import { configuredSearchPlugin } from "virtual:zudoku-search-plugin";
12
13
  import { registerShiki } from "virtual:zudoku-shiki-register";
13
- import { configuredSidebar } from "virtual:zudoku-sidebar";
14
14
  import "virtual:zudoku-theme.css";
15
15
  import {
16
16
  BuildCheck,
@@ -49,12 +49,11 @@ export const convertZudokuConfigToOptions = (
49
49
  title: "%s - Zudoku",
50
50
  ...config.metadata,
51
51
  },
52
- sidebars: configuredSidebar,
53
- topNavigation: config.topNavigation,
52
+ navigation: configuredNavigation,
54
53
  mdx: config.mdx,
55
54
  plugins: [
56
55
  ...(configuredAuthProvider ? [configuredAuthProvider] : []),
57
- ...configuredDocsPlugins,
56
+ ...(configuredDocsPlugin ? [configuredDocsPlugin] : []),
58
57
  ...configuredApiPlugins,
59
58
  ...(configuredSearchPlugin ? [configuredSearchPlugin] : []),
60
59
  ...(configuredRedirectPlugin ? [configuredRedirectPlugin] : []),
@@ -100,6 +99,8 @@ export const getRoutesByOptions = (
100
99
  },
101
100
  ]);
102
101
 
102
+ // @TODO Detect conflicts in routes and log warning
103
+
103
104
  return routes;
104
105
  };
105
106
 
@@ -31,10 +31,11 @@ const config = {
31
31
  : undefined,
32
32
  pageTitle,
33
33
  },
34
- topNavigation: [
34
+ navigation: [
35
35
  {
36
- id: "/",
36
+ type: "link",
37
37
  label: "API Reference",
38
+ to: "/",
38
39
  },
39
40
  ],
40
41
  plugins: [
@@ -42,7 +43,7 @@ const config = {
42
43
  openApiPlugin({
43
44
  type: "url",
44
45
  input: apiUrl!,
45
- navigationId: "/",
46
+ path: "/",
46
47
  }),
47
48
  ],
48
49
  } satisfies ZudokuConfig;
@@ -1,6 +1,6 @@
1
1
  import { useQuery } from "@tanstack/react-query";
2
2
  import { CircleFadingArrowUpIcon, LoaderCircleIcon } from "lucide-react";
3
- import { z } from "zod";
3
+ import { z } from "zod/v4";
4
4
  import { Button } from "../ui/Button.js";
5
5
 
6
6
  const BuildStatusSchema = z.object({
@@ -25,6 +25,7 @@ import { Banner } from "./Banner.js";
25
25
  import { ClientOnly } from "./ClientOnly.js";
26
26
  import { useZudoku } from "./context/ZudokuContext.js";
27
27
  import { MobileTopNavigation } from "./MobileTopNavigation.js";
28
+ import { PageProgress } from "./PageProgress.js";
28
29
  import { Search } from "./Search.js";
29
30
  import { Slot } from "./Slot.js";
30
31
  import { ThemeSwitch } from "./ThemeSwitch.js";
@@ -82,8 +83,9 @@ export const Header = memo(function HeaderInner() {
82
83
  return (
83
84
  <header className="sticky lg:top-0 z-10 bg-background/80 backdrop-blur w-full">
84
85
  <Banner />
85
- <div className={borderBottom}>
86
- <div className="max-w-screen-2xl mx-auto flex relative items-center justify-between h-(--top-header-height) px-4 lg:px-8 border-transparent">
86
+ <div className={cn(borderBottom, "relative")}>
87
+ <PageProgress />
88
+ <div className="max-w-screen-2xl mx-auto flex items-center justify-between h-(--top-header-height) px-4 lg:px-8 border-transparent">
87
89
  <div className="flex">
88
90
  <Link to="/">
89
91
  <div className="flex items-center gap-3.5">
@@ -182,7 +184,7 @@ export const Header = memo(function HeaderInner() {
182
184
  </div>
183
185
  </div>
184
186
  <div className={cn("hidden lg:block", borderBottom)}>
185
- <div className="max-w-screen-2xl mx-auto border-transparent">
187
+ <div className="max-w-screen-2xl mx-auto border-transparent relative">
186
188
  <Slot.Target name="top-navigation-before" />
187
189
  <TopNavigation />
188
190
  <Slot.Target name="top-navigation-after" />
@@ -46,7 +46,11 @@ export type HeadingProps = HTMLAttributes<HTMLHeadingElement> &
46
46
  className?: string;
47
47
  id?: string;
48
48
  level?: 1 | 2 | 3 | 4 | 5 | 6;
49
- registerSidebarAnchor?: boolean;
49
+ /**
50
+ * This is to set labels as active when the heading is in the viewport.
51
+ * It's used in the navigation/toc to highlight the current section.
52
+ */
53
+ registerNavigationAnchor?: boolean;
50
54
  };
51
55
 
52
56
  export const Heading = ({
@@ -54,7 +58,7 @@ export const Heading = ({
54
58
  children,
55
59
  id,
56
60
  className,
57
- registerSidebarAnchor,
61
+ registerNavigationAnchor,
58
62
  }: HeadingProps) => {
59
63
  const Component = getComponent(level ?? 1);
60
64
  const { ref } = useRegisterAnchorElement();
@@ -65,7 +69,7 @@ export const Heading = ({
65
69
  className: cn(className, "flex items-center gap-[0.33em]"),
66
70
  level,
67
71
  })}
68
- ref={registerSidebarAnchor ? ref : undefined}
72
+ ref={registerNavigationAnchor ? ref : undefined}
69
73
  id={id}
70
74
  >
71
75
  {children}
@@ -4,13 +4,13 @@ import { useNavigation } from "react-router";
4
4
  import { Drawer, DrawerTrigger } from "zudoku/ui/Drawer.js";
5
5
  import { cn } from "../util/cn.js";
6
6
  import { useCurrentNavigation, useZudoku } from "./context/ZudokuContext.js";
7
- import { Sidebar } from "./navigation/Sidebar.js";
7
+ import { Navigation } from "./navigation/Navigation.js";
8
8
  import { Slot } from "./Slot.js";
9
9
 
10
10
  export const Main = ({ children }: PropsWithChildren) => {
11
11
  const [isDrawerOpen, setDrawerOpen] = useState(false);
12
- const { sidebar } = useCurrentNavigation();
13
- const hasSidebar = sidebar.length > 0;
12
+ const { navigation } = useCurrentNavigation();
13
+ const hasNavigation = navigation.length > 0;
14
14
  const isNavigating = useNavigation().state === "loading";
15
15
  const { options } = useZudoku();
16
16
 
@@ -20,13 +20,13 @@ export const Main = ({ children }: PropsWithChildren) => {
20
20
  open={isDrawerOpen}
21
21
  onOpenChange={(open) => setDrawerOpen(open)}
22
22
  >
23
- {hasSidebar && (
24
- <Sidebar
23
+ {hasNavigation && (
24
+ <Navigation
25
25
  onRequestClose={() => setDrawerOpen(false)}
26
- sidebar={sidebar}
26
+ navigation={navigation}
27
27
  />
28
28
  )}
29
- {hasSidebar && (
29
+ {hasNavigation && (
30
30
  <div className="lg:hidden -mx-4 px-4 py-2 sticky bg-background/80 backdrop-blur-xs z-10 top-0 start-0 end-0 border-b">
31
31
  <DrawerTrigger className="flex items-center gap-2 px-4">
32
32
  <PanelLeftIcon size={16} strokeWidth={1.5} />
@@ -38,7 +38,7 @@ export const Main = ({ children }: PropsWithChildren) => {
38
38
  data-pagefind-body
39
39
  className={cn(
40
40
  "px-4 lg:pe-8 lg:px-8",
41
- !hasSidebar && "col-span-full",
41
+ !hasNavigation && "col-span-full",
42
42
  isNavigating && "animate-pulse",
43
43
  )}
44
44
  >
@@ -11,16 +11,19 @@ import {
11
11
  import { useZudoku } from "./context/ZudokuContext.js";
12
12
  import { PoweredByZudoku } from "./navigation/PoweredByZudoku.js";
13
13
  import { isHiddenItem } from "./navigation/utils.js";
14
+ import { PageProgress } from "./PageProgress.js";
14
15
  import { Search } from "./Search.js";
15
16
  import { Slot } from "./Slot.js";
16
17
  import { ThemeSwitch } from "./ThemeSwitch.js";
17
- import { PageProgress, TopNavItem } from "./TopNavigation.js";
18
+ import { TopNavItem } from "./TopNavigation.js";
18
19
 
19
20
  export const MobileTopNavigation = () => {
20
- const { topNavigation, options } = useZudoku();
21
+ const { navigation, options } = useZudoku();
21
22
  const { isAuthenticated } = useAuth();
22
23
  const [drawerOpen, setDrawerOpen] = useState(false);
23
24
 
25
+ const filteredItems = navigation.filter(isHiddenItem(isAuthenticated));
26
+
24
27
  return (
25
28
  <Drawer
26
29
  direction={options.page?.dir === "rtl" ? "left" : "right"}
@@ -50,15 +53,13 @@ export const MobileTopNavigation = () => {
50
53
  <li>
51
54
  <ThemeSwitch />
52
55
  </li>
53
- {topNavigation
54
- .filter(isHiddenItem(isAuthenticated))
55
- .map((item) => (
56
- <li key={item.label}>
57
- <button type="button" onClick={() => setDrawerOpen(false)}>
58
- <TopNavItem {...item} />
59
- </button>
60
- </li>
61
- ))}
56
+ {filteredItems.map((item) => (
57
+ <li key={item.label}>
58
+ <button type="button" onClick={() => setDrawerOpen(false)}>
59
+ <TopNavItem {...item} />
60
+ </button>
61
+ </li>
62
+ ))}
62
63
  </ul>
63
64
  </div>
64
65
  {options.page?.showPoweredBy !== false && (
@@ -0,0 +1,28 @@
1
+ import { useNProgress } from "@tanem/react-nprogress";
2
+ import { useEffect, useState } from "react";
3
+ import { useNavigation } from "react-router";
4
+
5
+ export const PageProgress = () => {
6
+ const navigation = useNavigation();
7
+ const isNavigating = navigation.state === "loading";
8
+ // delay the animation to avoid flickering
9
+ const [isAnimating, setIsAnimating] = useState(false);
10
+
11
+ useEffect(() => {
12
+ const timer = setTimeout(() => setIsAnimating(isNavigating), 200);
13
+
14
+ return () => clearTimeout(timer);
15
+ }, [isNavigating]);
16
+
17
+ const { isFinished, progress } = useNProgress({ isAnimating });
18
+
19
+ return (
20
+ <div
21
+ className="absolute w-0 left-0 right-0 bottom-[-1px] h-[2px] bg-primary transition-all duration-300 ease-in-out"
22
+ style={{
23
+ opacity: isFinished ? 0 : 1,
24
+ width: isFinished ? 0 : `${progress * 100}%`,
25
+ }}
26
+ />
27
+ );
28
+ };
@@ -1,44 +1,19 @@
1
- import { useNProgress } from "@tanem/react-nprogress";
2
1
  import { cx } from "class-variance-authority";
3
- import { Suspense, useEffect, useState } from "react";
4
- import { NavLink, useNavigation } from "react-router";
5
- import type { TopNavigationItem } from "../../config/validators/validate.js";
2
+ import { deepEqual } from "fast-equals";
3
+ import { Suspense } from "react";
4
+ import { NavLink } from "react-router";
5
+ import { type NavigationItem } from "../../config/validators/NavigationSchema.js";
6
6
  import { useAuth } from "../authentication/hook.js";
7
7
  import { joinUrl } from "../util/joinUrl.js";
8
8
  import { useCurrentNavigation, useZudoku } from "./context/ZudokuContext.js";
9
- import { isHiddenItem, traverseSidebar } from "./navigation/utils.js";
9
+ import { isHiddenItem, traverseNavigationItem } from "./navigation/utils.js";
10
10
  import { Slot } from "./Slot.js";
11
11
 
12
- export const PageProgress = () => {
13
- const navigation = useNavigation();
14
- const isNavigating = navigation.state === "loading";
15
- // delay the animation to avoid flickering
16
- const [isAnimating, setIsAnimating] = useState(false);
17
-
18
- useEffect(() => {
19
- const timer = setTimeout(() => setIsAnimating(isNavigating), 100);
20
-
21
- return () => clearTimeout(timer);
22
- }, [isNavigating]);
23
-
24
- const { isFinished, progress } = useNProgress({ isAnimating });
25
-
26
- return (
27
- <div
28
- className="absolute w-0 left-0 right-0 bottom-[-1px] h-[2px] bg-primary transition-all duration-300 ease-in-out"
29
- style={{
30
- opacity: isFinished ? 0 : 1,
31
- width: isFinished ? 0 : `${progress * 100}%`,
32
- }}
33
- />
34
- );
35
- };
36
-
37
12
  export const TopNavigation = () => {
38
- const { topNavigation } = useZudoku();
13
+ const { navigation } = useZudoku();
39
14
  const { isAuthenticated } = useAuth();
40
15
 
41
- const filteredItems = topNavigation.filter(isHiddenItem(isAuthenticated));
16
+ const filteredItems = navigation.filter(isHiddenItem(isAuthenticated));
42
17
 
43
18
  if (filteredItems.length === 0 || import.meta.env.MODE === "standalone") {
44
19
  return <style>{`:root { --top-nav-height: 0px; }`}</style>;
@@ -50,7 +25,7 @@ export const TopNavigation = () => {
50
25
  <nav className="text-sm">
51
26
  <ul className="flex flex-row items-center gap-8">
52
27
  {filteredItems.map((item) => (
53
- <li key={item.id}>
28
+ <li key={item.label + item.type}>
54
29
  <TopNavItem {...item} />
55
30
  </li>
56
31
  ))}
@@ -58,50 +33,66 @@ export const TopNavigation = () => {
58
33
  </nav>
59
34
  <Slot.Target name="top-navigation-side" />
60
35
  </div>
61
- <PageProgress />
36
+ {/* <PageProgress /> */}
62
37
  </Suspense>
63
38
  );
64
39
  };
65
40
 
66
- export const TopNavItem = ({
67
- id,
68
- label,
69
- default: defaultLink,
70
- }: TopNavigationItem) => {
71
- const { sidebars } = useZudoku();
72
- const currentSidebar = sidebars[id];
41
+ const getPathForItem = (item: NavigationItem): string => {
42
+ switch (item.type) {
43
+ case "doc":
44
+ return joinUrl(item.path);
45
+ case "link":
46
+ return item.to;
47
+ case "category": {
48
+ if (item.link?.path) {
49
+ return joinUrl(item.link.path);
50
+ }
51
+
52
+ return (
53
+ traverseNavigationItem(item, (child) => {
54
+ if (child.type !== "category") {
55
+ return getPathForItem(child);
56
+ }
57
+ }) ?? ""
58
+ );
59
+ }
60
+ case "custom-page":
61
+ return item.path;
62
+ }
63
+ };
64
+
65
+ export const TopNavItem = (item: NavigationItem) => {
73
66
  const currentNav = useCurrentNavigation();
74
- const isNavigating = Boolean(useNavigation().location);
75
- const isActive = currentNav.topNavItem?.id === id && !isNavigating;
67
+ const isActiveTopNavItem = deepEqual(currentNav.topNavItem, item);
76
68
 
77
- // TODO: This is a bit of a hack to get the first link in the sidebar
78
- // We should really process this when we load the config so we can validate
79
- // that the sidebar is actually set. In this case we just fall back to linking
80
- // to the id if we can't resolve a sidebar.
81
- const first =
82
- defaultLink ??
83
- (currentSidebar
84
- ? traverseSidebar(currentSidebar, (item) => {
85
- if (item.type === "doc") return joinUrl(item.id);
86
- })
87
- : joinUrl(id)) ??
88
- joinUrl(id);
69
+ const path = getPathForItem(item);
89
70
 
90
71
  return (
91
- // We don't use isActive here because it has to be inside the sidebar,
92
- // the top nav id doesn't necessarily start with the sidebar id
72
+ // We don't use isActive here because it has to be inside the navigation,
73
+ // the top nav id doesn't necessarily start with the navigation id
93
74
  <NavLink
94
- className={({ isPending }) =>
95
- cx(
96
- "block lg:py-3.5 font-medium -mb-px",
75
+ viewTransition
76
+ to={path}
77
+ className={({ isActive: isActiveNavLink, isPending }) => {
78
+ const isActive = isActiveNavLink || isActiveTopNavItem;
79
+ return cx(
80
+ "flex items-center gap-2 lg:py-3.5 font-medium -mb-px transition duration-150 delay-75 relative",
97
81
  isActive || isPending
98
- ? "border-primary text-foreground"
99
- : "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
100
- )
101
- }
102
- to={first}
82
+ ? [
83
+ "text-foreground",
84
+ // underline with view transition animation
85
+ "after:content-[''] after:absolute after:bottom-0 after:left-0 after:right-0",
86
+ "after:h-0.5 after:bg-primary",
87
+ isActive && "after:[view-transition-name:top-nav-underline]",
88
+ isPending && "after:bg-primary/25",
89
+ ]
90
+ : "text-foreground/75 hover:text-foreground",
91
+ );
92
+ }}
103
93
  >
104
- {label}
94
+ {item.icon && <item.icon size={16} className="align-[-0.125em]" />}
95
+ {item.label}
105
96
  </NavLink>
106
97
  );
107
98
  };
@@ -1,11 +1,12 @@
1
1
  import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { createContext, useContext } from "react";
3
3
  import { matchPath, useLocation } from "react-router";
4
+ import { type NavigationItem } from "../../../config/validators/NavigationSchema.js";
4
5
  import { useAuth } from "../../authentication/hook.js";
5
6
  import type { ZudokuContext } from "../../core/ZudokuContext.js";
6
7
  import { joinUrl } from "../../util/joinUrl.js";
7
8
  import { CACHE_KEYS } from "../cache.js";
8
- import { traverseSidebar } from "../navigation/utils.js";
9
+ import { traverseNavigation } from "../navigation/utils.js";
9
10
 
10
11
  export const ZudokuReactContext = createContext<ZudokuContext | undefined>(
11
12
  undefined,
@@ -30,8 +31,22 @@ export const useApiIdentities = () => {
30
31
  });
31
32
  };
32
33
 
34
+ const getItemPath = (item: NavigationItem) => {
35
+ switch (item.type) {
36
+ case "doc":
37
+ return joinUrl(item.path);
38
+ case "category":
39
+ return item.link ? joinUrl(item.link.path) : undefined;
40
+ case "link":
41
+ return item.to;
42
+ case "custom-page":
43
+ return item.path;
44
+ default:
45
+ return undefined;
46
+ }
47
+ };
33
48
  export const useCurrentNavigation = () => {
34
- const { getPluginSidebar, sidebars, topNavigation, options } = useZudoku();
49
+ const { getPluginNavigation, navigation, options } = useZudoku();
35
50
  const location = useLocation();
36
51
  const auth = useAuth();
37
52
 
@@ -39,44 +54,47 @@ export const useCurrentNavigation = () => {
39
54
  matchPath(route, location.pathname),
40
55
  );
41
56
 
42
- let currentSidebarItem = Object.entries(sidebars).find(([, sidebar]) => {
43
- return traverseSidebar(sidebar, (item) => {
44
- const itemId =
45
- item.type === "doc"
46
- ? joinUrl(item.id)
47
- : item.type === "category" && item.link
48
- ? joinUrl(item.link.id)
49
- : undefined;
50
-
51
- if (itemId === location.pathname) {
52
- return item;
53
- }
54
- });
57
+ const navItem = traverseNavigation(navigation, (item, parentCategories) => {
58
+ if (getItemPath(item) === location.pathname) {
59
+ return parentCategories.at(0) ?? item;
60
+ }
55
61
  });
56
- const currentTopNavItem =
57
- topNavigation.find((t) => t.id === currentSidebarItem?.[0]) ??
58
- topNavigation.find((item) => matchPath(item.id, location.pathname));
59
-
60
- if (
61
- currentTopNavItem &&
62
- !currentSidebarItem &&
63
- currentTopNavItem.id in sidebars
64
- ) {
65
- currentSidebarItem = ["", sidebars[currentTopNavItem.id]!];
66
- }
67
62
 
68
63
  const { data } = useSuspenseQuery({
69
- queryFn: () => getPluginSidebar(location.pathname),
70
- queryKey: ["plugin-sidebar", location.pathname],
64
+ queryFn: () => getPluginNavigation(location.pathname),
65
+ queryKey: ["plugin-navigation", location.pathname],
71
66
  });
72
67
 
73
- const hideSidebar =
68
+ let topNavItem = navItem;
69
+ if (!navItem && data.length > 0) {
70
+ // Extract base paths from plugin navigation items
71
+ const pluginBasePaths = data.flatMap((item) => {
72
+ return getItemPath(item)?.split("?").at(0)?.split("#").at(0) ?? [];
73
+ });
74
+
75
+ // Find top-level nav item that matches any plugin base path
76
+ topNavItem = navigation
77
+ .flatMap((item) => {
78
+ const itemPath = getItemPath(item);
79
+ return itemPath ? [{ item, path: itemPath }] : [];
80
+ })
81
+ .sort((a, b) => b.path.length - a.path.length)
82
+ .find(({ path }) => {
83
+ return pluginBasePaths.some(
84
+ (basePath) =>
85
+ matchPath({ path, end: false }, basePath) ??
86
+ matchPath({ path: basePath, end: false }, path),
87
+ );
88
+ })?.item;
89
+ }
90
+
91
+ const hasNavigation =
74
92
  auth.isAuthEnabled && !auth.isAuthenticated && isProtectedRoute;
75
93
 
76
94
  return {
77
- sidebar: hideSidebar
95
+ navigation: hasNavigation
78
96
  ? []
79
- : [...(currentSidebarItem ? currentSidebarItem[1] : []), ...data],
80
- topNavItem: currentTopNavItem,
97
+ : [...(navItem?.type === "category" ? navItem.items : []), ...data],
98
+ topNavItem,
81
99
  };
82
100
  };