zudoku 0.38.0 → 0.39.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 (177) hide show
  1. package/dist/app/tailwind.js +14 -0
  2. package/dist/app/tailwind.js.map +1 -1
  3. package/dist/lib/components/Heading.d.ts +1 -1
  4. package/dist/lib/components/Layout.js +2 -9
  5. package/dist/lib/components/Layout.js.map +1 -1
  6. package/dist/lib/components/Main.js +3 -1
  7. package/dist/lib/components/Main.js.map +1 -1
  8. package/dist/lib/components/Pagination.d.ts +10 -0
  9. package/dist/lib/components/Pagination.js +10 -0
  10. package/dist/lib/components/Pagination.js.map +1 -0
  11. package/dist/lib/components/TopNavigation.js +18 -2
  12. package/dist/lib/components/TopNavigation.js.map +1 -1
  13. package/dist/lib/components/navigation/SidebarItem.d.ts +1 -0
  14. package/dist/lib/components/navigation/SidebarItem.js +7 -3
  15. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  16. package/dist/lib/{plugins/markdown → components/navigation}/Toc.js +5 -7
  17. package/dist/lib/components/navigation/Toc.js.map +1 -0
  18. package/dist/lib/oas/graphql/index.d.ts +2 -1
  19. package/dist/lib/oas/graphql/index.js +74 -14
  20. package/dist/lib/oas/graphql/index.js.map +1 -1
  21. package/dist/lib/oas/parser/dereference/index.js +2 -0
  22. package/dist/lib/oas/parser/dereference/index.js.map +1 -1
  23. package/dist/lib/oas/parser/index.d.ts +5 -3
  24. package/dist/lib/oas/parser/index.js +0 -22
  25. package/dist/lib/oas/parser/index.js.map +1 -1
  26. package/dist/lib/plugins/api-catalog/index.js +19 -17
  27. package/dist/lib/plugins/api-catalog/index.js.map +1 -1
  28. package/dist/lib/plugins/markdown/MdxPage.js +3 -3
  29. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  30. package/dist/lib/plugins/openapi/ColorizedParam.js +1 -1
  31. package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
  32. package/dist/lib/plugins/openapi/OperationList.d.ts +1 -1
  33. package/dist/lib/plugins/openapi/OperationList.js +28 -8
  34. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  35. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  36. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  37. package/dist/lib/plugins/openapi/ParameterListItem.js +2 -1
  38. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  39. package/dist/lib/plugins/openapi/SchemaList.d.ts +1 -0
  40. package/dist/lib/plugins/openapi/SchemaList.js +52 -0
  41. package/dist/lib/plugins/openapi/SchemaList.js.map +1 -0
  42. package/dist/lib/plugins/openapi/client/GraphQLClient.d.ts +1 -1
  43. package/dist/lib/plugins/openapi/client/GraphQLClient.js +1 -1
  44. package/dist/lib/plugins/openapi/client/GraphQLClient.js.map +1 -1
  45. package/dist/lib/plugins/openapi/client/useCreateQuery.d.ts +6 -2
  46. package/dist/lib/plugins/openapi/client/useCreateQuery.js +5 -5
  47. package/dist/lib/plugins/openapi/client/useCreateQuery.js.map +1 -1
  48. package/dist/lib/plugins/openapi/components/EnumValues.js +1 -1
  49. package/dist/lib/plugins/openapi/components/EnumValues.js.map +1 -1
  50. package/dist/lib/plugins/openapi/graphql/gql.d.ts +6 -2
  51. package/dist/lib/plugins/openapi/graphql/gql.js +3 -2
  52. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  53. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +67 -11
  54. package/dist/lib/plugins/openapi/graphql/graphql.js +34 -5
  55. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  56. package/dist/lib/plugins/openapi/index.js +12 -0
  57. package/dist/lib/plugins/openapi/index.js.map +1 -1
  58. package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.d.ts +1 -2
  59. package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.js +2 -2
  60. package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.js.map +1 -1
  61. package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.d.ts +0 -1
  62. package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.js +1 -1
  63. package/dist/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.js.map +1 -1
  64. package/dist/lib/plugins/openapi/schema/SchemaExampleAndDefault.d.ts +4 -0
  65. package/dist/lib/plugins/openapi/schema/SchemaExampleAndDefault.js +12 -0
  66. package/dist/lib/plugins/openapi/schema/SchemaExampleAndDefault.js.map +1 -0
  67. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.d.ts +2 -4
  68. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js +12 -9
  69. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js.map +1 -1
  70. package/dist/lib/plugins/openapi/schema/SchemaView.d.ts +1 -2
  71. package/dist/lib/plugins/openapi/schema/SchemaView.js +30 -52
  72. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
  73. package/dist/lib/plugins/openapi/schema/utils.d.ts +1 -0
  74. package/dist/lib/plugins/openapi/schema/utils.js +3 -1
  75. package/dist/lib/plugins/openapi/schema/utils.js.map +1 -1
  76. package/dist/lib/plugins/openapi/util/getRoutes.js +9 -3
  77. package/dist/lib/plugins/openapi/util/getRoutes.js.map +1 -1
  78. package/dist/lib/util/useOnScreen.d.ts +3 -2
  79. package/dist/lib/util/useOnScreen.js +3 -3
  80. package/dist/lib/util/useOnScreen.js.map +1 -1
  81. package/dist/vite/api/schema-codegen.js +2 -2
  82. package/dist/vite/api/schema-codegen.js.map +1 -1
  83. package/dist/vite/api/schema-codegen.test.js +5 -0
  84. package/dist/vite/api/schema-codegen.test.js.map +1 -1
  85. package/dist/vite/plugin-api.js +8 -7
  86. package/dist/vite/plugin-api.js.map +1 -1
  87. package/lib/{AuthenticationPlugin-Duy_R1TU.js → AuthenticationPlugin-foqdvvkf.js} +2 -2
  88. package/lib/{AuthenticationPlugin-Duy_R1TU.js.map → AuthenticationPlugin-foqdvvkf.js.map} +1 -1
  89. package/lib/{Callout-B2vsR09t.js → Callout-D5frCCJ0.js} +2 -2
  90. package/lib/{Callout-B2vsR09t.js.map → Callout-D5frCCJ0.js.map} +1 -1
  91. package/lib/{Dialog-sbgekbjb.js → Dialog-Dv6WG8RN.js} +5 -5
  92. package/lib/{Dialog-sbgekbjb.js.map → Dialog-Dv6WG8RN.js.map} +1 -1
  93. package/lib/{Markdown-DIZ8nBVC.js → Markdown-aF5FdsNi.js} +1339 -1335
  94. package/lib/{Markdown-DIZ8nBVC.js.map → Markdown-aF5FdsNi.js.map} +1 -1
  95. package/lib/MdxPage-ZW1StNhp.js +83 -0
  96. package/lib/MdxPage-ZW1StNhp.js.map +1 -0
  97. package/lib/{OasProvider-D1A10JeA.js → OasProvider-Cld9RAMQ.js} +3 -3
  98. package/lib/{OasProvider-D1A10JeA.js.map → OasProvider-Cld9RAMQ.js.map} +1 -1
  99. package/lib/OperationList-D-OfzJm6.js +5065 -0
  100. package/lib/OperationList-D-OfzJm6.js.map +1 -0
  101. package/lib/Pagination-CYB3nVYx.js +46 -0
  102. package/lib/Pagination-CYB3nVYx.js.map +1 -0
  103. package/lib/SchemaList-Ci1WxRh0.js +148 -0
  104. package/lib/SchemaList-Ci1WxRh0.js.map +1 -0
  105. package/lib/SchemaView-Brn-YxHY.js +345 -0
  106. package/lib/SchemaView-Brn-YxHY.js.map +1 -0
  107. package/lib/{Select-fAYcJ0OU.js → Select-DVFRKf1R.js} +3 -3
  108. package/lib/{Select-fAYcJ0OU.js.map → Select-DVFRKf1R.js.map} +1 -1
  109. package/lib/{SlotletProvider-BEwNY8q0.js → SlotletProvider-DXvc0aY6.js} +2 -2
  110. package/lib/{SlotletProvider-BEwNY8q0.js.map → SlotletProvider-DXvc0aY6.js.map} +1 -1
  111. package/lib/Toc-YBsgI72s.js +92 -0
  112. package/lib/Toc-YBsgI72s.js.map +1 -0
  113. package/lib/{createServer-DjgKDpGV.js → createServer-mMau3eV_.js} +1732 -1664
  114. package/lib/{createServer-DjgKDpGV.js.map → createServer-mMau3eV_.js.map} +1 -1
  115. package/lib/{hook-Cge6LiTK.js → hook-CqpVYDqN.js} +28 -28
  116. package/lib/{hook-Cge6LiTK.js.map → hook-CqpVYDqN.js.map} +1 -1
  117. package/lib/index-Bt7MKhZq.js +2514 -0
  118. package/lib/index-Bt7MKhZq.js.map +1 -0
  119. package/lib/{index-B0y3fTg-.js → index-CjPMxpOV.js} +771 -730
  120. package/lib/index-CjPMxpOV.js.map +1 -0
  121. package/lib/{mutation-EChriCeF.js → mutation-8LjrN7uz.js} +2 -2
  122. package/lib/{mutation-EChriCeF.js.map → mutation-8LjrN7uz.js.map} +1 -1
  123. package/lib/ui/Command.js +1 -1
  124. package/lib/zudoku.auth-auth0.js +1 -1
  125. package/lib/zudoku.auth-clerk.js +2 -2
  126. package/lib/zudoku.auth-openid.js +2 -2
  127. package/lib/zudoku.components.js +31 -1454
  128. package/lib/zudoku.components.js.map +1 -1
  129. package/lib/zudoku.hooks.js +1 -1
  130. package/lib/zudoku.plugin-api-catalog.js +80 -77
  131. package/lib/zudoku.plugin-api-catalog.js.map +1 -1
  132. package/lib/zudoku.plugin-api-keys.js +3 -3
  133. package/lib/zudoku.plugin-custom-pages.js +1 -1
  134. package/lib/zudoku.plugin-markdown.js +1 -1
  135. package/lib/zudoku.plugin-openapi.js +2 -2
  136. package/lib/zudoku.plugin-search-pagefind.js +3 -3
  137. package/package.json +4 -3
  138. package/src/app/tailwind.ts +14 -0
  139. package/src/lib/components/Layout.tsx +4 -16
  140. package/src/lib/components/Main.tsx +3 -1
  141. package/src/lib/components/Pagination.tsx +47 -0
  142. package/src/lib/components/TopNavigation.tsx +29 -2
  143. package/src/lib/components/navigation/SidebarItem.tsx +10 -4
  144. package/src/lib/{plugins/markdown → components/navigation}/Toc.tsx +5 -14
  145. package/src/lib/oas/graphql/index.ts +118 -45
  146. package/src/lib/oas/parser/dereference/index.ts +2 -0
  147. package/src/lib/oas/parser/index.ts +7 -29
  148. package/src/lib/plugins/api-catalog/index.tsx +40 -35
  149. package/src/lib/plugins/markdown/MdxPage.tsx +6 -36
  150. package/src/lib/plugins/openapi/ColorizedParam.tsx +1 -1
  151. package/src/lib/plugins/openapi/OperationList.tsx +36 -15
  152. package/src/lib/plugins/openapi/OperationListItem.tsx +7 -2
  153. package/src/lib/plugins/openapi/ParameterListItem.tsx +2 -0
  154. package/src/lib/plugins/openapi/SchemaList.tsx +151 -0
  155. package/src/lib/plugins/openapi/client/GraphQLClient.tsx +1 -1
  156. package/src/lib/plugins/openapi/client/useCreateQuery.ts +12 -5
  157. package/src/lib/plugins/openapi/components/EnumValues.tsx +1 -1
  158. package/src/lib/plugins/openapi/graphql/gql.ts +15 -6
  159. package/src/lib/plugins/openapi/graphql/graphql.ts +104 -15
  160. package/src/lib/plugins/openapi/index.tsx +13 -0
  161. package/src/lib/plugins/openapi/schema/LogicalGroup/LogicalGroup.tsx +1 -8
  162. package/src/lib/plugins/openapi/schema/LogicalGroup/LogicalGroupItem.tsx +1 -2
  163. package/src/lib/plugins/openapi/schema/SchemaExampleAndDefault.tsx +36 -0
  164. package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +20 -21
  165. package/src/lib/plugins/openapi/schema/SchemaView.tsx +69 -141
  166. package/src/lib/plugins/openapi/schema/utils.ts +7 -1
  167. package/src/lib/plugins/openapi/util/getRoutes.tsx +9 -6
  168. package/src/lib/util/useOnScreen.ts +6 -4
  169. package/dist/lib/plugins/markdown/Toc.js.map +0 -1
  170. package/lib/MdxPage-JEdbfW-f.js +0 -195
  171. package/lib/MdxPage-JEdbfW-f.js.map +0 -1
  172. package/lib/OperationList-yOmYzMIp.js +0 -5379
  173. package/lib/OperationList-yOmYzMIp.js.map +0 -1
  174. package/lib/index-B0y3fTg-.js.map +0 -1
  175. package/lib/index.esm-CltAN0Tf.js +0 -711
  176. package/lib/index.esm-CltAN0Tf.js.map +0 -1
  177. /package/dist/lib/{plugins/markdown → components/navigation}/Toc.d.ts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-api-catalog.js","sources":["../src/lib/plugins/api-catalog/Catalog.tsx","../src/lib/plugins/api-catalog/index.tsx"],"sourcesContent":["import { useSuspenseQuery } from \"@tanstack/react-query\";\nimport { Helmet } from \"@zudoku/react-helmet-async\";\nimport { useMatch } from \"react-router\";\nimport { Link } from \"zudoku/components\";\nimport { useAuthState } from \"../../authentication/state.js\";\nimport { Heading } from \"../../components/Heading.js\";\nimport { Markdown } from \"../../components/Markdown.js\";\nimport { joinUrl } from \"../../util/joinUrl.js\";\nimport { type ApiCatalogPluginOptions, getKey } from \"./index.js\";\n\nexport const Catalog = ({\n items,\n filterCatalogItems = (items) => items,\n label = \"API Library\",\n categoryLabel,\n}: Omit<ApiCatalogPluginOptions, \"navigationId\"> & {\n categoryLabel?: string;\n}) => {\n const auth = useAuthState();\n const match = useMatch({ path: \"/catalog/:category\" });\n const activeCategory = match?.params.category;\n\n const catalogItems = useSuspenseQuery({\n queryFn: () => filterCatalogItems(items, { auth }),\n queryKey: [\"catalogItems\", auth],\n });\n\n // Only index the overview page, ignore the rest\n const dataSet = activeCategory ? { \"data-pagefind-ignore\": \"all\" } : {};\n\n return (\n <section\n className=\"pt-[--padding-content-top] pb-[--padding-content-bottom]\"\n {...dataSet}\n >\n <Helmet>\n <title>\n {categoryLabel ? `${categoryLabel} - ` : \"\"}\n {label}\n </title>\n </Helmet>\n <div className=\"grid gap-4\">\n <Heading level={2}>\n {label}\n {categoryLabel && ` - ${categoryLabel}`}\n </Heading>\n\n <div className=\"grid grid-cols-2 gap-4\">\n {catalogItems.data\n .filter(\n (api) =>\n !activeCategory ||\n api.categories.find((c) =>\n c.tags.find((t) => getKey(c.label, t) === activeCategory),\n ),\n )\n .map((api) => (\n <Link\n to={joinUrl(api.path)}\n className=\"no-underline hover:!text-foreground\"\n key={api.path}\n >\n <div className=\"border h-full rounded-lg p-4 flex flex-col gap-2 cursor-pointer hover:bg-border/20 font-normal\">\n <span className=\"font-semibold\">{api.label}</span>\n <Markdown\n className=\"text-sm whitespace-pre-wrap mb-6 line-clamp-2\"\n content={api.description}\n />\n </div>\n </Link>\n ))}\n </div>\n </div>\n </section>\n );\n};\n","import slugify from \"@sindresorhus/slugify\";\nimport { matchPath } from \"react-router\";\nimport type { SidebarItem } from \"../../../config/validators/SidebarSchema.js\";\nimport type { AuthState } from \"../../authentication/state.js\";\nimport type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { joinUrl } from \"../../util/joinUrl.js\";\nimport { Catalog } from \"./Catalog.js\";\n\nexport const getKey = (category: string, tag: string) =>\n slugify(`${category}-${tag}`);\n\nexport type ApiCatalogItem = {\n path: string;\n label: string;\n description: string;\n categories: CatalogCategory[];\n};\n\nexport type CatalogCategory = {\n label: string;\n tags: string[];\n};\n\nexport type ApiCatalogPluginOptions = {\n navigationId: string;\n label: string;\n categories?: CatalogCategory[];\n items: ApiCatalogItem[];\n filterCatalogItems?: filterCatalogItems;\n};\n\nexport type CatalogContext<ProviderData = unknown> = {\n auth: AuthState<ProviderData>;\n};\n\nexport type filterCatalogItems<ProviderData = unknown> = (\n items: ApiCatalogItem[],\n { auth }: CatalogContext<ProviderData>,\n) => ApiCatalogItem[];\n\nexport const apiCatalogPlugin = ({\n navigationId,\n items,\n label,\n categories = [],\n filterCatalogItems,\n}: {\n navigationId: string;\n label: string;\n categories?: CatalogCategory[];\n items: ApiCatalogItem[];\n filterCatalogItems?: filterCatalogItems;\n}): ZudokuPlugin => {\n return {\n getSidebar: async function Sidebar(path) {\n if (!matchPath({ path: joinUrl(navigationId), end: false }, path)) {\n return [];\n }\n\n const sidebar: SidebarItem[] = categories.map((category) => ({\n type: \"category\" as const,\n label: category.label,\n collapsible: false,\n items: category.tags.map((tag) => {\n const tagPath = getKey(category.label, tag);\n return {\n type: \"doc\" as const,\n id: joinUrl(navigationId, tagPath),\n label: tag,\n badge: {\n label: String(\n items.filter((api) =>\n api.categories.find((c) => c.tags.includes(tag)),\n ).length,\n ),\n color: \"outline\" as const,\n },\n };\n }),\n }));\n\n sidebar.unshift({\n type: \"doc\" as const,\n id: joinUrl(navigationId),\n label: \"Overview\",\n badge: { label: String(items.length), color: \"outline\" as const },\n });\n\n return sidebar;\n },\n getRoutes: () =>\n categories.flatMap((category) =>\n [undefined, ...category.tags].map((tag) => ({\n path: joinUrl(\n navigationId,\n tag ? getKey(category.label, tag) : undefined,\n ),\n element: (\n <Catalog\n label={label}\n categoryLabel={tag}\n items={items}\n filterCatalogItems={filterCatalogItems}\n categories={categories}\n />\n ),\n })),\n ),\n };\n};\n"],"names":["Catalog","items","filterCatalogItems","label","categoryLabel","auth","useAuthState","match","useMatch","activeCategory","catalogItems","useSuspenseQuery","dataSet","jsxs","jsx","Helmet","Heading","api","c","t","getKey","Link","joinUrl","Markdown","category","tag","slugify","apiCatalogPlugin","navigationId","categories","path","matchPath","sidebar","tagPath"],"mappings":"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"}
