zudoku 0.3.0-dev.2 → 0.3.0-dev.20

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 (220) hide show
  1. package/dist/app/App.js +11 -8
  2. package/dist/app/App.js.map +1 -1
  3. package/dist/config/config.d.ts +8 -15
  4. package/dist/lib/authentication/authentication.d.ts +2 -2
  5. package/dist/lib/authentication/hook.d.ts +1 -0
  6. package/dist/lib/authentication/hook.js +1 -0
  7. package/dist/lib/authentication/hook.js.map +1 -1
  8. package/dist/lib/authentication/providers/auth0.js +1 -0
  9. package/dist/lib/authentication/providers/auth0.js.map +1 -1
  10. package/dist/lib/authentication/providers/clerk.js +2 -0
  11. package/dist/lib/authentication/providers/clerk.js.map +1 -1
  12. package/dist/lib/authentication/providers/openid.js +2 -0
  13. package/dist/lib/authentication/providers/openid.js.map +1 -1
  14. package/dist/lib/authentication/state.d.ts +1 -0
  15. package/dist/lib/authentication/state.js +1 -0
  16. package/dist/lib/authentication/state.js.map +1 -1
  17. package/dist/lib/components/DevPortal.d.ts +2 -20
  18. package/dist/lib/components/DevPortal.js +13 -9
  19. package/dist/lib/components/DevPortal.js.map +1 -1
  20. package/dist/lib/components/Header.js +4 -4
  21. package/dist/lib/components/Header.js.map +1 -1
  22. package/dist/lib/components/Heading.d.ts +9 -4
  23. package/dist/lib/components/Heading.js +17 -2
  24. package/dist/lib/components/Heading.js.map +1 -1
  25. package/dist/lib/components/Layout.js +1 -1
  26. package/dist/lib/components/Layout.js.map +1 -1
  27. package/dist/lib/components/SyntaxHighlight.js +5 -1
  28. package/dist/lib/components/SyntaxHighlight.js.map +1 -1
  29. package/dist/lib/components/context/DevPortalProvider.d.ts +1 -1
  30. package/dist/lib/components/context/DevPortalProvider.js +2 -2
  31. package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
  32. package/dist/lib/components/index.d.ts +2 -0
  33. package/dist/lib/components/index.js +2 -0
  34. package/dist/lib/components/index.js.map +1 -1
  35. package/dist/lib/core/DevPortalContext.d.ts +33 -3
  36. package/dist/lib/core/DevPortalContext.js +8 -4
  37. package/dist/lib/core/DevPortalContext.js.map +1 -1
  38. package/dist/lib/core/plugins.d.ts +7 -4
  39. package/dist/lib/core/plugins.js +1 -0
  40. package/dist/lib/core/plugins.js.map +1 -1
  41. package/dist/lib/oas/graphql/index.js +1 -1
  42. package/dist/lib/oas/graphql/index.js.map +1 -1
  43. package/dist/lib/oas/parser/index.js +3 -1
  44. package/dist/lib/oas/parser/index.js.map +1 -1
  45. package/dist/lib/plugins/api-keys/CreateApiKey.d.ts +4 -0
  46. package/dist/lib/plugins/{api-key → api-keys}/CreateApiKey.js +1 -1
  47. package/dist/lib/plugins/api-keys/CreateApiKey.js.map +1 -0
  48. package/dist/lib/plugins/api-keys/SettingsApiKeys.d.ts +4 -0
  49. package/dist/lib/plugins/api-keys/SettingsApiKeys.js +38 -0
  50. package/dist/lib/plugins/api-keys/SettingsApiKeys.js.map +1 -0
  51. package/dist/lib/plugins/api-keys/index.js +94 -0
  52. package/dist/lib/plugins/api-keys/index.js.map +1 -0
  53. package/dist/lib/plugins/markdown/MdxPage.d.ts +3 -2
  54. package/dist/lib/plugins/markdown/MdxPage.js +5 -4
  55. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  56. package/dist/lib/plugins/markdown/generateRoutes.d.ts +2 -2
  57. package/dist/lib/plugins/markdown/generateRoutes.js +2 -2
  58. package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -1
  59. package/dist/lib/plugins/markdown/index.d.ts +4 -1
  60. package/dist/lib/plugins/markdown/index.js +2 -2
  61. package/dist/lib/plugins/markdown/index.js.map +1 -1
  62. package/dist/lib/plugins/openapi/OperationList.js +4 -3
  63. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  64. package/dist/lib/plugins/openapi/OperationListItem.js +8 -4
  65. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  66. package/dist/lib/plugins/openapi/ParameterList.js +1 -1
  67. package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
  68. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  69. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  70. package/dist/lib/plugins/openapi/{MakeRequest.d.ts → PlaygroundDialogWrapper.d.ts} +1 -1
  71. package/dist/lib/plugins/openapi/{MakeRequest.js → PlaygroundDialogWrapper.js} +4 -4
  72. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -0
  73. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +1 -9
  74. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  75. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +1 -1
  76. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  77. package/dist/lib/plugins/openapi/SchemaListView.js +4 -26
  78. package/dist/lib/plugins/openapi/SchemaListView.js.map +1 -1
  79. package/dist/lib/plugins/openapi/SchemaListViewItem.d.ts +7 -0
  80. package/dist/lib/plugins/openapi/SchemaListViewItem.js +16 -0
  81. package/dist/lib/plugins/openapi/SchemaListViewItem.js.map +1 -0
  82. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.d.ts +8 -0
  83. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js +17 -0
  84. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js.map +1 -0
  85. package/dist/lib/plugins/openapi/Sidecar.js +10 -8
  86. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  87. package/dist/lib/plugins/openapi/{Select.d.ts → SimpleSelect.d.ts} +3 -2
  88. package/dist/lib/plugins/openapi/SimpleSelect.js +5 -0
  89. package/dist/lib/plugins/openapi/SimpleSelect.js.map +1 -0
  90. package/dist/lib/plugins/openapi/index.js +8 -0
  91. package/dist/lib/plugins/openapi/index.js.map +1 -1
  92. package/dist/lib/plugins/openapi/playground/Headers.d.ts +2 -3
  93. package/dist/lib/plugins/openapi/playground/Headers.js +16 -6
  94. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  95. package/dist/lib/plugins/openapi/playground/Playground.d.ts +4 -3
  96. package/dist/lib/plugins/openapi/playground/Playground.js +8 -12
  97. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  98. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.d.ts +3 -0
  99. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +10 -0
  100. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -0
  101. package/dist/lib/plugins/openapi/util/prose.d.ts +1 -0
  102. package/dist/lib/plugins/openapi/util/prose.js +4 -0
  103. package/dist/lib/plugins/openapi/util/prose.js.map +1 -0
  104. package/dist/lib/plugins/openapi/worker/worker.js +25 -1
  105. package/dist/lib/plugins/openapi/worker/worker.js.map +1 -1
  106. package/dist/lib/ui/button-variants.d.ts +1 -1
  107. package/dist/lib/util/MdxComponents.js +1 -1
  108. package/dist/lib/util/MdxComponents.js.map +1 -1
  109. package/dist/lib/util/objectEntries.d.ts +4 -0
  110. package/dist/lib/util/objectEntries.js +2 -0
  111. package/dist/lib/util/objectEntries.js.map +1 -0
  112. package/dist/lib/util/renderIf.d.ts +1 -0
  113. package/dist/lib/util/renderIf.js +2 -0
  114. package/dist/lib/util/renderIf.js.map +1 -0
  115. package/dist/vite/config.js +1 -2
  116. package/dist/vite/config.js.map +1 -1
  117. package/dist/vite/config.test.js +1 -1
  118. package/dist/vite/config.test.js.map +1 -1
  119. package/dist/vite/dev-server.js +1 -1
  120. package/dist/vite/dev-server.js.map +1 -1
  121. package/dist/vite/plugin-api-keys.js +2 -2
  122. package/dist/vite/plugin-api-keys.js.map +1 -1
  123. package/dist/vite/plugin-api.js +3 -3
  124. package/dist/vite/plugin-api.js.map +1 -1
  125. package/dist/vite/plugin-docs.js +2 -2
  126. package/dist/vite/plugin-docs.js.map +1 -1
  127. package/dist/vite/plugin-docs.test.js +1 -1
  128. package/dist/vite/plugin-docs.test.js.map +1 -1
  129. package/dist/vite/plugin-redirect.js +2 -2
  130. package/dist/vite/plugin-redirect.js.map +1 -1
  131. package/lib/DevPortalProvider-CRKuwoXc.js +4123 -0
  132. package/lib/Markdown-5LmPZyLV.js +8620 -0
  133. package/lib/MdxComponents-DYD_QPVF.js +3017 -0
  134. package/lib/Select-DR3PiqjV.js +4569 -0
  135. package/lib/Spinner-DjQ2eBxC.js +181 -0
  136. package/lib/assets/{worker-BCcpCNJ7.js → worker-DGvzLstc.js} +9843 -9800
  137. package/lib/hook-FCY9-FHO.js +24 -0
  138. package/lib/index-By9bEW57.js +411 -0
  139. package/lib/{index-DNx3xWa2.js → index-PyGcnQFX.js} +13 -12
  140. package/lib/loglevel-CA34MiFn.js +152 -0
  141. package/lib/prism-csharp.min-Yizuc34Y.js +34 -0
  142. package/lib/prism-objectivec.min-BXSWqpJJ.js +1 -0
  143. package/lib/{state-oycsxkHz.js → state-Ds_OxRHP.js} +19 -18
  144. package/lib/util-voKLTRDG.js +740 -0
  145. package/lib/zudoku.auth-auth0.js +2 -1
  146. package/lib/zudoku.auth-clerk.js +10 -8
  147. package/lib/zudoku.auth-openid.js +442 -588
  148. package/lib/zudoku.components.js +283 -292
  149. package/lib/zudoku.openapi-worker.js +12 -12
  150. package/lib/zudoku.plugin-api-keys.js +292 -0
  151. package/lib/zudoku.plugin-markdown.js +255 -0
  152. package/lib/zudoku.plugin-openapi.js +6240 -0
  153. package/lib/zudoku.plugin-redirect.js +10 -0
  154. package/package.json +17 -4
  155. package/src/app/App.tsx +12 -8
  156. package/src/lib/authentication/authentication.ts +2 -5
  157. package/src/lib/authentication/hook.ts +1 -0
  158. package/src/lib/authentication/providers/auth0.tsx +1 -0
  159. package/src/lib/authentication/providers/clerk.tsx +2 -0
  160. package/src/lib/authentication/providers/openid.tsx +2 -0
  161. package/src/lib/authentication/state.ts +2 -0
  162. package/src/lib/components/DevPortal.tsx +12 -28
  163. package/src/lib/components/Header.tsx +25 -22
  164. package/src/lib/components/Heading.tsx +26 -7
  165. package/src/lib/components/Layout.tsx +2 -2
  166. package/src/lib/components/SyntaxHighlight.tsx +5 -1
  167. package/src/lib/components/context/DevPortalProvider.ts +2 -2
  168. package/src/lib/components/index.ts +3 -0
  169. package/src/lib/core/DevPortalContext.ts +42 -12
  170. package/src/lib/core/plugins.ts +10 -5
  171. package/src/lib/oas/graphql/index.ts +2 -2
  172. package/src/lib/oas/parser/index.ts +3 -1
  173. package/src/lib/plugins/{api-key → api-keys}/CreateApiKey.tsx +2 -8
  174. package/src/lib/plugins/{api-key → api-keys}/SettingsApiKeys.tsx +21 -14
  175. package/src/lib/plugins/{api-key → api-keys}/index.tsx +67 -18
  176. package/src/lib/plugins/markdown/MdxPage.tsx +50 -33
  177. package/src/lib/plugins/markdown/generateRoutes.tsx +12 -2
  178. package/src/lib/plugins/markdown/index.tsx +8 -1
  179. package/src/lib/plugins/openapi/OperationList.tsx +9 -3
  180. package/src/lib/plugins/openapi/OperationListItem.tsx +66 -41
  181. package/src/lib/plugins/openapi/ParameterList.tsx +1 -1
  182. package/src/lib/plugins/openapi/ParameterListItem.tsx +3 -4
  183. package/src/lib/plugins/openapi/{MakeRequest.tsx → PlaygroundDialogWrapper.tsx} +3 -3
  184. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -16
  185. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +3 -1
  186. package/src/lib/plugins/openapi/SchemaListView.tsx +15 -182
  187. package/src/lib/plugins/openapi/SchemaListViewItem.tsx +110 -0
  188. package/src/lib/plugins/openapi/SchemaListViewItemGroup.tsx +63 -0
  189. package/src/lib/plugins/openapi/Sidecar.tsx +15 -10
  190. package/src/lib/plugins/openapi/{Select.tsx → SimpleSelect.tsx} +5 -2
  191. package/src/lib/plugins/openapi/index.tsx +17 -4
  192. package/src/lib/plugins/openapi/playground/Headers.tsx +60 -33
  193. package/src/lib/plugins/openapi/playground/Playground.tsx +158 -193
  194. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +34 -0
  195. package/src/lib/plugins/openapi/util/prose.ts +7 -0
  196. package/src/lib/plugins/openapi/worker/worker.ts +27 -1
  197. package/src/lib/util/MdxComponents.tsx +1 -1
  198. package/src/lib/util/objectEntries.ts +5 -0
  199. package/src/lib/util/renderIf.ts +4 -0
  200. package/dist/lib/plugins/api-key/CreateApiKey.d.ts +0 -5
  201. package/dist/lib/plugins/api-key/CreateApiKey.js.map +0 -1
  202. package/dist/lib/plugins/api-key/SettingsApiKeys.d.ts +0 -5
  203. package/dist/lib/plugins/api-key/SettingsApiKeys.js +0 -38
  204. package/dist/lib/plugins/api-key/SettingsApiKeys.js.map +0 -1
  205. package/dist/lib/plugins/api-key/index.js +0 -62
  206. package/dist/lib/plugins/api-key/index.js.map +0 -1
  207. package/dist/lib/plugins/index.d.ts +0 -4
  208. package/dist/lib/plugins/index.js +0 -5
  209. package/dist/lib/plugins/index.js.map +0 -1
  210. package/dist/lib/plugins/openapi/MakeRequest.js.map +0 -1
  211. package/dist/lib/plugins/openapi/Select.js +0 -5
  212. package/dist/lib/plugins/openapi/Select.js.map +0 -1
  213. package/dist/vite/common.d.ts +0 -1
  214. package/dist/vite/common.js +0 -5
  215. package/dist/vite/common.js.map +0 -1
  216. package/lib/Spinner-7LezPqGn.js +0 -8393
  217. package/lib/clerk-Wslx_mPo.js +0 -19685
  218. package/lib/zudoku.plugins.js +0 -19857
  219. package/src/lib/plugins/index.ts +0 -4
  220. /package/dist/lib/plugins/{api-key → api-keys}/index.d.ts +0 -0
