zudoku 0.3.0-dev.10 → 0.3.0-dev.11

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 (50) hide show
  1. package/dist/lib/authentication/authentication.d.ts +2 -2
  2. package/dist/lib/components/DevPortal.js +12 -7
  3. package/dist/lib/components/DevPortal.js.map +1 -1
  4. package/dist/lib/components/context/DevPortalProvider.d.ts +1 -1
  5. package/dist/lib/components/context/DevPortalProvider.js +2 -2
  6. package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
  7. package/dist/lib/core/DevPortalContext.js +1 -1
  8. package/dist/lib/core/DevPortalContext.js.map +1 -1
  9. package/dist/lib/core/plugins.d.ts +7 -4
  10. package/dist/lib/core/plugins.js +1 -0
  11. package/dist/lib/core/plugins.js.map +1 -1
  12. package/dist/lib/oas/graphql/index.js +1 -1
  13. package/dist/lib/oas/graphql/index.js.map +1 -1
  14. package/dist/lib/oas/parser/index.js +3 -1
  15. package/dist/lib/oas/parser/index.js.map +1 -1
  16. package/dist/lib/plugins/openapi/{MakeRequest.d.ts → PlaygroundDialogWrapper.d.ts} +1 -1
  17. package/dist/lib/plugins/openapi/{MakeRequest.js → PlaygroundDialogWrapper.js} +4 -4
  18. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -0
  19. package/dist/lib/plugins/openapi/Sidecar.js +2 -2
  20. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  21. package/dist/lib/plugins/openapi/index.js +8 -0
  22. package/dist/lib/plugins/openapi/index.js.map +1 -1
  23. package/dist/lib/plugins/openapi/playground/Playground.d.ts +4 -4
  24. package/dist/lib/plugins/openapi/playground/Playground.js +7 -11
  25. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  26. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.d.ts +3 -0
  27. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +10 -0
  28. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -0
  29. package/dist/lib/plugins/openapi/worker/worker.js +25 -1
  30. package/dist/lib/plugins/openapi/worker/worker.js.map +1 -1
  31. package/lib/{Spinner-DCwVN24H.js → Spinner-CzCKCaUK.js} +487 -484
  32. package/lib/assets/{worker-BCcpCNJ7.js → worker-DGvzLstc.js} +9843 -9800
  33. package/lib/zudoku.components.js +156 -144
  34. package/lib/zudoku.openapi-worker.js +12 -12
  35. package/lib/zudoku.plugins.js +1931 -1923
  36. package/package.json +2 -1
  37. package/src/lib/authentication/authentication.ts +2 -5
  38. package/src/lib/components/DevPortal.tsx +9 -3
  39. package/src/lib/components/context/DevPortalProvider.ts +2 -2
  40. package/src/lib/core/DevPortalContext.ts +1 -1
  41. package/src/lib/core/plugins.ts +10 -5
  42. package/src/lib/oas/graphql/index.ts +2 -2
  43. package/src/lib/oas/parser/index.ts +3 -1
  44. package/src/lib/plugins/openapi/{MakeRequest.tsx → PlaygroundDialogWrapper.tsx} +3 -3
  45. package/src/lib/plugins/openapi/Sidecar.tsx +2 -2
  46. package/src/lib/plugins/openapi/index.tsx +17 -4
  47. package/src/lib/plugins/openapi/playground/Playground.tsx +157 -187
  48. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +34 -0
  49. package/src/lib/plugins/openapi/worker/worker.ts +27 -1
  50. package/dist/lib/plugins/openapi/MakeRequest.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.3.0-dev.10",
3
+ "version": "0.3.0-dev.11",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -53,6 +53,7 @@
53
53
  }
54
54
  },