1
+ {"version":3,"file":"zudoku.plugin-api-catalog.js","sources":["../src/lib/plugins/api-catalog/Catalog.tsx","../src/lib/plugins/api-catalog/index.tsx"],"sourcesContent":["import { useSuspenseQuery } from \"@tanstack/react-query\";\nimport { Helmet } from \"@zudoku/react-helmet-async\";\nimport { useMatch } from \"react-router\";\nimport { Link } from \"zudoku/components\";\nimport { useAuthState } from \"../../authentication/state.js\";\nimport { Heading } from \"../../components/Heading.js\";\nimport { Markdown } from \"../../components/Markdown.js\";\nimport { joinUrl } from \"../../util/joinUrl.js\";\nimport { type ApiCatalogPluginOptions, getKey } from \"./index.js\";\n\nexport const Catalog = ({\n items,\n filterCatalogItems = (items) => items,\n label = \"API Library\",\n categoryLabel,\n}: Omit<ApiCatalogPluginOptions, \"navigationId\"> & {\n categoryLabel?: string;\n}) => {\n const auth = useAuthState();\n const match = useMatch({ path: \"/catalog/:category\" });\n const activeCategory = match?.params.category;\n\n const catalogItems = useSuspenseQuery({\n queryFn: () => filterCatalogItems(items, { auth }),\n queryKey: [\"catalogItems\", auth],\n });\n\n // Only index the overview page, ignore the rest\n const dataSet = activeCategory ? { \"data-pagefind-ignore\": \"all\" } : {};\n\n return (\n <section\n className=\"pt-[--padding-content-top] pb-[--padding-content-bottom]\"\n {...dataSet}\n >\n <Helmet>\n <title>\n {categoryLabel ? `${categoryLabel} - ` : \"\"}\n {label}\n </title>\n </Helmet>\n <div className=\"grid gap-4\">\n <Heading level={2}>\n {label}\n {categoryLabel && ` - ${categoryLabel}`}\n </Heading>\n\n <div className=\"grid grid-cols-2 gap-4\">\n {catalogItems.data\n .filter(\n (api) =>\n !activeCategory ||\n api.categories.find((c) =>\n c.tags.find((t) => getKey(c.label, t) === activeCategory),\n ),\n )\n .map((api) => (\n <Link\n to={joinUrl(api.path)}\n className=\"no-underline hover:!text-foreground\"\n key={api.path}\n >\n <div className=\"border h-full rounded-lg p-4 flex flex-col gap-2 cursor-pointer hover:bg-border/20 font-normal\">\n <span className=\"font-semibold\">{api.label}</span>\n <Markdown\n className=\"text-sm whitespace-pre-wrap mb-6 line-clamp-2\"\n content={api.description}\n />\n </div>\n </Link>\n ))}\n </div>\n </div>\n </section>\n );\n};\n","import slugify from \"@sindresorhus/slugify\";\nimport { matchPath } from \"react-router\";\nimport type { SidebarItem } from \"../../../config/validators/SidebarSchema.js\";\nimport type { AuthState } from \"../../authentication/state.js\";\nimport type { ZudokuPlugin } from \"../../core/plugins.js\";\nimport { joinUrl } from \"../../util/joinUrl.js\";\nimport { Catalog } from \"./Catalog.js\";\n\nexport const getKey = (category: string, tag: string) =>\n slugify(`${category}-${tag}`);\n\nexport type ApiCatalogItem = {\n path: string;\n label: string;\n description: string;\n categories: CatalogCategory[];\n};\n\nexport type CatalogCategory = {\n label: string;\n tags: string[];\n};\n\nexport type ApiCatalogPluginOptions = {\n navigationId: string;\n label: string;\n categories?: CatalogCategory[];\n items: ApiCatalogItem[];\n filterCatalogItems?: filterCatalogItems;\n};\n\nexport type CatalogContext<ProviderData = unknown> = {\n auth: AuthState<ProviderData>;\n};\n\nexport type filterCatalogItems<ProviderData = unknown> = (\n items: ApiCatalogItem[],\n { auth }: CatalogContext<ProviderData>,\n) => ApiCatalogItem[];\n\nexport const apiCatalogPlugin = ({\n navigationId,\n items,\n label,\n categories = [],\n filterCatalogItems,\n}: {\n navigationId: string;\n label: string;\n categories?: CatalogCategory[];\n items: ApiCatalogItem[];\n filterCatalogItems?: filterCatalogItems;\n}): ZudokuPlugin => {\n const paths = Object.fromEntries(\n categories.flatMap((category) =>\n [undefined, ...category.tags].map((tag) => [\n joinUrl(navigationId, tag ? getKey(category.label, tag) : undefined),\n tag,\n ]),\n ),\n );\n\n return {\n getSidebar: async (currentPath) => {\n const matches = Object.keys(paths).some((path) =>\n matchPath(path, currentPath),\n );\n\n if (!matches) {\n return [];\n }\n\n const sidebar: SidebarItem[] = categories.map((category) => ({\n type: \"category\" as const,\n label: category.label,\n collapsible: false,\n items: category.tags.map((tag) => ({\n type: \"doc\" as const,\n id: joinUrl(navigationId, getKey(category.label, tag)),\n label: tag,\n badge: {\n label: String(\n items.filter((api) =>\n api.categories.find((c) => c.tags.includes(tag)),\n ).length,\n ),\n color: \"outline\" as const,\n },\n })),\n }));\n\n sidebar.unshift({\n type: \"doc\" as const,\n id: joinUrl(navigationId),\n label: \"Overview\",\n badge: { label: String(items.length), color: \"outline\" as const },\n });\n\n return sidebar;\n },\n getRoutes: () =>\n Object.entries(paths).map(([path, tag]) => ({\n path,\n element: (\n <Catalog\n label={label}\n categoryLabel={tag}\n items={items}\n filterCatalogItems={filterCatalogItems}\n categories={categories}\n />\n ),\n })),\n };\n};\n"],"names":["Catalog","items","filterCatalogItems","label","categoryLabel","auth","useAuthState","match","useMatch","activeCategory","catalogItems","useSuspenseQuery","dataSet","jsxs","jsx","Helmet","Heading","api","t","getKey","Link","joinUrl","Markdown","category","tag","slugify","apiCatalogPlugin","navigationId","categories","paths","currentPath","path","matchPath","sidebar","c"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { j as e } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import { RotateCwIcon as j, TrashIcon as v, EyeOffIcon as w, EyeIcon as K, CheckIcon as b, CopyIcon as k, FileKey2Icon as N } from "lucide-react";
3
- import { D as I, S as x, R as S } from "./SlotletProvider-BEwNY8q0.js";
3
+ import { D as I, S as x, R as S } from "./SlotletProvider-DXvc0aY6.js";
4
4
  import { i as c } from "./invariant-Caa8-XvF.js";
5
- import { a as h, g, l as A, b as C } from "./hook-Cge6LiTK.js";
6
- import { u as d, S as E, a as P, b as D, c as q, d as R, e as p } from "./Select-fAYcJ0OU.js";
5
+ import { e as h, g, l as A, d as C } from "./hook-CqpVYDqN.js";
6
+ import { u as d, S as E, a as P, b as D, c as q, d as R, e as p } from "./Select-DVFRKf1R.js";
7
7
  import { a as O } from "./index.esm--gIChbWs.js";
8
8
  import { a as z, L as u, O as F } from "./chunk-HA7DTUK3-C4gP41vD.js";
9
9
  import { Button as l } from "./ui/Button.js";
@@ -1,6 +1,6 @@
1
1
  import { j as o } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import a from "react";
3
- import { P as n } from "./Markdown-DIZ8nBVC.js";
3
+ import { P as n } from "./Markdown-aF5FdsNi.js";
4
4
  import { c } from "./cn-qaFjX9_3.js";
5
5
  import { u as p } from "./useExposedProps-B9qXJedG.js";
6
6
  const u = ({
@@ -53,7 +53,7 @@ const P = (e) => ({
53
53
  const u = {
54
54
  path: r,
55
55
  lazy: async () => {
56
- const { MdxPage: p } = await import("./MdxPage-JEdbfW-f.js"), { default: f, ...l } = await i();
56
+ const { MdxPage: p } = await import("./MdxPage-ZW1StNhp.js"), { default: f, ...l } = await i();
57
57
  return {
58
58
  element: /* @__PURE__ */ d.jsx(
59
59
  p,
@@ -1,9 +1,9 @@
1
1
  import "./jsx-runtime-CYK1ROHF.js";
2
2
  import "lucide-react";
3
3
  import "./chunk-HA7DTUK3-C4gP41vD.js";
4
- import "./hook-Cge6LiTK.js";
4
+ import "./hook-CqpVYDqN.js";
5
5
  import "./ui/Button.js";
6
- import { U as a, o as e } from "./index-B0y3fTg-.js";
6
+ import { U as a, o as e } from "./index-CjPMxpOV.js";
7
7
  export {
8
8
  a as UNTAGGED_PATH,
9
9
  e as openApiPlugin
@@ -1,11 +1,11 @@
1
1
  import { j as e } from "./jsx-runtime-CYK1ROHF.js";
2
2
  import { C as S } from "./ClientOnly-E7hGysn1.js";
3
3
  import { VisuallyHidden as k } from "@radix-ui/react-visually-hidden";
4
- import { u as C, a as v, m as y, o as N, d as L } from "./hook-Cge6LiTK.js";
4
+ import { u as C, e as v, m as y, o as N, f as L } from "./hook-CqpVYDqN.js";
5
5
  import { useRef as j, useCallback as _, useLayoutEffect as w, useState as R } from "react";
6
6
  import { B as T } from "./Button-Fp19CMUr.js";
7
- import { C as F, a as f, b as p, c as q, d as I, e as E, f as $ } from "./Callout-B2vsR09t.js";
8
- import { b as D } from "./Dialog-sbgekbjb.js";
7
+ import { C as F, a as f, b as p, c as q, d as I, e as E, f as $ } from "./Callout-D5frCCJ0.js";
8
+ import { b as D } from "./Dialog-Dv6WG8RN.js";
9
9
  import { BracketsIcon as P, FileTextIcon as B } from "lucide-react";
10
10
  import { a as U, L as x } from "./chunk-HA7DTUK3-C4gP41vD.js";
11
11
  const A = async ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "type": "module",
5
5
  "homepage": "https://zudoku.dev",
6
6
  "repository": {
@@ -162,13 +162,14 @@
162
162
  "@sindresorhus/slugify": "2.2.1",
163
163
  "@stefanprobst/rehype-extract-toc": "2.2.1",
164
164
  "@tailwindcss/typography": "0.5.16",
165
+ "@tanem/react-nprogress": "5.0.55",
165
166
  "@tanstack/react-query": "5.66.6",
166
167
  "@types/react": "19.0.10",
167
168
  "@types/react-dom": "19.0.4",
168
169
  "@vitejs/plugin-react": "4.3.4",
169
170
  "@zudoku/httpsnippet": "10.0.9",
170
171
  "@zudoku/react-helmet-async": "2.0.5",
171
- "autoprefixer": "10.4.20",
172
+ "autoprefixer": "10.4.21",
172
173
  "class-variance-authority": "0.7.1",
173
174
  "clsx": "2.1.1",
174
175
  "cmdk": "1.0.4",
@@ -244,7 +245,7 @@
244
245
  "@graphql-codegen/cli": "5.0.5",
245
246
  "@graphql-codegen/client-preset": "4.7.0",
246
247
  "@testing-library/react": "16.2.0",
247
- "@types/estree": "1.0.6",
248
+ "@types/estree": "1.0.7",
248
249
  "@types/express": "5.0.0",
249
250
  "@types/har-format": "1.2.16",
250
251
  "@types/hast": "^3.0.4",
@@ -28,6 +28,20 @@ const config = (zudokuConfig?: LoadedConfig): Omit<Config, "content"> => {
28
28
  content,
29
29
  theme: {
30
30
  extend: {
31
+ keyframes: {
32
+ "bounce-x-start": {
33
+ "0%, 100%": { transform: "translateX(0)" },
34
+ "50%": { transform: "translateX(-25%)" },
35
+ },
36
+ "bounce-x-end": {
37
+ "0%, 100%": { transform: "translateX(0)" },
38
+ "50%": { transform: "translateX(25%)" },
39
+ },
40
+ },
41
+ animation: {
42
+ "bounce-x-start": "bounce-x-start 1s infinite",
43
+ "bounce-x-end": "bounce-x-end 1s infinite",
44
+ },
31
45
  fontFamily: {
32
46
  sans,
33
47
  mono,
@@ -1,7 +1,6 @@
1
1
  import { Helmet } from "@zudoku/react-helmet-async";
2
2
  import { Suspense, useEffect, type ReactNode } from "react";
3
- import { Outlet, useLocation, useNavigation } from "react-router";
4
- import { useSpinDelay } from "spin-delay";
3
+ import { Outlet, useLocation } from "react-router";
5
4
  import { joinUrl } from "../util/joinUrl.js";
6
5
  import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
7
6
  import { useScrollToTop } from "../util/useScrollToTop.js";
@@ -29,13 +28,6 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
29
28
  authentication?.onPageLoad?.();
30
29
  }, [authentication]);
31
30
 
32
- // Page transition is happening: https://reactrouter.com/start/framework/pending-navigation
33
- const isNavigating = Boolean(useNavigation().location);
34
- const showSpinner = useSpinDelay(isNavigating, {
35
- delay: 300,
36
- minDuration: 500,
37
- });
38
-
39
31
  return (
40
32
  <>
41
33
  {import.meta.env.MODE === "standalone" && (
@@ -62,13 +54,9 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
62
54
  <Slotlet name="layout-after-head" />
63
55
 
64
56
  <div className="grid grid-cols-1 grid-rows-[min-content_1fr] lg:grid-cols-[var(--side-nav-width)_1fr] max-w-screen-2xl w-full lg:mx-auto px-4 lg:px-8 2xl:border-x">
65
- {showSpinner ? (
66
- <LoadingFallback />
67
- ) : (
68
- <Suspense fallback={<LoadingFallback />}>
69
- <Main>{children ?? <Outlet />}</Main>
70
- </Suspense>
71
- )}
57
+ <Suspense fallback={<LoadingFallback />}>
58
+ <Main>{children ?? <Outlet />}</Main>
59
+ </Suspense>
72
60
  </div>
73
61
  </>
74
62
  );
@@ -1,5 +1,6 @@
1
1
  import { PanelLeftIcon } from "lucide-react";
2
2
  import { type PropsWithChildren, useState } from "react";
3
+ import { useNavigation } from "react-router";
3
4
  import { Drawer, DrawerTrigger } from "zudoku/ui/Drawer.js";
4
5
  import { cn } from "../util/cn.js";
5
6
  import { useCurrentNavigation } from "./context/ZudokuContext.js";
@@ -10,6 +11,7 @@ export const Main = ({ children }: PropsWithChildren) => {
10
11
  const [isDrawerOpen, setDrawerOpen] = useState(false);
11
12
  const { sidebar } = useCurrentNavigation();
12
13
  const hasSidebar = sidebar.length > 0;
14
+ const isNavigating = useNavigation().state === "loading";
13
15
 
14
16
  return (
15
17
  <Drawer
@@ -34,8 +36,8 @@ export const Main = ({ children }: PropsWithChildren) => {
34
36
  <main
35
37
  data-pagefind-body
36
38
  className={cn(
37
- "h-auto dark:border-white/10 translate-x-0",
38
39
  hasSidebar ? "lg:pl-12" : "col-span-full",
40
+ isNavigating && "animate-pulse",
39
41
  )}
40
42
  >
41
43
  <Slotlet name="zudoku-before-content" />
@@ -0,0 +1,47 @@
1
+ import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
2
+ import { Link } from "react-router";
3
+ import { cn } from "../util/cn.js";
4
+ import { Button } from "./index.js";
5
+
6
+ export const Pagination = ({
7
+ prev,
8
+ next,
9
+ }: {
10
+ prev: { to: string; label: string } | undefined;
11
+ next: { to: string; label: string } | undefined;
12
+ }) => {
13
+ const linkClass =
14
+ "group transition-all p-5 space-x-1 transition-all hover:text-foreground";
15
+
16
+ return (
17
+ <div
18
+ className={cn(
19
+ "flex my-8 -mx-4 text-muted-foreground font-semibold",
20
+ prev ? "justify-between" : "justify-end",
21
+ )}
22
+ >
23
+ {prev && (
24
+ <Button variant="ghost" asChild>
25
+ <Link to={prev.to} relative="path" className={linkClass}>
26
+ <ArrowLeftIcon
27
+ className="group-hover:motion-safe:animate-bounce-x-start"
28
+ size={12}
29
+ />
30
+ <span className="truncate">{prev.label}</span>
31
+ </Link>
32
+ </Button>
33
+ )}
34
+ {next && (
35
+ <Button variant="ghost" asChild>
36
+ <Link to={next.to} relative="path" className={linkClass}>
37
+ <span className="truncate ">{next.label}</span>
38
+ <ArrowRightIcon
39
+ className="group-hover:motion-safe:animate-bounce-x-end"
40
+ size={12}
41
+ />
42
+ </Link>
43
+ </Button>
44
+ )}
45
+ </div>
46
+ );
47
+ };
@@ -1,5 +1,6 @@
1
+ import { useNProgress } from "@tanem/react-nprogress";
1
2
  import { cx } from "class-variance-authority";
2
- import { Suspense } from "react";
3
+ import { Suspense, useEffect, useState } from "react";
3
4
  import { NavLink, useNavigation } from "react-router";
4
5
  import type { TopNavigationItem } from "../../config/validators/common.js";
5
6
  import { useAuth } from "../authentication/hook.js";
@@ -20,6 +21,31 @@ export const isHiddenItem =
20
21
  );
21
22
  };
22
23
 
24
+ const Progress = () => {
25
+ const navigation = useNavigation();
26
+ const isNavigating = navigation.state === "loading";
27
+ // delay the animation to avoid flickering
28
+ const [isAnimating, setIsAnimating] = useState(false);
29
+
30
+ useEffect(() => {
31
+ const timer = setTimeout(() => setIsAnimating(isNavigating), 100);
32
+
33
+ return () => clearTimeout(timer);
34
+ }, [isNavigating]);
35
+
36
+ const { isFinished, progress } = useNProgress({ isAnimating });
37
+
38
+ return (
39
+ <div
40
+ className="absolute w-0 left-0 right-0 bottom-[-1px] h-[2px] bg-primary transition-all duration-300 ease-in-out"
41
+ style={{
42
+ opacity: isFinished ? 0 : 1,
43
+ width: isFinished ? 0 : `${progress * 100}%`,
44
+ }}
45
+ />
46
+ );
47
+ };
48
+
23
49
  export const TopNavigation = () => {
24
50
  const { topNavigation } = useZudoku();
25
51
  const { isAuthenticated } = useAuth();
@@ -32,7 +58,7 @@ export const TopNavigation = () => {
32
58
 
33
59
  return (
34
60
  <Suspense>
35
- <div className="items-center justify-between px-8 h-[--top-nav-height] hidden lg:flex text-sm">
61
+ <div className="items-center justify-between px-8 h-[--top-nav-height] hidden lg:flex text-sm relative">
36
62
  <nav className="text-sm">
37
63
  <ul className="flex flex-row items-center gap-8">
38
64
  {filteredItems.map((item) => (
@@ -43,6 +69,7 @@ export const TopNavigation = () => {
43
69
  </ul>
44
70
  </nav>
45
71
  <Slotlet name="top-navigation-side" />
72
+ <Progress />
46
73
  </div>
47
74
  </Suspense>
48
75
  );
@@ -3,14 +3,14 @@ import { ExternalLinkIcon } from "lucide-react";
3
3
  import { NavLink, useLocation, useSearchParams } from "react-router";
4
4
 
5
5
  import type { SidebarItem as SidebarItemType } from "../../../config/validators/SidebarSchema.js";
6
- import { joinPath } from "../../util/joinPath.js";
6
+ import { joinUrl } from "../../util/joinUrl.js";
7
7
  import { AnchorLink } from "../AnchorLink.js";
8
8
  import { useViewportAnchor } from "../context/ViewportAnchorContext.js";
9
9
  import { SidebarBadge } from "./SidebarBadge.js";
10
10
  import { SidebarCategory } from "./SidebarCategory.js";
11
11
 
12
12
  export const navigationListItem = cva(
13
- "flex items-center gap-2 px-[--padding-nav-item] my-0.5 py-1.5 rounded-lg hover:bg-accent",
13
+ "flex items-center gap-2 px-[--padding-nav-item] my-0.5 py-1.5 rounded-lg hover:bg-accent tabular-nums",
14
14
  {
15
15
  variants: {
16
16
  isActive: {
@@ -21,6 +21,10 @@ export const navigationListItem = cva(
21
21
  true: "text-foreground/30",
22
22
  false: "",
23
23
  },
24
+ isPending: {
25
+ true: "bg-accent animate-pulse",
26
+ false: "",
27
+ },
24
28
  },
25
29
  defaultVariants: {
26
30
  isActive: false,
@@ -49,8 +53,10 @@ export const SidebarItem = ({
49
53
  case "doc":
50
54
  return (
51
55
  <NavLink
52
- className={({ isActive }) => navigationListItem({ isActive })}
53
- to={joinPath(item.id)}
56
+ className={({ isActive, isPending }) =>
57
+ navigationListItem({ isActive, isPending })
58
+ }
59
+ to={joinUrl(item.id)}
54
60
  onClick={onRequestClose}
55
61
  end
56
62
  >
@@ -7,9 +7,9 @@ import {
7
7
  type CSSProperties,
8
8
  type PropsWithChildren,
9
9
  } from "react";
10
- import { AnchorLink } from "../../components/AnchorLink.js";
11
- import { useViewportAnchor } from "../../components/context/ViewportAnchorContext.js";
12
10
  import { cn } from "../../util/cn.js";
11
+ import { AnchorLink } from "../AnchorLink.js";
12
+ import { useViewportAnchor } from "../context/ViewportAnchorContext.js";
13
13
 
14
14
  const DATA_ANCHOR_ATTR = "data-active";
15
15
 
@@ -24,23 +24,14 @@ const TocItem = ({
24
24
  className?: string;
25
25
  }>) => {
26
26
  return (
27
- <li
28
- className={cn(
29
- "truncate",
30
- isActive
31
- ? "text-primary"
32
- : "text-foreground/65 dark:text-foreground/75",
33
- className,
34
- )}
35
- title={item.value}
36
- >
27
+ <li className={cn("truncate", className)} title={item.value}>
37
28
  <AnchorLink
38
29
  to={`#${item.id}`}
39
30
  {...{ [DATA_ANCHOR_ATTR]: item.id }}
40
31
  className={cn(
41
32
  isActive
42
33
  ? "text-primary"
43
- : "text-foreground/65 dark:text-foreground/75 hover:text-foreground",
34
+ : "hover:text-accent-foreground text-muted-foreground",
44
35
  )}
45
36
  >
46
37
  {item.value}
@@ -89,7 +80,7 @@ export const Toc = ({ entries }: { entries: TocEntry[] }) => {
89
80
  }, [activeAnchor]);
90
81
 
91
82
  return (
92
- <aside className="sticky scrollbar top-[--header-height] h-[calc(100vh-var(--header-height))] pt-[--padding-content-top] pb-[--padding-content-bottom] overflow-y-auto ps-1 text-sm">
83
+ <aside className="sticky scrollbar top-8 lg:top-[--header-height] h-[calc(100vh-var(--header-height))] pt-[--padding-content-top] pb-[--padding-content-bottom] overflow-y-auto ps-1 text-sm">
93
84
  <div className="flex items-center gap-2 font-medium mb-2">
94
85
  <ListTreeIcon size={16} />
95
86
  On this page