zudoku 0.26.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/config/config.d.ts +1 -0
  2. package/dist/config/loader.js +1 -1
  3. package/dist/config/loader.js.map +1 -1
  4. package/dist/config/validators/common.d.ts +11 -0
  5. package/dist/config/validators/common.js +1 -0
  6. package/dist/config/validators/common.js.map +1 -1
  7. package/dist/config/validators/validate.d.ts +5 -0
  8. package/dist/lib/authentication/providers/auth0.js +1 -1
  9. package/dist/lib/authentication/providers/auth0.js.map +1 -1
  10. package/dist/lib/authentication/providers/openid.d.ts +1 -1
  11. package/dist/lib/authentication/providers/openid.js +10 -6
  12. package/dist/lib/authentication/providers/openid.js.map +1 -1
  13. package/dist/lib/components/Autocomplete.d.ts +12 -0
  14. package/dist/lib/components/Autocomplete.js +47 -0
  15. package/dist/lib/components/Autocomplete.js.map +1 -0
  16. package/dist/lib/components/Header.js +3 -3
  17. package/dist/lib/components/Header.js.map +1 -1
  18. package/dist/lib/components/index.js +2 -2
  19. package/dist/lib/components/index.js.map +1 -1
  20. package/dist/lib/plugins/markdown/MdxPage.js +8 -2
  21. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  22. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.d.ts +3 -1
  23. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js +3 -2
  24. package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.js.map +1 -1
  25. package/dist/lib/plugins/openapi/Sidecar.js +1 -1
  26. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  27. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.d.ts +6 -0
  28. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js +12 -0
  29. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js.map +1 -0
  30. package/dist/lib/plugins/openapi/playground/Headers.js +66 -4
  31. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  32. package/dist/lib/plugins/openapi/playground/Playground.d.ts +5 -1
  33. package/dist/lib/plugins/openapi/playground/Playground.js +36 -11
  34. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  35. package/dist/lib/plugins/openapi/playground/QueryParams.js +20 -30
  36. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  37. package/dist/lib/plugins/openapi/post-processors/removeExtensions.d.ts +2 -1
  38. package/dist/lib/plugins/openapi/post-processors/removeExtensions.js +5 -3
  39. package/dist/lib/plugins/openapi/post-processors/removeExtensions.js.map +1 -1
  40. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js +49 -0
  41. package/dist/lib/plugins/openapi/post-processors/removeExtensions.test.js.map +1 -1
  42. package/dist/lib/plugins/openapi/post-processors/removeParameters.d.ts +10 -0
  43. package/dist/lib/plugins/openapi/post-processors/removeParameters.js +66 -0
  44. package/dist/lib/plugins/openapi/post-processors/removeParameters.js.map +1 -0
  45. package/dist/lib/plugins/openapi/post-processors/removeParameters.test.d.ts +1 -0
  46. package/dist/lib/plugins/openapi/post-processors/removeParameters.test.js +131 -0
  47. package/dist/lib/plugins/openapi/post-processors/removeParameters.test.js.map +1 -0
  48. package/dist/lib/ui/Command.d.ts +9 -1
  49. package/dist/lib/ui/Command.js +5 -1
  50. package/dist/lib/ui/Command.js.map +1 -1
  51. package/dist/lib/util/joinUrl.d.ts +1 -0
  52. package/dist/lib/util/joinUrl.js +40 -0
  53. package/dist/lib/util/joinUrl.js.map +1 -0
  54. package/dist/vite/build.js +10 -10
  55. package/dist/vite/build.js.map +1 -1
  56. package/dist/vite/config.js +4 -1
  57. package/dist/vite/config.js.map +1 -1
  58. package/dist/vite/dev-server.js +4 -1
  59. package/dist/vite/dev-server.js.map +1 -1
  60. package/dist/vite/plugin-api.d.ts +1 -1
  61. package/dist/vite/plugin-api.js +23 -5
  62. package/dist/vite/plugin-api.js.map +1 -1
  63. package/dist/vite/plugin-auth.js +4 -1
  64. package/dist/vite/plugin-auth.js.map +1 -1
  65. package/dist/vite/plugin-mdx.js +9 -4
  66. package/dist/vite/plugin-mdx.js.map +1 -1
  67. package/dist/vite/prerender.d.ts +2 -2
  68. package/dist/vite/prerender.js +4 -4
  69. package/dist/vite/prerender.js.map +1 -1
  70. package/dist/zuplo/enrich-with-zuplo.d.ts +5 -0
  71. package/dist/zuplo/enrich-with-zuplo.js +184 -0
  72. package/dist/zuplo/enrich-with-zuplo.js.map +1 -0
  73. package/dist/zuplo/env.d.ts +1 -0
  74. package/dist/zuplo/env.js +3 -0
  75. package/dist/zuplo/env.js.map +1 -1
  76. package/dist/zuplo/policy-types.d.ts +33 -0
  77. package/dist/zuplo/policy-types.js +8 -0
  78. package/dist/zuplo/policy-types.js.map +1 -0
  79. package/dist/zuplo/with-zuplo-processors.d.ts +3 -0
  80. package/dist/zuplo/with-zuplo-processors.js +26 -0
  81. package/dist/zuplo/with-zuplo-processors.js.map +1 -0
  82. package/dist/zuplo/with-zuplo.d.ts +1 -2
  83. package/dist/zuplo/with-zuplo.js +6 -27
  84. package/dist/zuplo/with-zuplo.js.map +1 -1
  85. package/lib/{AnchorLink-_Vu02ceN.js → AnchorLink-bObQitZv.js} +2 -2
  86. package/lib/{AnchorLink-_Vu02ceN.js.map → AnchorLink-bObQitZv.js.map} +1 -1
  87. package/lib/{AuthenticationPlugin-DNXBcsVN.js → AuthenticationPlugin-C9SwOxkc.js} +3 -3
  88. package/lib/{AuthenticationPlugin-DNXBcsVN.js.map → AuthenticationPlugin-C9SwOxkc.js.map} +1 -1
  89. package/lib/{Markdown-BrfrjEk_.js → Markdown-DFN6p0J-.js} +2 -2
  90. package/lib/{Markdown-BrfrjEk_.js.map → Markdown-DFN6p0J-.js.map} +1 -1
  91. package/lib/{MdxPage-LNZLj_A5.js → MdxPage-D9c4z09Q.js} +63 -58
  92. package/lib/MdxPage-D9c4z09Q.js.map +1 -0
  93. package/lib/{OperationList-PCwzTp1r.js → OperationList-DGJWDx1G.js} +875 -871
  94. package/lib/{OperationList-PCwzTp1r.js.map → OperationList-DGJWDx1G.js.map} +1 -1
  95. package/lib/{Route-Pzk6qwIk.js → Route-VdmEyOD0.js} +3 -3
  96. package/lib/{Route-Pzk6qwIk.js.map → Route-VdmEyOD0.js.map} +1 -1
  97. package/lib/{Select-DkOpAG0c.js → Select-D3O7wISy.js} +3 -3
  98. package/lib/{Select-DkOpAG0c.js.map → Select-D3O7wISy.js.map} +1 -1
  99. package/lib/{SlotletProvider-DPbx9KdU.js → SlotletProvider-_3zzX_g_.js} +4 -4
  100. package/lib/{SlotletProvider-DPbx9KdU.js.map → SlotletProvider-_3zzX_g_.js.map} +1 -1
  101. package/lib/{Button-oroWHXAy.js → Spinner-BlzrEEk1.js} +15 -12
  102. package/lib/Spinner-BlzrEEk1.js.map +1 -0
  103. package/lib/{ZudokuContext-D3ayHjP-.js → ZudokuContext-DeQZEp-x.js} +2 -2
  104. package/lib/{ZudokuContext-D3ayHjP-.js.map → ZudokuContext-DeQZEp-x.js.map} +1 -1
  105. package/lib/{chunk-SYFQ2XB5-KWlHsT7t.js → chunk-SYFQ2XB5-BF5IDYrB.js} +6 -5
  106. package/lib/{chunk-SYFQ2XB5-KWlHsT7t.js.map → chunk-SYFQ2XB5-BF5IDYrB.js.map} +1 -1
  107. package/lib/{hook-DUyACbIK.js → hook-BRQEDRbn.js} +2 -2
  108. package/lib/{hook-DUyACbIK.js.map → hook-BRQEDRbn.js.map} +1 -1
  109. package/lib/index-B7mqiOei.js +509 -0
  110. package/lib/index-B7mqiOei.js.map +1 -0
  111. package/lib/index-CXRrqOIl.js +1750 -0
  112. package/lib/index-CXRrqOIl.js.map +1 -0
  113. package/lib/joinUrl-BTy9bvoK.js +20 -0
  114. package/lib/joinUrl-BTy9bvoK.js.map +1 -0
  115. package/lib/post-processors/removeExtensions.js +7 -7
  116. package/lib/post-processors/removeExtensions.js.map +1 -1
  117. package/lib/post-processors/removeParameters.js +48 -0
  118. package/lib/post-processors/removeParameters.js.map +1 -0
  119. package/lib/ui/ActionButton.js +10 -11
  120. package/lib/ui/ActionButton.js.map +1 -1
  121. package/lib/ui/Command.js +125 -13
  122. package/lib/ui/Command.js.map +1 -1
  123. package/lib/{useExposedProps-BBHR7aLM.js → useExposedProps-CetwhZpP.js} +2 -2
  124. package/lib/{useExposedProps-BBHR7aLM.js.map → useExposedProps-CetwhZpP.js.map} +1 -1
  125. package/lib/zudoku.auth-auth0.js +7 -9
  126. package/lib/zudoku.auth-auth0.js.map +1 -1
  127. package/lib/zudoku.auth-clerk.js +1 -1
  128. package/lib/zudoku.auth-openid.js +223 -219
  129. package/lib/zudoku.auth-openid.js.map +1 -1
  130. package/lib/zudoku.components.js +219 -219
  131. package/lib/zudoku.components.js.map +1 -1
  132. package/lib/zudoku.plugin-api-catalog.js +3 -3
  133. package/lib/zudoku.plugin-api-keys.js +5 -5
  134. package/lib/zudoku.plugin-custom-pages.js +2 -2
  135. package/lib/zudoku.plugin-markdown.js +1 -1
  136. package/lib/zudoku.plugin-openapi.js +4 -4
  137. package/lib/zudoku.plugin-redirect.js +1 -1
  138. package/package.json +1 -1
  139. package/src/app/main.css +50 -50
  140. package/src/lib/authentication/providers/auth0.tsx +1 -4
  141. package/src/lib/authentication/providers/openid.tsx +12 -5
  142. package/src/lib/components/Autocomplete.tsx +111 -0
  143. package/src/lib/components/Header.tsx +3 -3
  144. package/src/lib/components/index.ts +2 -2
  145. package/src/lib/plugins/markdown/MdxPage.tsx +9 -1
  146. package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +5 -0
  147. package/src/lib/plugins/openapi/Sidecar.tsx +1 -0
  148. package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +51 -0
  149. package/src/lib/plugins/openapi/playground/Headers.tsx +138 -41
  150. package/src/lib/plugins/openapi/playground/Playground.tsx +156 -62
  151. package/src/lib/plugins/openapi/playground/QueryParams.tsx +89 -122
  152. package/src/lib/plugins/openapi/post-processors/removeExtensions.test.ts +58 -0
  153. package/src/lib/plugins/openapi/post-processors/removeExtensions.ts +7 -4
  154. package/src/lib/plugins/openapi/post-processors/removeParameters.test.ts +148 -0
  155. package/src/lib/plugins/openapi/post-processors/removeParameters.ts +101 -0
  156. package/src/lib/ui/Command.tsx +20 -0
  157. package/src/lib/util/joinUrl.ts +57 -0
  158. package/dist/lib/plugins/openapi/playground/EnumSelector.d.ts +0 -8
  159. package/dist/lib/plugins/openapi/playground/EnumSelector.js +0 -21
  160. package/dist/lib/plugins/openapi/playground/EnumSelector.js.map +0 -1
  161. package/lib/Button-oroWHXAy.js.map +0 -1
  162. package/lib/Command-D5DE0DD7.js +0 -611
  163. package/lib/Command-D5DE0DD7.js.map +0 -1
  164. package/lib/MdxPage-LNZLj_A5.js.map +0 -1
  165. package/lib/Spinner-C5gHXrVz.js +0 -7
  166. package/lib/Spinner-C5gHXrVz.js.map +0 -1
  167. package/lib/index-CaILD1AV.js +0 -1292
  168. package/lib/index-CaILD1AV.js.map +0 -1
  169. package/src/lib/plugins/openapi/playground/EnumSelector.tsx +0 -86