@@ -8,21 +8,15 @@ import { useState } from "react";
8
8
  import { Link } from "react-router-dom";
9
9
  import { useDevPortal } from "../../components/context/DevPortalProvider.js";
10
10
  import { Button } from "../../ui/Button.js";
11
- import { Card } from "../../ui/Card.js";
12
- import { type ApiKeyPluginOptions, ApiKeyService } from "./index.js";
11
+ import { ApiKeyService } from "./index.js";
13
12
 
14
- export const SettingsApiKeys = ({
15
- options,
16
- service,
17
- }: {
18
- options: ApiKeyPluginOptions;
19
- service: ApiKeyService;
20
- }) => {
13
+ export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
21
14
  const context = useDevPortal();
22
15
  const queryClient = useQueryClient();
23
16
  const { data } = useSuspenseQuery({
24
17
  queryFn: () => service.getKeys(context),
25
18
  queryKey: ["api-keys"],
19
+ retry: false,
26
20
  });
27
21
 
28
22
  const deleteKeyMutation = useMutation({
@@ -39,7 +33,7 @@ export const SettingsApiKeys = ({
39
33
  });
40
34
 
41
35
  return (
42
- <div className="max-w-screen-lg pt-[--padding-content-top] pb-[--padding-content-bottom]">
36
+ <div className="max-w-screen-lg h-full pt-[--padding-content-top] pb-[--padding-content-bottom]">
43
37
  <div className="flex justify-between mb-4 border-b border-border pb-3">
44
38
  <h1 className="font-medium text-2xl">API Keys</h1>
45
39
  {service.createKey && (
@@ -49,9 +43,22 @@ export const SettingsApiKeys = ({
49
43
  )}
50
44
  </div>
51
45
 
52
- <Card>
53
- <ul className="grid grid-cols-[min-content_1fr_min-content] ">
54
- {data.map((key) => (
46
+ {data.length === 0 ? (
47
+ <div className="flex flex-col justify-center gap-4 items-center h-1/2 my-8">
48
+ <div className="text-center">
49
+ No API keys created yet.
50
+ <br />
51
+ Get started and create the first one now
52
+ </div>
53
+ {service.createKey && (
54
+ <Button asChild>
55
+ <Link to="/settings/api-keys/new">Create API Key</Link>
56
+ </Button>
57
+ )}
58
+ </div>
59
+ ) : (
60
+ <ul className="grid grid-cols-[minmax(250px,min-content)_1fr_min-content] ">
61
+ {data.map((key: any) => (
55
62
  <li
56
63
  className="border-b border-border p-5 grid grid-cols-subgrid col-span-full gap-2 items-center"
57
64
  key={key.id}
@@ -100,7 +107,7 @@ export const SettingsApiKeys = ({
100
107
  </li>
101
108
  ))}
102
109
  </ul>
103
- </Card>
110
+ )}
104
111
  </div>
105
112
  );
106
113
  };
@@ -1,8 +1,13 @@
1
+ import logger from "loglevel";
2
+ import { Outlet, useRouteError } from "react-router-dom";
3
+ import invariant from "tiny-invariant";
4
+ import { useAuth } from "../../authentication/hook.js";
1
5
  import { DevPortalContext } from "../../core/DevPortalContext.js";
2
6
  import {
3
7
  type ApiIdentityPlugin,
4
8
  type DevPortalPlugin,
5
9
  } from "../../core/plugins.js";
10
+ import { Button } from "../../ui/Button.js";
6
11
  import { CreateApiKey } from "./CreateApiKey.js";
7
12
  import { SettingsApiKeys } from "./SettingsApiKeys.js";
8
13
 
@@ -47,7 +52,8 @@ const createDefaultHandler = (endpoint: string): ApiKeyService => {
47
52
 
48
53
  await context.signRequest(request);
49
54
 
50
- await fetch(request);
55
+ const response = await fetch(request);
56
+ invariant(response.ok, "Failed to delete API key");
51
57
  },
52
58
  createKey: async (apiKey, context) => {
53
59
  const request = new Request(endpoint + `/v1/developer/api-keys`, {
@@ -59,7 +65,9 @@ const createDefaultHandler = (endpoint: string): ApiKeyService => {
59
65
  });
60
66
 
61
67
  await context.signRequest(request);
62
- await fetch(request);
68
+
69
+ const response = await fetch(request);
70
+ invariant(response.ok, "Failed to create API key");
63
71
  },
64
72
  getKeys: async (context) => {
65
73
  const request = new Request(endpoint + `/v1/developer/api-keys`);
@@ -67,12 +75,43 @@ const createDefaultHandler = (endpoint: string): ApiKeyService => {
67
75
  await context.signRequest(request);
68
76
 
69
77
  const keys = await fetch(request);
78
+ invariant(keys.ok, "Failed to fetch API keys");
70
79
 
71
80
  return await keys.json();
72
81
  },
73
82
  };
74
83
  };
75
84
 
85
+ const ProtectedRoute = () => {
86
+ const auth = useAuth();
87
+
88
+ // TODO: should we suspend here somehow?
89
+ if (auth.isPending) {
90
+ return null;
91
+ }
92
+
93
+ return auth.isAuthenticated ? (
94
+ <Outlet />
95
+ ) : (
96
+ <div className="flex flex-col justify-center gap-2 items-center h-1/2 my-12">
97
+ Please login first to view this page
98
+ <Button onClick={() => auth.login()}>Login</Button>
99
+ </div>
100
+ );
101
+ };
102
+
103
+ const SettingsErrorBoundary = () => {
104
+ const error = useRouteError();
105
+ logger.error(String(error));
106
+
107
+ return (
108
+ <div className="flex flex-col justify-center gap-2 items-center h-1/2 my-12">
109
+ <h1>Something went wrong</h1>
110
+ {error instanceof Error && <p>{error.message}</p>}
111
+ </div>
112
+ );
113
+ };
114
+
76
115
  export const apiKeyPlugin = (
77
116
  options: ApiKeyPluginOptions,
78
117
  ): DevPortalPlugin & ApiIdentityPlugin => {
@@ -84,26 +123,36 @@ export const apiKeyPlugin = (
84
123
 
85
124
  return {
86
125
  getIdentities: async (context) => {
87
- const keys = await service.getKeys(context);
88
-
89
- return keys.map((key) => ({
90
- authorizeRequest: (request) => {
91
- request.headers.set("Authorization", `Bearer ${key.key}`);
92
- return request;
93
- },
94
- id: key.id,
95
- label: key.description ?? key.id,
96
- }));
126
+ try {
127
+ const keys = await service.getKeys(context);
128
+
129
+ return keys.map((key) => ({
130
+ authorizeRequest: (request) => {
131
+ request.headers.set("Authorization", `Bearer ${key.key}`);
132
+ return request;
133
+ },
134
+ id: key.id,
135
+ label: key.description ?? key.id,
136
+ }));
137
+ } catch {
138
+ return [];
139
+ }
97
140
  },
98
141
  getRoutes: () => {
99
142
  return [
100
143
  {
101
- path: "/settings/api-keys",
102
- element: <SettingsApiKeys options={options} service={service} />,
103
- },
104
- {
105
- path: "/settings/api-keys/new",
106
- element: <CreateApiKey options={options} service={service} />,
144
+ element: <ProtectedRoute />,
145
+ errorElement: <SettingsErrorBoundary />,
146
+ children: [
147
+ {
148
+ path: "/settings/api-keys",
149
+ element: <SettingsApiKeys service={service} />,
150
+ },
151
+ {
152
+ path: "/settings/api-keys/new",
153
+ element: <CreateApiKey service={service} />,
154
+ },
155
+ ],
107
156
  },
108
157
  ];
109
158
  },
@@ -12,7 +12,7 @@ import { cn } from "../../util/cn.js";
12
12
  import slugify from "../../util/slugify.js";
13
13
  import { traverseNavigation } from "../../util/traverseNavigation.js";
14
14
  import { Toc } from "./Toc.js";
15
- import type { MDXImport } from "./index.js";
15
+ import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
16
16
 
17
17
  const MarkdownHeadings = {
18
18
  h2: ({ children, id }) => (
@@ -30,11 +30,13 @@ const MarkdownHeadings = {
30
30
 
31
31
  export const MdxPage = ({
32
32
  mdxComponent: MdxComponent,
33
- frontmatter,
33
+ frontmatter = {},
34
+ defaultOptions,
34
35
  tableOfContents,
35
36
  }: PropsWithChildren<
36
37
  Omit<MDXImport, "default"> & {
37
38
  mdxComponent: MDXImport["default"];
39
+ defaultOptions?: MarkdownPluginDefaultOptions;
38
40
  }
39
41
  >) => {
40
42
  const navItem = useTopNavigationItem();
@@ -50,10 +52,11 @@ export const MdxPage = ({
50
52
 
51
53
  const title = frontmatter.title;
52
54
  const category = frontmatter.category ?? categoryTitle;
53
- const hideToc = frontmatter.toc === false;
54
-
55
+ const hideToc = frontmatter.toc === false || defaultOptions?.toc === false;
55
56
  const pageTitle =
56
57
  tableOfContents.find((item) => item.depth === 1)?.value ?? title;
58
+ const hidePager =
59
+ frontmatter.disablePager ?? defaultOptions?.disablePager ?? false;
57
60
 
58
61
  const tocEntries =
59
62
  tableOfContents.find((item) => item.depth === 1)?.children ??
@@ -112,35 +115,49 @@ export const MdxPage = ({
112
115
  <MdxComponent
113
116
  components={{ ...useMDXComponents(), ...MarkdownHeadings }}
114
117
  />
115
- <hr />
116
- <div className="not-prose flex items-center justify-between gap-8">
117
- {prev.path ? (
118
- <Link
119
- to={prev.path}
120
- className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-start hover:border-primary/85 transition shadow-sm hover:shadow-md"
121
- title={typeof prev.label === "string" ? prev.label : undefined}
122
- >
123
- <div className="text-sm text-muted-foreground">
124
- Previous page
125
- </div>
126
- <div className="text-lg text-primary truncate">{prev.label}</div>
127
- </Link>
128
- ) : (
129
- <div className="flex-1" />
130
- )}
131
- {next.path ? (
132
- <Link
133
- to={next.path}
134
- className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-end hover:border-primary/85 transition shadow-sm hover:shadow-md"
135
- title={typeof next.label === "string" ? next.label : undefined}
136
- >
137
- <div className="text-sm text-muted-foreground">Next page →</div>
138
- <div className="text-lg text-primary truncate">{next.label}</div>
139
- </Link>
140
- ) : (
141
- <div className="flex-1" />
142
- )}
143
- </div>
118
+ {!hidePager && (
119
+ <>
120
+ <hr />
121
+ <div className="not-prose flex items-center justify-between gap-8">
122
+ {prev.path ? (
123
+ <Link
124
+ to={prev.path}
125
+ className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-start hover:border-primary/85 transition shadow-sm hover:shadow-md"
126
+ title={
127
+ typeof prev.label === "string" ? prev.label : undefined
128
+ }
129
+ >
130
+ <div className="text-sm text-muted-foreground">
131
+ Previous page
132
+ </div>
133
+ <div className="text-lg text-primary truncate">
134
+ {prev.label}
135
+ </div>
136
+ </Link>
137
+ ) : (
138
+ <div className="flex-1" />
139
+ )}
140
+ {next.path ? (
141
+ <Link
142
+ to={next.path}
143
+ className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-end hover:border-primary/85 transition shadow-sm hover:shadow-md"
144
+ title={
145
+ typeof next.label === "string" ? next.label : undefined
146
+ }
147
+ >
148
+ <div className="text-sm text-muted-foreground">
149
+ Next page →
150
+ </div>
151
+ <div className="text-lg text-primary truncate">
152
+ {next.label}
153
+ </div>
154
+ </Link>
155
+ ) : (
156
+ <div className="flex-1" />
157
+ )}
158
+ </div>
159
+ </>
160
+ )}
144
161
  </div>
145
162
  <div className="hidden xl:block">
146
163
  {showToc && <Toc entries={tocEntries} />}
@@ -3,10 +3,14 @@ import { isPathItem } from "../../components/navigation/util.js";
3
3
  import { Navigate, type RouteObject } from "../../core/router.js";
4
4
  import { traverseNavigation } from "../../util/traverseNavigation.js";
5
5
  import { MdxPage } from "./MdxPage.js";
6
- import type { MarkdownPluginOptions } from "./index.js";
6
+ import {
7
+ MarkdownPluginDefaultOptions,
8
+ MarkdownPluginOptions,
9
+ } from "./index.js";
7
10
 
8
11
  export const generateRoutes = (
9
12
  markdownFiles: MarkdownPluginOptions["markdownFiles"],
13
+ defaultOptions?: MarkdownPluginDefaultOptions,
10
14
  ): RouteObject[] => {
11
15
  const routes = Object.entries(markdownFiles).flatMap(
12
16
  ([file, importPromise]) => {
@@ -28,7 +32,13 @@ export const generateRoutes = (
28
32
  const { default: Component, ...props } = await importPromise();
29
33
 
30
34
  return {
31
- element: <MdxPage mdxComponent={Component} {...props} />,
35
+ element: (
36
+ <MdxPage
37
+ mdxComponent={Component}
38
+ {...props}
39
+ defaultOptions={defaultOptions}
40
+ />
41
+ ),
32
42
  };
33
43
  },
34
44
  } satisfies RouteObject;
@@ -5,13 +5,19 @@ import { generateRoutes } from "./generateRoutes.js";
5
5
 
6
6
  export type MarkdownPluginOptions = {
7
7
  markdownFiles: Record<string, () => Promise<MDXImport>>;
8
+ defaultOptions?: MarkdownPluginDefaultOptions;
8
9
  };
10
+ export type MarkdownPluginDefaultOptions = Pick<
11
+ Frontmatter,
12
+ "toc" | "disablePager"
13
+ >;
9
14
 
10
15
  export type Frontmatter = {
11
16
  title?: string;
12
17
  description?: string;
13
18
  category?: string;
14
19
  toc?: boolean;
20
+ disablePager?: boolean;
15
21
  };
16
22
 
17
23
  export type MDXImport = {
@@ -22,10 +28,11 @@ export type MDXImport = {
22
28
 
23
29
  export const markdownPlugin = ({
24
30
  markdownFiles,
31
+ defaultOptions,
25
32
  }: MarkdownPluginOptions): DevPortalPlugin => {
26
33
  return {
27
34
  getRoutes() {
28
- return generateRoutes(markdownFiles);
35
+ return generateRoutes(markdownFiles, defaultOptions);
29
36
  },
30
37
  };
31
38
  };
@@ -1,11 +1,12 @@
1
1
  import { ResultOf } from "@graphql-typed-document-node/core";
2
2
  import { CategoryHeading } from "../../components/CategoryHeading.js";
3
3
  import { Heading } from "../../components/Heading.js";
4
- import { Markdown, ProseClasses } from "../../components/Markdown.js";
4
+ import { Markdown } from "../../components/Markdown.js";
5
5
  import { cn } from "../../util/cn.js";
6
6
  import { OperationListItem } from "./OperationListItem.js";
7
7
  import { graphql } from "./graphql/index.js";
8
8
  import { useOasConfig } from "./index.js";
9
+ import { SchemaProseClasses } from "./util/prose.js";
9
10
  import { useQuery } from "./util/urql.js";
10
11
 
11
12
  export const OperationsFragment = graphql(/* GraphQL */ `
@@ -94,7 +95,12 @@ export const OperationList = () => {
94
95
 
95
96
  return (
96
97
  <div className="pt-[--padding-content-top]">
97
- <div className={cn(ProseClasses, "mb-16")}>
98
+ <div
99
+ className={cn(
100
+ SchemaProseClasses,
101
+ "mb-16 max-w-full prose-img:max-w-prose",
102
+ )}
103
+ >
98
104
  <CategoryHeading>Overview</CategoryHeading>
99
105
  <Heading level={1} id="description" registerSidebarAnchor>
100
106
  {result.data.schema.title}
@@ -108,7 +114,7 @@ export const OperationList = () => {
108
114
  {tag.name && <CategoryHeading>{tag.name}</CategoryHeading>}
109
115
  {tag.description && (
110
116
  <Markdown
111
- className={`${ProseClasses} mt-2 mb-12`}
117
+ className={`${SchemaProseClasses} mt-2 mb-12`}
112
118
  content={tag.description}
113
119
  />
114
120
  )}
@@ -1,11 +1,13 @@
1
1
  import { Heading } from "../../components/Heading.js";
2
- import { Markdown, ProseClasses } from "../../components/Markdown.js";
2
+ import { Markdown } from "../../components/Markdown.js";
3
3
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/Tabs.js";
4
+ import { renderIf } from "../../util/renderIf.js";
4
5
  import { OperationsFragment } from "./OperationList.js";
5
6
  import { ParameterList } from "./ParameterList.js";
6
7
  import { SchemaListView } from "./SchemaListView.js";
7
8
  import { Sidecar } from "./Sidecar.js";
8
9
  import { FragmentType, useFragment } from "./graphql/index.js";
10
+ import { SchemaProseClasses } from "./util/prose.js";
9
11
 
10
12
  export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
11
13
  export type ParameterGroup = (typeof PARAM_GROUPS)[number];
@@ -20,24 +22,24 @@ export const OperationListItem = ({
20
22
  operation.parameters ?? [],
21
23
  (param) => param.in,
22
24
  );
23
- const first = operation.responses.find((re) => re.statusCode === "200");
25
+ const first = operation.responses.at(0);
24
26
  return (
25
27
  <div
26
28
  key={operation.operationId}
27
- className="grid grid-cols-2 gap-8 items-start border-b-2 mb-16 pb-16 border-border"
29
+ className="grid grid-cols-[4fr_3fr] gap-8 items-start border-b-2 mb-16 pb-16 border-border"
28
30
  >
29
- <div className={ProseClasses}>
30
- <Heading
31
- level={2}
32
- className="mt-0"
33
- id={operation.slug}
34
- registerSidebarAnchor
35
- >
31
+ <div className="flex flex-col gap-4">
32
+ <Heading level={2} id={operation.slug} registerSidebarAnchor>
36
33
  {operation.summary}
37
34
  </Heading>
38
- {operation.description && <Markdown content={operation.description} />}
35
+ {operation.description && (
36
+ <Markdown
37
+ className={SchemaProseClasses}
38
+ content={operation.description}
39
+ />
40
+ )}
39
41
  {operation.parameters && operation.parameters.length > 0 && (
40
- <div className="mt-4">
42
+ <>
41
43
  {PARAM_GROUPS.flatMap((group) =>
42
44
  groupedParameters[group]?.length ? (
43
45
  <ParameterList
@@ -50,37 +52,60 @@ export const OperationListItem = ({
50
52
  []
51
53
  ),
52
54
  )}
55
+ </>
56
+ )}
57
+ {renderIf(operation.requestBody?.content?.at(0)?.schema, (schema) => (
58
+ <div className="mt-4 flex flex-col gap-4">
59
+ <Heading level={3} className="capitalize">
60
+ Request Body
61
+ </Heading>
62
+ <SchemaListView schema={schema} />
53
63
  </div>
64
+ ))}
65
+ {operation.responses.length > 0 && (
66
+ <>
67
+ <Heading
68
+ level={3}
69
+ className="capitalize mt-8 pt-8 border-border border-t"
70
+ >
71
+ Responses
72
+ </Heading>
73
+ <Tabs defaultValue={`${first?.statusCode}${first?.description}`}>
74
+ <TabsList>
75
+ {operation.responses.map((response) => (
76
+ <TabsTrigger
77
+ value={response.statusCode + response.description}
78
+ key={response.statusCode}
79
+ title={response.description}
80
+ >
81
+ {response.statusCode}
82
+ </TabsTrigger>
83
+ ))}
84
+ </TabsList>
85
+ <ul className="list-none m-0 px-0 overflow-hidden">
86
+ {operation.responses.map((response) => (
87
+ <TabsContent
88
+ value={response.statusCode + response.description}
89
+ key={response.statusCode}
90
+ >
91
+ {renderIf(
92
+ response.content?.find((content) => content.schema),
93
+ (content) => {
94
+ return (
95
+ <SchemaListView schema={content.schema} name="" />
96
+ );
97
+ },
98
+ ) ?? (
99
+ <div className="border-border font-mono text-sm border rounded p-4">
100
+ No response body
101
+ </div>
102
+ )}
103
+ </TabsContent>
104
+ ))}
105
+ </ul>
106
+ </Tabs>
107
+ </>
54
108
  )}
55
- <Heading level={3} className="capitalize">
56
- Responses
57
- </Heading>
58
- <Tabs defaultValue={`${first?.statusCode}${first?.description}`}>
59
- <TabsList>
60
- {operation.responses.map((response) => (
61
- <TabsTrigger
62
- value={response.statusCode + response.description}
63
- key={response.statusCode}
64
- title={response.description}
65
- >
66
- {response.statusCode}
67
- </TabsTrigger>
68
- ))}
69
- </TabsList>
70
- <ul className="list-none m-0 px-0 overflow-hidden">
71
- {operation.responses.map((response) => (
72
- <TabsContent
73
- value={response.statusCode + response.description}
74
- key={response.statusCode}
75
- >
76
- <SchemaListView
77
- schema={response.content?.at(0)?.schema}
78
- name=""
79
- />
80
- </TabsContent>
81
- ))}
82
- </ul>
83
- </Tabs>
84
109
  </div>
85
110
 
86
111
  <Sidecar operation={operation} />
@@ -18,7 +18,7 @@ export const ParameterList = ({
18
18
  <Heading level={3} id={`${id}/${group}-parameters`} className="capitalize">
19
19
  {group === "header" ? "Headers" : `${group} Parameters`}
20
20
  </Heading>
21
- <ul className="list-none m-0 px-0 overflow-hidden">
21
+ <ul className="list-none m-0 px-0 overflow-hidden border border-border divide-y divide-border rounded">
22
22
  {parameters.map((parameter) => (
23
23
  <ParameterListItem
24
24
  key={`${parameter.name}-${parameter.in}`}
@@ -28,14 +28,13 @@ export const ParameterListItem = ({
28
28
  group: ParameterGroup;
29
29
  id: string;
30
30
  }) => (
31
- <li className="not-prose px-2 py-4 border-t border-border bg-border/20 text-sm flex flex-col gap-1">
31
+ <li className="p-4 bg-border/20 text-sm flex flex-col gap-1">
32
32
  <div className="flex items-center gap-2">
33
33
  <code>
34
34
  {group === "path" ? (
35
35
  <ColorizedParam
36
36
  name={parameter.name}
37
37
  backgroundOpacity="15%"
38
- className="px-1"
39
38
  slug={id + "-" + parameter.name.toLocaleLowerCase()}
40
39
  />
41
40
  ) : (
@@ -43,12 +42,12 @@ export const ParameterListItem = ({
43
42
  )}
44
43
  </code>
45
44
  {parameter.required && (
46
- <span className="py-px px-1.5 font-medium text-xs bg-primary/75 text-muted rounded-lg">
45
+ <span className="py-px px-1.5 font-medium bg-primary/75 text-muted rounded-lg">
47
46
  required
48
47
  </span>
49
48
  )}
50
49
  {getParameterSchema(parameter).type && (
51
- <span className="text-xs text-muted-foreground">
50
+ <span className="text-muted-foreground">
52
51
  {getParameterSchema(parameter).type}
53
52
  </span>
54
53
  )}
@@ -1,7 +1,7 @@
1
1
  import { graphql } from "./graphql/index.js";
2
2
  import { useOasConfig } from "./index.js";
3
3
  import type { OperationListItemResult } from "./OperationList.js";
4
- import { Playground } from "./playground/Playground.js";
4
+ import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
5
5
 
6
6
  import { useQuery } from "urql";
7
7
 
@@ -13,7 +13,7 @@ const GetServerQuery = graphql(/* GraphQL */ `
13
13
  }
14
14
  `);
15
15
 
16
- export const MakeRequest = ({
16
+ export const PlaygroundDialogWrapper = ({
17
17
  operation,
18
18
  }: {
19
19
  operation: OperationListItemResult;
@@ -50,7 +50,7 @@ export const MakeRequest = ({
50
50
  false;
51
51
 
52
52
  return (
53
- <Playground
53
+ <PlaygroundDialog
54
54
  host={server.data?.schema.url ?? ""}
55
55
  method={operation.method}
56
56
  url={operation.path}
@@ -1,8 +1,6 @@
1
- import { useState } from "react";
2
1
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
3
2
  import { type SchemaObject } from "../../oas/graphql/index.js";
4
3
  import type { OperationListItemResult } from "./OperationList.js";
5
- import { Select } from "./Select.js";
6
4
  import * as SidecarBox from "./SidecarBox.js";
7
5
  import { generateSchemaExample } from "./util/generateSchemaExample.js";
8
6
 
@@ -12,23 +10,13 @@ type Content = NonNullable<
12
10
 
13
11
  // @todo should we handle multiple content types?
14
12
  export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
15
- const [selected, setSelected] = useState("example");
16
-
17
13
  if (!content.length) return null;
18
14
 
19
15
  return (
20
16
  <>
21
- <div>lol</div>
22
17
  <SidecarBox.Root>
23
18
  <SidecarBox.Head className="text-xs flex justify-between items-center">
24
- <span className="font-mono">Request Body</span>
25
- <Select
26
- onChange={(e) => setSelected(e.target.value)}
27
- options={[
28
- { value: "example", label: "Example" },
29
- { value: "schema", label: "Schema" },
30
- ]}
31
- />
19
+ <span className="font-mono">Request Body Example</span>
32
20
  </SidecarBox.Head>
33
21
  <SidecarBox.Body>
34
22
  <SyntaxHighlight
@@ -37,9 +25,7 @@ export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
37
25
  copyable
38
26
  className="text-xs"
39
27
  code={JSON.stringify(
40
- selected === "example"
41
- ? generateSchemaExample(content[0].schema as SchemaObject)
42
- : content[0].schema,
28
+ generateSchemaExample(content[0].schema as SchemaObject),
43
29
  null,
44
30
  2,
45
31
  )}