55
55
  "dependencies": {
56
+ "@envelop/core": "5.0.1",
56
57
  "@graphql-typed-document-node/core": "3.2.0",
57
58
  "@lekoarts/rehype-meta-as-attributes": "3.0.1",
58
59
  "@mdx-js/react": "3.0.1",
@@ -1,9 +1,6 @@
1
- import {
2
- InitializationPlugin,
3
- NavigationPlugin,
4
- } from "../../lib/core/plugins.js";
1
+ import { CommonPlugin, NavigationPlugin } from "../../lib/core/plugins.js";
5
2
 
6
- type AuthenticationPlugin = NavigationPlugin & InitializationPlugin;
3
+ type AuthenticationPlugin = NavigationPlugin & CommonPlugin;
7
4
 
8
5
  export interface AuthenticationProvider extends AuthenticationPlugin {
9
6
  login(): Promise<void>;
@@ -1,13 +1,13 @@
1
1
  import { MDXProvider } from "@mdx-js/react";
2
2
  import { QueryClientProvider } from "@tanstack/react-query";
3
- import { memo, Suspense, useEffect, useMemo } from "react";
3
+ import { Fragment, memo, Suspense, useEffect, useMemo } from "react";
4
4
  import {
5
5
  DevPortalContext,
6
6
  queryClient,
7
7
  type NavigationItem,
8
8
  } from "../core/DevPortalContext.js";
9
- import { HelmetProvider } from "../core/helmet.js";
10
- import { type DevPortalPlugin } from "../core/plugins.js";
9
+ import { Helmet, HelmetProvider } from "../core/helmet.js";
10
+ import { hasHead, type DevPortalPlugin } from "../core/plugins.js";
11
11
 
12
12
  import { AuthenticationProvider } from "../authentication/authentication.js";
13
13
  import {
@@ -64,9 +64,15 @@ const DevPortalInner = (props: DevPortalProps) => {
64
64
  void devPortalContext.initialize();
65
65
  }, [devPortalContext]);
66
66
 
67
+ const heads = props.plugins
68
+ ?.filter(hasHead)
69
+ // eslint-disable-next-line react/no-array-index-key
70
+ .map((plugin, i) => <Fragment key={i}>{plugin.getHead?.()}</Fragment>);
71
+
67
72
  return (
68
73
  <QueryClientProvider client={queryClient}>
69
74
  <HelmetProvider>
75
+ <Helmet>{heads}</Helmet>
70
76
  <DevPortalProvider value={devPortalContext}>
71
77
  <MDXProvider components={mdxComponents}>
72
78
  <ThemeProvider>
@@ -1,4 +1,4 @@
1
- import { useSuspenseQuery } from "@tanstack/react-query";
1
+ import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { createContext, useContext } from "react";
3
3
  import { matchPath, useLocation } from "react-router-dom";
4
4
  import { DevPortalContext } from "../../core/DevPortalContext.js";
@@ -21,7 +21,7 @@ export const useDevPortal = () => {
21
21
 
22
22
  export const useApiIdentities = () => {
23
23
  const { getApiIdentities } = useDevPortal();
24
- return useSuspenseQuery({
24
+ return useQuery({
25
25
  queryFn: getApiIdentities,
26
26
  queryKey: ["api-identities"],
27
27
  });
@@ -88,7 +88,7 @@ export class DevPortalContext {
88
88
  await Promise.all([
89
89
  this.plugins
90
90
  .filter(needsInitialization)
91
- .map((plugin) => plugin.initialize(this)),
91
+ .map((plugin) => plugin.initialize?.(this)),
92
92
  ]);
93
93
  };
94
94
 
@@ -1,3 +1,4 @@
1
+ import { type ReactElement } from "react";
1
2
  import { type RouteObject } from "react-router-dom";
2
3
  import {
3
4
  DevPortalContext,
@@ -10,9 +11,9 @@ export type PluginNavigationCategory = {
10
11
  } & NavigationCategory;
11
12
 
12
13
  export type DevPortalPlugin =
14
+ | CommonPlugin
13
15
  | NavigationPlugin
14
- | ApiIdentityPlugin
15
- | InitializationPlugin;
16
+ | ApiIdentityPlugin;
16
17
 
17
18
  export interface NavigationPlugin {
18
19
  getRoutes: () => RouteObject[];
@@ -23,8 +24,9 @@ export interface ApiIdentityPlugin {
23
24
  getIdentities: (context: DevPortalContext) => Promise<ApiIdentity[]>;
24
25
  }
25
26
 
26
- export interface InitializationPlugin {
27
- initialize: (context: DevPortalContext) => Promise<void> | void;
27
+ export interface CommonPlugin {
28
+ initialize?: (context: DevPortalContext) => Promise<void> | void;
29
+ getHead?: () => ReactElement | undefined;
28
30
  }
29
31
 
30
32
  export const isNavigationPlugin = (
@@ -34,9 +36,12 @@ export const isNavigationPlugin = (
34
36
 
35
37
  export const needsInitialization = (
36
38
  obj: DevPortalPlugin,
37
- ): obj is InitializationPlugin =>
39
+ ): obj is CommonPlugin =>
38
40
  "initialize" in obj && typeof obj.initialize === "function";
39
41
 
42
+ export const hasHead = (obj: DevPortalPlugin): obj is CommonPlugin =>
43
+ "getHead" in obj && typeof obj.getHead === "function";
44
+
40
45
  export const isApiIdentityPlugin = (
41
46
  obj: DevPortalPlugin,
42
47
  ): obj is ApiIdentityPlugin =>
@@ -45,7 +45,7 @@ export const slugifyOperation = (operation: OperationLike, tag?: string) => {
45
45
  );
46
46
  };
47
47
 
48
- const cache = new LRUCache<string, OpenAPIDocument>({
48
+ const cache = new LRUCache<string, Promise<OpenAPIDocument>>({
49
49
  ttl: 60 * 10 * 1000,
50
50
  ttlAutopurge: true,
51
51
  });
@@ -384,7 +384,7 @@ const loadOpenAPISchema = async (input: NonNullable<unknown>) => {
384
384
  return cache.get(hash)!;
385
385
  }
386
386
 
387
- const schema = await validate(input);
387
+ const schema = validate(input);
388
388
 
389
389
  cache.set(hash, schema);
390
390
 
@@ -54,7 +54,9 @@ const parseSchemaInput = async (
54
54
  return JSON.parse(schemaInput);
55
55
  }
56
56
  if (schemaInput.includes("://")) {
57
- const response = await fetch(schemaInput);
57
+ const response = await fetch(schemaInput, {
58
+ cache: "force-cache",
59
+ });
58
60
  return (await response.json()) as JSONSchema;
59
61
  }
60
62
  const yaml = await import("yaml");
@@ -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}
@@ -5,9 +5,9 @@ import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
5
5
  import type { SchemaObject } from "../../oas/parser/index.js";
6
6
  import { cn } from "../../util/cn.js";
7
7
  import { ColorizedParam } from "./ColorizedParam.js";
8
- import { MakeRequest } from "./MakeRequest.js";
9
8
  import { MethodTextColorMap } from "./MethodBadge.js";
10
9
  import type { OperationListItemResult } from "./OperationList.js";
10
+ import { PlaygroundDialogWrapper } from "./PlaygroundDialogWrapper.js";
11
11
  import { RequestBodySidecarBox } from "./RequestBodySidecarBox.js";
12
12
  import { ResponsesSidecarBox } from "./ResponsesSidecarBox.js";
13
13
  import * as SidecarBox from "./SidecarBox.js";
@@ -142,7 +142,7 @@ export const Sidecar = ({
142
142
  { value: "swift", label: "Swift" },
143
143
  ]}
144
144
  />
145
- <MakeRequest operation={operation} />
145
+ <PlaygroundDialogWrapper operation={operation} />
146
146
  </div>
147
147
  </SidecarBox.Head>
148
148
  <SidecarBox.Body>
@@ -15,7 +15,6 @@ import {
15
15
  } from "./util/urql.js";
16
16
 
17
17
  import { createSharedWorkerClient } from "virtual:zudoku-openapi-worker";
18
- import type { createSharedWorkerClient as createSharedWorkerClientType } from "./worker/createSharedWorkerClient.js";
19
18
 
20
19
  const OasContext = createContext<{ config: OasPluginConfig } | undefined>(
21
20
  undefined,
@@ -85,11 +84,25 @@ export const openApiPlugin = (config: OasPluginConfig): DevPortalPlugin => {
85
84
  url: config.server,
86
85
  exchanges: [cacheExchange, fetchExchange],
87
86
  })
88
- : (createSharedWorkerClient() as ReturnType<
89
- typeof createSharedWorkerClientType
90
- >);
87
+ : createSharedWorkerClient();
91
88
 
92
89
  return {
90
+ getHead: () => {
91
+ if (config.type === "url") {
92
+ return (
93
+ <link
94
+ rel="preload"
95
+ href={config.input}
96
+ as="fetch"
97
+ crossOrigin="anonymous"
98
+ />
99
+ );
100
+ }
101
+
102
+ if (config.server) {
103
+ return <link rel="preconnect" href={config.server} />;
104
+ }
105
+ },
93
106
  getNavigation: async (path: string) => {
94
107
  if (!matchPath({ path: basePath, end: false }, path)) {
95
108
  return [];
@@ -1,15 +1,7 @@
1
- import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
2
1
  import { useMutation } from "@tanstack/react-query";
3
- import { CirclePlayIcon } from "lucide-react";
4
2
  import { Fragment } from "react";
5
3
  import { FormProvider, useForm } from "react-hook-form";
6
4
  import { useApiIdentities } from "../../../components/context/DevPortalProvider.js";
7
- import {
8
- Dialog,
9
- DialogContent,
10
- DialogTitle,
11
- DialogTrigger,
12
- } from "../../../components/Dialog.js";
13
5
  import {
14
6
  Select,
15
7
  SelectContent,
@@ -28,6 +20,8 @@ import { Headers } from "./Headers.js";
28
20
  import { PathParams } from "./PathParams.js";
29
21
  import { QueryParams } from "./QueryParams.js";
30
22
 
23
+ export const NO_IDENTITY = "__none";
24
+
31
25
  function mimeTypeToLanguage(mimeType: string) {
32
26
  const mimeTypeMapping = {
33
27
  "application/json": "json",
@@ -81,17 +75,7 @@ export type PlaygroundForm = {
81
75
  identity?: string;
82
76
  };
83
77
 
84
- export const NO_IDENTITY = "__none";
85
-
86
- const Playground = ({
87
- url,
88
- host,
89
- method,
90
- headers = [{ name: "", value: "" }],
91
- queryParams = [],
92
- pathParams = [],
93
- hasParams,
94
- }: {
78
+ export type PlaygroundContentProps = {
95
79
  host: string;
96
80
  url: string;
97
81
  method: string;
@@ -99,7 +83,17 @@ const Playground = ({
99
83
  queryParams?: QueryParam[];
100
84
  pathParams?: PathParam[];
101
85
  hasParams: boolean;
102
- }) => {
86
+ };
87
+
88
+ export const Playground = ({
89
+ host,
90
+ url,
91
+ method,
92
+ headers = [{ name: "", value: "" }],
93
+ queryParams = [],
94
+ pathParams = [],
95
+ hasParams,
96
+ }: PlaygroundContentProps) => {
103
97
  const { register, control, handleSubmit, watch, ...form } =
104
98
  useForm<PlaygroundForm>({
105
99
  defaultValues: {
@@ -129,7 +123,7 @@ const Playground = ({
129
123
 
130
124
  if (data.identity !== NO_IDENTITY) {
131
125
  identities.data
132
- .find((i) => i.id === data.identity)
126
+ ?.find((i) => i.id === data.identity)
133
127
  ?.authorizeRequest(request);
134
128
  }
135
129
  const response = await fetch(request);
@@ -181,181 +175,157 @@ const Playground = ({
181
175
  <wbr />
182
176
  </Fragment>
183
177
  ));
184
-
185
178
  return (
186
- <Dialog>
187
- <DialogTrigger asChild>
188
- <CirclePlayIcon
189
- className="cursor-pointer text-primary hover:text-primary/80"
190
- size={16}
191
- />
192
- </DialogTrigger>
193
-
194
- <DialogContent
195
- className="max-w-screen-xl w-full h-5/6 overflow-auto p-0"
196
- aria-describedby={undefined}
197
- >
198
- <VisuallyHidden>
199
- <DialogTitle>Playground</DialogTitle>
200
- </VisuallyHidden>
201
- <FormProvider {...{ register, control, handleSubmit, watch, ...form }}>
202
- <form
203
- onSubmit={handleSubmit((data) => queryMutation.mutateAsync(data))}
204
- >
205
- <div className="grid grid-cols-2 text-sm h-full">
206
- <div className="flex flex-col gap-4 p-8 bg-muted/50 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
207
- <div className="flex gap-2 items-stretch">
208
- <div className="flex flex-1 items-center w-full border rounded-md border-border">
209
- <div className="border-r border-border p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
210
- {method.toUpperCase()}
211
- </div>
212
- <div className="p-2 font-mono text-xs">
213
- {path}
214
- {urlQueryParams.length > 0 ? "?" : ""}
215
- {urlQueryParams}
179
+ <FormProvider {...{ register, control, handleSubmit, watch, ...form }}>
180
+ <form onSubmit={handleSubmit((data) => queryMutation.mutateAsync(data))}>
181
+ <div className="grid grid-cols-2 text-sm h-full">
182
+ <div className="flex flex-col gap-4 p-8 bg-muted/50 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
183
+ <div className="flex gap-2 items-stretch">
184
+ <div className="flex flex-1 items-center w-full border rounded-md border-border">
185
+ <div className="border-r border-border p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
186
+ {method.toUpperCase()}
187
+ </div>
188
+ <div className="p-2 font-mono text-xs">
189
+ {path}
190
+ {urlQueryParams.length > 0 ? "?" : ""}
191
+ {urlQueryParams}
192
+ </div>
193
+ </div>
194
+ <Button type="submit" className="h-auto flex gap-1">
195
+ Send
196
+ </Button>
197
+ </div>
198
+ <Tabs defaultValue={hasParams ? "parameters" : "headers"}>
199
+ <div className="flex justify-between">
200
+ <TabsList>
201
+ {hasParams && (
202
+ <TabsTrigger value="parameters">Parameters</TabsTrigger>
203
+ )}
204
+ <TabsTrigger value="headers">
205
+ Headers{" "}
206
+ {formState.headers.length > 0 &&
207
+ `(${formState.headers.length})`}
208
+ </TabsTrigger>
209
+ <TabsTrigger
210
+ value="body"
211
+ disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
212
+ method.toUpperCase(),
213
+ )}
214
+ >
215
+ Body
216
+ </TabsTrigger>
217
+ </TabsList>
218
+ <div className="flex gap-2 items-center">
219
+ Auth:
220
+ <Select
221
+ onValueChange={(value) => form.setValue("identity", value)}
222
+ defaultValue={formState.identity}
223
+ >
224
+ <SelectTrigger className="w-[180px] flex">
225
+ {identities.isPending ? <Spinner /> : <SelectValue />}
226
+ </SelectTrigger>
227
+ <SelectContent align="center">
228
+ <SelectItem value={NO_IDENTITY}>None</SelectItem>
229
+ {identities.data?.map((identity) => (
230
+ <SelectItem key={identity.id} value={identity.id}>
231
+ {identity.label}
232
+ </SelectItem>
233
+ ))}
234
+ </SelectContent>
235
+ </Select>
236
+ </div>
237
+ </div>
238
+ <TabsContent value="headers">
239
+ <Headers control={control} register={register} />
240
+ </TabsContent>
241
+ <TabsContent value="parameters">
242
+ <div className="grid grid-cols-[min-content_1fr_1fr_auto]">
243
+ {pathParams.length > 0 && (
244
+ <div className="font-semibold my-3 col-span-3">
245
+ Path Parameters
216
246
  </div>
217
- </div>
218
- <Button type="submit" className="h-auto flex gap-1">
219
- Send
220
- </Button>
247
+ )}
248
+ <PathParams control={control} register={register} />
249
+ {queryParams.length > 0 && (
250
+ <span className="font-semibold my-3 col-span-3">
251
+ Query Parameters
252
+ </span>
253
+ )}
254
+ <QueryParams control={control} />
221
255
  </div>
222
- <Tabs defaultValue={hasParams ? "parameters" : "headers"}>
223
- <div className="flex justify-between">
224
- <TabsList>
225
- {hasParams && (
226
- <TabsTrigger value="parameters">Parameters</TabsTrigger>
227
- )}
228
- <TabsTrigger value="headers">
229
- Headers{" "}
230
- {formState.headers.length > 0 &&
231
- `(${formState.headers.length})`}
232
- </TabsTrigger>
233
- <TabsTrigger
234
- value="body"
235
- disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
236
- method.toUpperCase(),
237
- )}
238
- >
239
- Body
240
- </TabsTrigger>
241
- </TabsList>
242
- <div className="flex gap-2 items-center">
243
- Auth:
244
- <Select
245
- onValueChange={(value) =>
246
- form.setValue("identity", value)
247
- }
248
- defaultValue={formState.identity}
249
- >
250
- <SelectTrigger className="w-[180px]">
251
- <SelectValue />
252
- </SelectTrigger>
253
- <SelectContent align="center">
254
- <SelectItem value={NO_IDENTITY}>None</SelectItem>
255
- {identities.data.map((identity) => (
256
- <SelectItem key={identity.id} value={identity.id}>
257
- {identity.label}
258
- </SelectItem>
259
- ))}
260
- </SelectContent>
261
- </Select>
256
+ </TabsContent>
257
+ <TabsContent value="body">
258
+ <textarea
259
+ {...register("body")}
260
+ className="border border-border w-full rounded p-2 bg-muted h-40"
261
+ />
262
+ </TabsContent>
263
+ </Tabs>
264
+ </div>
265
+ <div className="flex flex-col gap-4 p-8 bg-muted/70">
266
+ {queryMutation.error ? (
267
+ <div>{queryMutation.error.message}</div>
268
+ ) : queryMutation.data ? (
269
+ <div className="flex flex-col gap-2">
270
+ <div className="flex gap-2">
271
+ <div className="flex text-xs gap-6">
272
+ <div>
273
+ Status: {queryMutation.data.status}{" "}
274
+ {statusCodeMap[queryMutation.data.status] ?? ""}
262
275
  </div>
276
+ <div>Time: {queryMutation.data.time.toFixed(0)}ms</div>
277
+ <div>Size: {queryMutation.data.size} B</div>
263
278
  </div>
264
- <TabsContent value="headers">
265
- <Headers control={control} register={register} />
266
- </TabsContent>
267
- <TabsContent value="parameters">
268
- <div className="grid grid-cols-[min-content_1fr_1fr_auto]">
269
- {pathParams.length > 0 && (
270
- <div className="font-semibold my-3 col-span-3">
271
- Path Parameters
272
- </div>
273
- )}
274
- <PathParams control={control} register={register} />
275
- {queryParams.length > 0 && (
276
- <span className="font-semibold my-3 col-span-3">
277
- Query Parameters
278
- </span>
279
- )}
280
- <QueryParams control={control} />
281
- </div>
279
+ </div>
280
+ {/*<UrlDisplay host={host} path={url} />*/}
281
+ <Tabs defaultValue="response">
282
+ <TabsList>
283
+ <TabsTrigger value="response">Response</TabsTrigger>
284
+ <TabsTrigger value="headers">
285
+ {headerEntries.length
286
+ ? `Headers (${headerEntries.length})`
287
+ : "No headers"}
288
+ </TabsTrigger>
289
+ </TabsList>
290
+
291
+ <TabsContent value="response">
292
+ <Card className="shadow-none p-4">
293
+ <SyntaxHighlight
294
+ language={lang ?? "json"}
295
+ noBackground
296
+ className="overflow-x-auto "
297
+ code={queryMutation.data.body ?? JSON.stringify("")}
298
+ />
299
+ </Card>
282
300
  </TabsContent>
283
- <TabsContent value="body">
284
- <textarea
285
- {...register("body")}
286
- className="border border-border w-full rounded p-2 bg-muted h-40"
287
- />
301
+ <TabsContent value="headers">
302
+ <Card className="grid grid-cols-2 w-full gap-2.5 font-mono text-xs shadow-none p-4">
303
+ <div className="font-semibold">Key</div>
304
+ <div className="font-semibold">Value</div>
305
+ {headerEntries.map(([key, value]) => (
306
+ <Fragment key={key}>
307
+ <div>{key}</div>
308
+ <div>{value}</div>
309
+ </Fragment>
310
+ ))}
311
+ </Card>
288
312
  </TabsContent>
289
313
  </Tabs>
290
314
  </div>
291
- <div className="flex flex-col gap-4 p-8 bg-muted/70">
292
- {queryMutation.error ? (
293
- <div>{queryMutation.error.message}</div>
294
- ) : queryMutation.data ? (
295
- <div className="flex flex-col gap-2">
296
- <div className="flex gap-2">
297
- <div className="flex text-xs gap-6">
298
- <div>
299
- Status: {queryMutation.data.status}{" "}
300
- {statusCodeMap[queryMutation.data.status] ?? ""}
301
- </div>
302
- <div>Time: {queryMutation.data.time.toFixed(0)}ms</div>
303
- <div>Size: {queryMutation.data.size} B</div>
304
- </div>
305
- </div>
306
- {/*<UrlDisplay host={host} path={url} />*/}
307
- <Tabs defaultValue="response">
308
- <TabsList>
309
- <TabsTrigger value="response">Response</TabsTrigger>
310
- <TabsTrigger value="headers">
311
- {headerEntries.length
312
- ? `Headers (${headerEntries.length})`
313
- : "No headers"}
314
- </TabsTrigger>
315
- </TabsList>
316
-
317
- <TabsContent value="response">
318
- <Card className="shadow-none p-4">
319
- <SyntaxHighlight
320
- language={lang ?? "json"}
321
- noBackground
322
- className="overflow-x-auto "
323
- code={queryMutation.data.body ?? JSON.stringify("")}
324
- />
325
- </Card>
326
- </TabsContent>
327
- <TabsContent value="headers">
328
- <Card className="grid grid-cols-2 w-full gap-2.5 font-mono text-xs shadow-none p-4">
329
- <div className="font-semibold">Key</div>
330
- <div className="font-semibold">Value</div>
331
- {headerEntries.map(([key, value]) => (
332
- <Fragment key={key}>
333
- <div>{key}</div>
334
- <div>{value}</div>
335
- </Fragment>
336
- ))}
337
- </Card>
338
- </TabsContent>
339
- </Tabs>
340
- </div>
341
- ) : (
342
- <div className="grid place-items-center h-full">
343
- <span className="text-[16px] font-semibold text-muted-foreground">
344
- {queryMutation.isPending ? (
345
- <Spinner />
346
- ) : (
347
- "Send a request first to see the response here"
348
- )}
349
- </span>
350
- </div>
351
- )}
315
+ ) : (
316
+ <div className="grid place-items-center h-full">
317
+ <span className="text-[16px] font-semibold text-muted-foreground">
318
+ {queryMutation.isPending ? (
319
+ <Spinner />
320
+ ) : (
321
+ "Send a request first to see the response here"
322
+ )}
323
+ </span>
352
324
  </div>
353
- </div>
354
- </form>
355
- </FormProvider>
356
- </DialogContent>
357
- </Dialog>
325
+ )}
326
+ </div>
327
+ </div>
328
+ </form>
329
+ </FormProvider>
358
330
  );
359
331
  };
360
-
361
- export { Playground };