@@ -1,9 +1,48 @@
1
1
  import { XIcon } from "lucide-react";
2
- import { Control, useFieldArray, UseFormRegister } from "react-hook-form";
2
+ import { useRef } from "react";
3
+ import {
4
+ Control,
5
+ Controller,
6
+ useFieldArray,
7
+ useFormContext,
8
+ UseFormRegister,
9
+ } from "react-hook-form";
10
+ import { Card } from "zudoku/ui/Card.js";
11
+ import { Checkbox } from "zudoku/ui/Checkbox.js";
12
+ import { Autocomplete } from "../../../components/Autocomplete.js";
3
13
  import { Button } from "../../../ui/Button.js";
4
14
  import { Input } from "../../../ui/Input.js";
5
15
  import { type PlaygroundForm } from "./Playground.js";
6
16
 
17
+ const headerOptions = Object.freeze([
18
+ "Accept",
19
+ "Accept-Encoding",
20
+ "Accept-Language",
21
+ "Authorization",
22
+ "Cache-Control",
23
+ "Connection",
24
+ "Content-Disposition",
25
+ "Content-Encoding",
26
+ "Content-Language",
27
+ "Content-Length",
28
+ "Content-Range",
29
+ "Content-Security-Policy",
30
+ "Content-Type",
31
+ "Cookie",
32
+ "Date",
33
+ "ETag",
34
+ "Expires",
35
+ "Host",
36
+ "If-Modified-Since",
37
+ "Location",
38
+ "Origin",
39
+ "Pragma",
40
+ "Referer",
41
+ "Set-Cookie",
42
+ "User-Agent",
43
+ "X-Requested-With",
44
+ ]);
45
+
7
46
  export const Headers = ({
8
47
  control,
9
48
  register,
@@ -15,53 +54,111 @@ export const Headers = ({
15
54
  control,
16
55
  name: "headers",
17
56
  });
57
+ const { setValue } = useFormContext<PlaygroundForm>();
58
+ const valueRefs = useRef<Array<HTMLInputElement | null>>([]);
59
+ const nameRefs = useRef<Array<HTMLInputElement | null>>([]);
60
+
61
+ const addNewHeader = () => {
62
+ append({
63
+ name: "",
64
+ value: "",
65
+ active: false,
66
+ } as PlaygroundForm["headers"][number]);
67
+ };
68
+
69
+ const handleHeaderEnter = (index: number) => {
70
+ valueRefs.current[index]?.focus();
71
+ };
72
+
73
+ const handleValueEnter = (index: number) => {
74
+ addNewHeader();
75
+ requestAnimationFrame(() => nameRefs.current[index + 1]?.focus());
76
+ };
18
77
 
19
78
  return (
20
79
  <div className="flex flex-col gap-2">
21
- <table className="w-full [&_td]:border [&_td]:p-1.5 [&_td]:px-2">
22
- <tbody>
23
- {fields.map((header, i) => (
24
- <tr
25
- key={header.id}
26
- className="group has-[:focus]:bg-muted/50 hover:bg-muted/50"
27
- >
28
- <td className="flex gap-2 items-center">
29
- <Input
30
- {...register(`headers.${i}.name`)}
31
- placeholder="Name"
32
- className="border-0 shadow-none text-xs font-mono"
33
- autoComplete="off"
34
- />
35
- </td>
36
- <td>
37
- <div className="flex items-center gap-2">
38
- <Input
39
- placeholder={"Value"}
40
- className="border-0 shadow-none text-xs font-mono"
41
- {...register(`headers.${i}.value`)}
42
- autoComplete="off"
80
+ <Card className="flex flex-col gap-2 overflow-hidden">
81
+ <table className="w-full">
82
+ <tbody>
83
+ {fields.map((header, i) => (
84
+ <tr
85
+ key={header.id}
86
+ className="group has-[:focus]:bg-muted/50 hover:bg-muted/50"
87
+ >
88
+ <td className="flex gap-2 items-center pl-3">
89
+ <Controller
90
+ control={control}
91
+ name={`headers.${i}.active`}
92
+ render={({ field }) => (
93
+ <Checkbox
94
+ variant="outline"
95
+ id={`headers.${i}.active`}
96
+ checked={field.value}
97
+ onCheckedChange={(checked) => {
98
+ field.onChange(checked);
99
+ }}
100
+ />
101
+ )}
102
+ />
103
+ <Controller
104
+ control={control}
105
+ name={`headers.${i}.name`}
106
+ render={({ field }) => (
107
+ <Autocomplete
108
+ {...field}
109
+ placeholder="Name"
110
+ className="border-0 shadow-none bg-transparent text-xs font-mono"
111
+ options={headerOptions}
112
+ onEnterPress={() => handleHeaderEnter(i)}
113
+ onChange={(e) => {
114
+ field.onChange(e);
115
+ setValue(`headers.${i}.active`, true);
116
+ }}
117
+ ref={(el) => {
118
+ nameRefs.current[i] = el;
119
+ }}
120
+ />
121
+ )}
43
122
  />
44
- <Button
45
- size="icon"
46
- variant="ghost"
47
- className="text-muted-foreground opacity-0 group-hover:opacity-100"
48
- onClick={() => {
49
- remove(i);
50
- }}
51
- type="button"
52
- >
53
- <XIcon size={16} />
54
- </Button>
55
- </div>
56
- </td>
57
- </tr>
58
- ))}
59
- </tbody>
60
- </table>
123
+ </td>
124
+ <td>
125
+ <div className="flex items-center gap-2">
126
+ <Input
127
+ placeholder="Value"
128
+ className="w-full border-0 shadow-none text-xs font-mono"
129
+ {...register(`headers.${i}.value`)}
130
+ ref={(el) => {
131
+ valueRefs.current[i] = el;
132
+ }}
133
+ onKeyDown={(e) => {
134
+ if (e.key === "Enter" && e.currentTarget.value.trim()) {
135
+ handleValueEnter(i);
136
+ }
137
+ }}
138
+ autoComplete="off"
139
+ />
140
+ <Button
141
+ size="icon"
142
+ variant="ghost"
143
+ className="text-muted-foreground opacity-0 group-hover:opacity-100"
144
+ onClick={() => {
145
+ remove(i);
146
+ }}
147
+ type="button"
148
+ >
149
+ <XIcon size={16} />
150
+ </Button>
151
+ </div>
152
+ </td>
153
+ </tr>
154
+ ))}
155
+ </tbody>
156
+ </table>
157
+ </Card>
61
158
  <div className="text-end">
62
159
  <Button
63
160
  className=""
64
- onClick={() => append({ name: "", value: "" })}
161
+ onClick={addNewHeader}
65
162
  type="button"
66
163
  variant="secondary"
67
164
  >
@@ -1,6 +1,10 @@
1
1
  import { useMutation } from "@tanstack/react-query";
2
+ import { InfoIcon } from "lucide-react";
2
3
  import { Fragment, useEffect, useRef, useTransition } from "react";
3
4
  import { FormProvider, useForm } from "react-hook-form";
5
+ import { Alert, AlertDescription, AlertTitle } from "zudoku/ui/Alert.js";
6
+ import { Label } from "zudoku/ui/Label.js";
7
+ import { RadioGroup, RadioGroupItem } from "zudoku/ui/RadioGroup.js";
4
8
  import {
5
9
  Select,
6
10
  SelectContent,
@@ -8,6 +12,7 @@ import {
8
12
  SelectTrigger,
9
13
  SelectValue,
10
14
  } from "zudoku/ui/Select.js";
15
+ import { Textarea } from "zudoku/ui/Textarea.js";
11
16
  import { useSelectedServerStore } from "../../../authentication/state.js";
12
17
  import { useApiIdentities } from "../../../components/context/ZudokuContext.js";
13
18
  import { Spinner } from "../../../components/Spinner.js";
@@ -15,8 +20,11 @@ import { Button } from "../../../ui/Button.js";
15
20
  import { Callout } from "../../../ui/Callout.js";
16
21
  import { Card, CardContent, CardHeader, CardTitle } from "../../../ui/Card.js";
17
22
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
23
+ import { cn } from "../../../util/cn.js";
18
24
  import { ColorizedParam } from "../ColorizedParam.js";
25
+ import { Content } from "../SidecarExamples.js";
19
26
  import { createUrl } from "./createUrl.js";
27
+ import ExamplesDropdown from "./ExamplesDropdown.js";
20
28
  import { Headers } from "./Headers.js";
21
29
  import { PathParams } from "./PathParams.js";
22
30
  import { QueryParams } from "./QueryParams.js";
@@ -40,6 +48,7 @@ const statusCodeMap: Record<number, string> = {
40
48
  export type Header = {
41
49
  name: string;
42
50
  defaultValue?: string;
51
+ defaultActive?: boolean;
43
52
  };
44
53
  export type QueryParam = {
45
54
  name: string;
@@ -67,6 +76,7 @@ export type PlaygroundForm = {
67
76
  headers: Array<{
68
77
  name: string;
69
78
  value: string;
79
+ active: boolean;
70
80
  }>;
71
81
  identity?: string;
72
82
  };
@@ -80,6 +90,7 @@ export type PlaygroundContentProps = {
80
90
  queryParams?: QueryParam[];
81
91
  pathParams?: PathParam[];
82
92
  defaultBody?: string;
93
+ examples?: Content;
83
94
  };
84
95
 
85
96
  export const Playground = ({
@@ -91,6 +102,7 @@ export const Playground = ({
91
102
  queryParams = [],
92
103
  pathParams = [],
93
104
  defaultBody = "",
105
+ examples,
94
106
  }: PlaygroundContentProps) => {
95
107
  const { selectedServer, setSelectedServer } = useSelectedServerStore();
96
108
  const [, startTransition] = useTransition();
@@ -98,20 +110,38 @@ export const Playground = ({
98
110
  useForm<PlaygroundForm>({
99
111
  defaultValues: {
100
112
  body: defaultBody,
101
- queryParams: queryParams.map((param) => ({
102
- name: param.name,
103
- value: param.defaultValue ?? "",
104
- active: param.defaultActive ?? false,
105
- enum: param.enum ?? [],
106
- })),
113
+ queryParams: queryParams
114
+ .map((param) => ({
115
+ name: param.name,
116
+ value: param.defaultValue ?? "",
117
+ active: param.defaultActive ?? false,
118
+ enum: param.enum ?? [],
119
+ }))
120
+ .concat([
121
+ {
122
+ name: "",
123
+ value: "",
124
+ active: false,
125
+ enum: [],
126
+ },
127
+ ]),
107
128
  pathParams: pathParams.map((param) => ({
108
129
  name: param.name,
109
130
  value: param.defaultValue ?? "",
110
131
  })),
111
- headers: headers.map((header) => ({
112
- name: header.name,
113
- value: header.defaultValue ?? "",
114
- })),
132
+ headers: headers
133
+ .map((header) => ({
134
+ name: header.name,
135
+ value: header.defaultValue ?? "",
136
+ active: header.defaultActive ?? false,
137
+ }))
138
+ .concat([
139
+ {
140
+ name: "",
141
+ value: "",
142
+ active: false,
143
+ },
144
+ ]),
115
145
  identity: NO_IDENTITY,
116
146
  },
117
147
  });
@@ -137,7 +167,7 @@ export const Playground = ({
137
167
  method: method.toUpperCase(),
138
168
  headers: Object.fromEntries(
139
169
  data.headers
140
- .filter((h) => h.name)
170
+ .filter((h) => h.name && h.active)
141
171
  .map((header) => [header.name, header.value]),
142
172
  ),
143
173
  body: data.body ? data.body : undefined,
@@ -229,6 +259,7 @@ export const Playground = ({
229
259
  });
230
260
  }}
231
261
  value={selectedServer}
262
+ defaultValue={selectedServer}
232
263
  >
233
264
  <SelectTrigger className="p-0 border-none flex-row-reverse bg-transparent text-xs gap-0.5 h-auto">
234
265
  <SelectValue />
@@ -256,7 +287,7 @@ export const Playground = ({
256
287
  <div className="flex flex-col gap-4 p-4 after:bg-muted-foreground/20 relative after:absolute after:w-px after:inset-0 after:left-auto">
257
288
  <div className="flex gap-2 items-stretch">
258
289
  <div className="flex flex-1 items-center w-full border rounded-md">
259
- <div className="border-r p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono">
290
+ <div className="border-r p-2 bg-muted rounded-l-md self-stretch font-semibold font-mono flex items-center">
260
291
  {method.toUpperCase()}
261
292
  </div>
262
293
  <div className="flex items-center flex-wrap p-2 font-mono text-xs">
@@ -270,54 +301,30 @@ export const Playground = ({
270
301
  Send
271
302
  </Button>
272
303
  </div>
273
- <Tabs
274
- defaultValue={
275
- queryParams.length + pathParams.length > 0
276
- ? "parameters"
277
- : "headers"
278
- }
279
- >
304
+ <Tabs defaultValue="parameters">
280
305
  <div className="flex flex-wrap gap-1 justify-between">
281
306
  <TabsList>
282
- {queryParams.length + pathParams.length > 0 && (
283
- <TabsTrigger value="parameters">Parameters</TabsTrigger>
284
- )}
307
+ <TabsTrigger value="parameters">
308
+ Parameters
309
+ {(formState.pathParams.some((p) => p.value !== "") ||
310
+ formState.queryParams.some((p) => p.active)) && (
311
+ <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
312
+ )}
313
+ </TabsTrigger>
285
314
  <TabsTrigger value="headers">
286
- Headers{" "}
287
- {formState.headers.length > 0 &&
288
- `(${formState.headers.length})`}
315
+ Headers
316
+ {formState.headers.filter((h) => h.active).length > 0 && (
317
+ <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
318
+ )}
289
319
  </TabsTrigger>
290
- <TabsTrigger
291
- value="body"
292
- disabled={
293
- !["POST", "PUT", "PATCH", "DELETE"].includes(
294
- method.toUpperCase(),
295
- )
296
- }
297
- >
298
- Body
320
+ <TabsTrigger value="auth">
321
+ Auth
322
+ {formState.identity !== NO_IDENTITY && (
323
+ <div className="w-2 h-2 rounded-full bg-blue-400 ml-2" />
324
+ )}
299
325
  </TabsTrigger>
326
+ <TabsTrigger value="body">Body</TabsTrigger>
300
327
  </TabsList>
301
- <div className="flex gap-2 items-center">
302
- Auth:
303
- <Select
304
- onValueChange={(value) => setValue("identity", value)}
305
- value={formState.identity}
306
- defaultValue={formState.identity}
307
- >
308
- <SelectTrigger className="w-[180px] flex">
309
- {identities.isPending ? <Spinner /> : <SelectValue />}
310
- </SelectTrigger>
311
- <SelectContent align="center">
312
- <SelectItem value={NO_IDENTITY}>None</SelectItem>
313
- {identities.data?.map((identity) => (
314
- <SelectItem key={identity.id} value={identity.id}>
315
- {identity.label}
316
- </SelectItem>
317
- ))}
318
- </SelectContent>
319
- </Select>
320
- </div>
321
328
  </div>
322
329
  <TabsContent value="headers">
323
330
  <Headers control={control} register={register} />
@@ -329,18 +336,105 @@ export const Playground = ({
329
336
  <PathParams control={control} />
330
337
  </div>
331
338
  )}
332
- {queryParams.length > 0 && (
333
- <div className="flex flex-col gap-4 my-4">
334
- <span className="font-semibold">Query Parameters</span>
335
- <QueryParams control={control} queryParams={queryParams} />
336
- </div>
337
- )}
339
+ <div className="flex flex-col gap-4 my-4">
340
+ <span className="font-semibold">Query Parameters</span>
341
+ <QueryParams control={control} queryParams={queryParams} />
342
+ </div>
338
343
  </TabsContent>
339
344
  <TabsContent value="body">
340
- <textarea
345
+ {!["POST", "PUT", "PATCH", "DELETE"].includes(
346
+ method.toUpperCase(),
347
+ ) && (
348
+ <Alert className="mb-2">
349
+ <InfoIcon className="w-4 h-4" />
350
+ <AlertTitle>Body</AlertTitle>
351
+ <AlertDescription>
352
+ Body is only supported for POST, PUT, PATCH, and DELETE
353
+ requests
354
+ </AlertDescription>
355
+ </Alert>
356
+ )}
357
+ <Textarea
341
358
  {...register("body")}
342
- className="border w-full rounded p-2 bg-muted h-40"
359
+ className={cn(
360
+ "border w-full rounded-lg p-2 bg-muted h-40 font-mono",
361
+ !["POST", "PUT", "PATCH", "DELETE"].includes(
362
+ method.toUpperCase(),
363
+ ) && "h-20",
364
+ )}
365
+ placeholder={
366
+ !["POST", "PUT", "PATCH", "DELETE"].includes(
367
+ method.toUpperCase(),
368
+ )
369
+ ? "This request does not support a body"
370
+ : undefined
371
+ }
372
+ disabled={
373
+ !["POST", "PUT", "PATCH", "DELETE"].includes(
374
+ method.toUpperCase(),
375
+ )
376
+ }
343
377
  />
378
+ {examples && (
379
+ <ExamplesDropdown
380
+ examples={examples}
381
+ onSelect={(example) =>
382
+ setValue("body", JSON.stringify(example.value, null, 2))
383
+ }
384
+ />
385
+ )}
386
+ </TabsContent>
387
+ <TabsContent value="auth">
388
+ <div className="flex flex-col gap-4 my-4">
389
+ {identities.data?.length === 0 && (
390
+ <Alert>
391
+ <InfoIcon className="w-4 h-4" />
392
+ <AlertTitle>Authentication</AlertTitle>
393
+ <AlertDescription>
394
+ No identities found. Please create an identity first.
395
+ </AlertDescription>
396
+ </Alert>
397
+ )}
398
+ <div className="flex flex-col items-center gap-2">
399
+ <Card className="w-full overflow-hidden">
400
+ <RadioGroup
401
+ onValueChange={(value) => setValue("identity", value)}
402
+ value={formState.identity}
403
+ defaultValue={formState.identity}
404
+ className="gap-0"
405
+ disabled={identities.data?.length === 0}
406
+ >
407
+ <Label
408
+ className="h-12 border-b items-center flex p-4 cursor-pointer hover:bg-accent"
409
+ htmlFor="none"
410
+ >
411
+ <RadioGroupItem value={NO_IDENTITY} id="none">
412
+ None
413
+ </RadioGroupItem>
414
+ <Label htmlFor="none" className="ml-2">
415
+ None
416
+ </Label>
417
+ </Label>
418
+ {identities.data?.map((identity) => (
419
+ <Label
420
+ key={identity.id}
421
+ className="h-12 border-b items-center flex p-4 cursor-pointer hover:bg-accent"
422
+ >
423
+ <RadioGroupItem
424
+ value={identity.id}
425
+ id={identity.id}
426
+ >
427
+ {identity.label}
428
+ </RadioGroupItem>
429
+ <Label htmlFor={identity.id} className="ml-2">
430
+ {identity.label}
431
+ </Label>
432
+ </Label>
433
+ ))}
434
+ </RadioGroup>
435
+ </Card>
436
+ </div>
437
+ </div>
344
438
  </TabsContent>
345
439
  </Tabs>
346
440
  </div>