zudoku 0.28.3 → 0.29.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 (116) hide show
  1. package/dist/cli/common/output.js.map +1 -1
  2. package/dist/cli/dev/handler.js +5 -0
  3. package/dist/cli/dev/handler.js.map +1 -1
  4. package/dist/config/validators/common.d.ts +111 -0
  5. package/dist/config/validators/common.js +9 -6
  6. package/dist/config/validators/common.js.map +1 -1
  7. package/dist/config/validators/validate.d.ts +42 -0
  8. package/dist/lib/authentication/state.d.ts +9 -0
  9. package/dist/lib/authentication/state.js +11 -0
  10. package/dist/lib/authentication/state.js.map +1 -1
  11. package/dist/lib/components/Autocomplete.d.ts +4 -3
  12. package/dist/lib/components/Autocomplete.js +2 -2
  13. package/dist/lib/components/Autocomplete.js.map +1 -1
  14. package/dist/lib/components/PathRenderer.js +23 -20
  15. package/dist/lib/components/PathRenderer.js.map +1 -1
  16. package/dist/lib/components/navigation/SidebarItem.js +1 -0
  17. package/dist/lib/components/navigation/SidebarItem.js.map +1 -1
  18. package/dist/lib/oas/graphql/index.d.ts +7 -0
  19. package/dist/lib/oas/graphql/index.js +7 -6
  20. package/dist/lib/oas/graphql/index.js.map +1 -1
  21. package/dist/lib/plugins/openapi/Endpoint.js +6 -7
  22. package/dist/lib/plugins/openapi/Endpoint.js.map +1 -1
  23. package/dist/lib/plugins/openapi/OperationList.js +11 -7
  24. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  25. package/dist/lib/plugins/openapi/Sidecar.js +4 -4
  26. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  27. package/dist/lib/plugins/openapi/graphql/gql.d.ts +2 -2
  28. package/dist/lib/plugins/openapi/graphql/gql.js +2 -2
  29. package/dist/lib/plugins/openapi/graphql/gql.js.map +1 -1
  30. package/dist/lib/plugins/openapi/graphql/graphql.d.ts +20 -4
  31. package/dist/lib/plugins/openapi/graphql/graphql.js +16 -2
  32. package/dist/lib/plugins/openapi/graphql/graphql.js.map +1 -1
  33. package/dist/lib/plugins/openapi/index.js +57 -28
  34. package/dist/lib/plugins/openapi/index.js.map +1 -1
  35. package/dist/lib/plugins/openapi/interfaces.d.ts +4 -2
  36. package/dist/lib/plugins/openapi/playground/Headers.d.ts +3 -2
  37. package/dist/lib/plugins/openapi/playground/Headers.js +42 -26
  38. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  39. package/dist/lib/plugins/openapi/playground/ParamsGrid.d.ts +4 -0
  40. package/dist/lib/plugins/openapi/playground/ParamsGrid.js +2 -1
  41. package/dist/lib/plugins/openapi/playground/ParamsGrid.js.map +1 -1
  42. package/dist/lib/plugins/openapi/playground/PathParams.js +3 -3
  43. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  44. package/dist/lib/plugins/openapi/playground/Playground.d.ts +1 -0
  45. package/dist/lib/plugins/openapi/playground/Playground.js +4 -4
  46. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  47. package/dist/lib/plugins/openapi/playground/QueryParams.js +21 -21
  48. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  49. package/dist/lib/ui/Input.d.ts +1 -2
  50. package/dist/lib/ui/Input.js.map +1 -1
  51. package/dist/lib/util/useScrollToAnchor.js +1 -1
  52. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  53. package/dist/lib/util/useScrollToTop.js +6 -4
  54. package/dist/lib/util/useScrollToTop.js.map +1 -1
  55. package/dist/vite/plugin-api.js +13 -6
  56. package/dist/vite/plugin-api.js.map +1 -1
  57. package/dist/vite/plugin-mdx.js +18 -12
  58. package/dist/vite/plugin-mdx.js.map +1 -1
  59. package/dist/vite/plugin.js.map +1 -1
  60. package/lib/{Markdown-LcMEZ0Sn.js → Markdown-8mv9nhGd.js} +3338 -3360
  61. package/lib/Markdown-8mv9nhGd.js.map +1 -0
  62. package/lib/{MdxPage-DkH3V4hV.js → MdxPage-BalfwlsD.js} +3 -3
  63. package/lib/{MdxPage-DkH3V4hV.js.map → MdxPage-BalfwlsD.js.map} +1 -1
  64. package/lib/{OperationList-wzZNceUl.js → OperationList-B3VX94x4.js} +500 -493
  65. package/lib/OperationList-B3VX94x4.js.map +1 -0
  66. package/lib/{Select-DJkXPPD0.js → Select-BcAbBUmk.js} +2 -2
  67. package/lib/{Select-DJkXPPD0.js.map → Select-BcAbBUmk.js.map} +1 -1
  68. package/lib/{SlotletProvider-D1t2ePCI.js → SlotletProvider-D0mFmGJu.js} +2 -2
  69. package/lib/{SlotletProvider-D1t2ePCI.js.map → SlotletProvider-D0mFmGJu.js.map} +1 -1
  70. package/lib/{createServer-DIztAu7i.js → createServer-E3cXjB0P.js} +4 -6
  71. package/lib/{createServer-DIztAu7i.js.map → createServer-E3cXjB0P.js.map} +1 -1
  72. package/lib/{hook-CiX69UZ6.js → hook-NIpDSpau.js} +2 -2
  73. package/lib/{hook-CiX69UZ6.js.map → hook-NIpDSpau.js.map} +1 -1
  74. package/lib/{index-DrR58fsJ.js → index-P0YUtHIb.js} +802 -745
  75. package/lib/index-P0YUtHIb.js.map +1 -0
  76. package/lib/state-bfQxaDxU.js +211 -0
  77. package/lib/{state-mM7uaXTW.js.map → state-bfQxaDxU.js.map} +1 -1
  78. package/lib/ui/Input.js.map +1 -1
  79. package/lib/{useScrollToAnchor-DYGn1MT9.js → useScrollToAnchor-BVCQSeKB.js} +29 -28
  80. package/lib/{useScrollToAnchor-DYGn1MT9.js.map → useScrollToAnchor-BVCQSeKB.js.map} +1 -1
  81. package/lib/zudoku.auth-auth0.js +1 -1
  82. package/lib/zudoku.auth-clerk.js +1 -1
  83. package/lib/zudoku.auth-openid.js +1 -1
  84. package/lib/zudoku.components.js +348 -347
  85. package/lib/zudoku.components.js.map +1 -1
  86. package/lib/zudoku.plugin-api-catalog.js +2 -2
  87. package/lib/zudoku.plugin-api-keys.js +3 -3
  88. package/lib/zudoku.plugin-custom-pages.js +1 -1
  89. package/lib/zudoku.plugin-markdown.js +1 -1
  90. package/lib/zudoku.plugin-openapi.js +2 -2
  91. package/package.json +2 -1
  92. package/src/app/demo-cdn.html +31 -31
  93. package/src/lib/authentication/state.ts +18 -0
  94. package/src/lib/components/Autocomplete.tsx +6 -4
  95. package/src/lib/components/PathRenderer.tsx +6 -4
  96. package/src/lib/components/navigation/SidebarItem.tsx +1 -0
  97. package/src/lib/oas/graphql/index.ts +10 -9
  98. package/src/lib/plugins/openapi/Endpoint.tsx +11 -9
  99. package/src/lib/plugins/openapi/OperationList.tsx +16 -10
  100. package/src/lib/plugins/openapi/Sidecar.tsx +4 -4
  101. package/src/lib/plugins/openapi/graphql/gql.ts +4 -4
  102. package/src/lib/plugins/openapi/graphql/graphql.ts +30 -6
  103. package/src/lib/plugins/openapi/index.tsx +74 -41
  104. package/src/lib/plugins/openapi/interfaces.ts +5 -10
  105. package/src/lib/plugins/openapi/playground/Headers.tsx +125 -89
  106. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +6 -1
  107. package/src/lib/plugins/openapi/playground/PathParams.tsx +9 -9
  108. package/src/lib/plugins/openapi/playground/Playground.tsx +7 -4
  109. package/src/lib/plugins/openapi/playground/QueryParams.tsx +88 -86
  110. package/src/lib/ui/Input.tsx +1 -2
  111. package/src/lib/util/useScrollToAnchor.ts +1 -1
  112. package/src/lib/util/useScrollToTop.ts +8 -3
  113. package/lib/Markdown-LcMEZ0Sn.js.map +0 -1
  114. package/lib/OperationList-wzZNceUl.js.map +0 -1
  115. package/lib/index-DrR58fsJ.js.map +0 -1
  116. package/lib/state-mM7uaXTW.js +0 -202
@@ -26,14 +26,20 @@ const GetCategoriesQuery = graphql(`
26
26
  `);
27
27
 
28
28
  const GetOperationsQuery = graphql(`
29
- query GetOperations(
30
- $input: JSON!
31
- $type: SchemaType!
32
- $tag: String
33
- $untagged: Boolean
34
- ) {
29
+ query GetOperations($input: JSON!, $type: SchemaType!, $tag: String) {
35
30
  schema(input: $input, type: $type) {
36
- operations(tag: $tag, untagged: $untagged) {
31
+ operations(tag: $tag) {
32
+ slug
33
+ deprecated
34
+ method
35
+ summary
36
+ operationId
37
+ path
38
+ tags {
39
+ name
40
+ }
41
+ }
42
+ untagged: operations(untagged: true) {
37
43
  slug
38
44
  deprecated
39
45
  method
@@ -138,7 +144,7 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
138
144
  );
139
145
  const version = urlVersion ?? Object.keys(config.input).at(0);
140
146
 
141
- const data = await client.fetch(GetCategoriesQuery, {
147
+ const tagData = await client.fetch(GetCategoriesQuery, {
142
148
  type: config.type,
143
149
  input: config.type === "file" ? config.input[version!] : config.input,
144
150
  });
@@ -150,42 +156,69 @@ export const openApiPlugin = (config: OpenApiPluginOptions): ZudokuPlugin => {
150
156
  const operationsData = await client.fetch(GetOperationsQuery, {
151
157
  type: config.type,
152
158
  input: config.type === "file" ? config.input[version!] : config.input,
153
- tag,
154
- untagged: tag === undefined,
159
+ tag: !config.loadTags ? tag : undefined,
155
160
  });
156
161
 
157
- const items = operationsData.schema.operations.map((operation) => ({
158
- type: "link" as const,
159
- label: operation.summary ?? operation.path,
160
- href: `#${operation.slug}`,
161
- badge: {
162
- label: operation.method,
163
- color: MethodColorMap[operation.method.toLowerCase()]!,
164
- invert: true,
165
- } as const,
166
- }));
167
-
168
- const categories = data.schema.tags
169
- // .filter((tag) => tag.operations.length > 0)
170
- .map<SidebarItem>((tag) => {
171
- const categoryLink = joinUrl(
172
- basePath,
173
- urlVersion,
174
- tag.name ? slugify(tag.name) : UNTAGGED_PATH,
175
- );
176
- return {
177
- type: "category",
178
- label: tag.name || "Other endpoints",
179
- link: {
180
- type: "doc" as const,
181
- id: categoryLink,
182
- label: tag.name!,
183
- },
184
- collapsible: false,
185
- collapsed: true,
186
- items: path === categoryLink ? items : [],
187
- };
162
+ const categories = tagData.schema.tags.flatMap<SidebarItem>((tag) => {
163
+ const categoryLink = joinUrl(basePath, urlVersion, slugify(tag.name));
164
+
165
+ const operations = operationsData.schema.operations
166
+ .filter(
167
+ (operation) =>
168
+ operation.tags?.length !== 0 &&
169
+ operation.tags?.map((t) => t.name).includes(tag.name),
170
+ )
171
+ .map((operation) => ({
172
+ type: "link" as const,
173
+ label: operation.summary ?? operation.path,
174
+ href: `${categoryLink}#${operation.slug}`,
175
+ badge: {
176
+ label: operation.method,
177
+ color: MethodColorMap[operation.method.toLowerCase()]!,
178
+ invert: true,
179
+ } as const,
180
+ }));
181
+
182
+ if (config.loadTags && operations.length === 0) {
183
+ return [];
184
+ }
185
+
186
+ return {
187
+ type: "category",
188
+ label: tag.name,
189
+ link: {
190
+ type: "doc" as const,
191
+ id: categoryLink,
192
+ label: tag.name,
193
+ },
194
+ collapsible: config.loadTags,
195
+ collapsed: !config.loadTags,
196
+ items: operations,
197
+ };
198
+ });
199
+
200
+ const { untagged } = operationsData.schema;
201
+
202
+ if (untagged.length > 0) {
203
+ const categoryLink = joinUrl(basePath, urlVersion, UNTAGGED_PATH);
204
+
205
+ categories.push({
206
+ type: "category",
207
+ label: "Other endpoints",
208
+ link: {
209
+ type: "doc" as const,
210
+ id: categoryLink,
211
+ label: "Other endpoints",
212
+ },
213
+ collapsible: config.loadTags,
214
+ collapsed: !config.loadTags,
215
+ items: untagged.map((operation) => ({
216
+ type: "link" as const,
217
+ label: operation.summary ?? operation.path,
218
+ href: `${categoryLink}#${operation.slug}`,
219
+ })),
188
220
  });
221
+ }
189
222
 
190
223
  return categories;
191
224
  } catch {
@@ -1,19 +1,13 @@
1
+ type DynamicInput = () => Promise<unknown>;
2
+
1
3
  type OasSource =
2
4
  | { type: "url"; input: string }
3
- | {
4
- type: "file";
5
- input: {
6
- [version: string]: () => Promise<unknown>;
7
- };
8
- }
5
+ | { type: "file"; input: { [version: string]: DynamicInput } }
9
6
  | { type: "raw"; input: string };
10
7
 
11
8
  export type ContextOasSource =
12
9
  | { type: "url"; input: string }
13
- | {
14
- type: "file";
15
- input: () => Promise<unknown>;
16
- }
10
+ | { type: "file"; input: DynamicInput }
17
11
  | { type: "raw"; input: string };
18
12
 
19
13
  export type OasPluginConfig = {
@@ -21,6 +15,7 @@ export type OasPluginConfig = {
21
15
  navigationId?: string;
22
16
  skipPreload?: boolean;
23
17
  tagPages?: Array<string>;
18
+ loadTags?: boolean;
24
19
  } & OasPluginConfigOptions &
25
20
  OasSource;
26
21
 
@@ -1,5 +1,5 @@
1
1
  import { XIcon } from "lucide-react";
2
- import { useRef } from "react";
2
+ import { useCallback, useEffect, useRef } from "react";
3
3
  import {
4
4
  Control,
5
5
  Controller,
@@ -11,8 +11,8 @@ import { Checkbox } from "zudoku/ui/Checkbox.js";
11
11
  import { Autocomplete } from "../../../components/Autocomplete.js";
12
12
  import { Button } from "../../../ui/Button.js";
13
13
  import { Input } from "../../../ui/Input.js";
14
- import ParamsGrid from "./ParamsGrid.js";
15
- import { type PlaygroundForm } from "./Playground.js";
14
+ import ParamsGrid, { ParamsGridItem } from "./ParamsGrid.js";
15
+ import { Header, type PlaygroundForm } from "./Playground.js";
16
16
 
17
17
  const headerOptions = Object.freeze([
18
18
  "Accept",
@@ -43,22 +43,31 @@ const headerOptions = Object.freeze([
43
43
  "X-Requested-With",
44
44
  ]);
45
45
 
46
- export const Headers = ({ control }: { control: Control<PlaygroundForm> }) => {
47
- const { fields, append, remove } = useFieldArray<PlaygroundForm>({
46
+ export const Headers = ({
47
+ control,
48
+ headers: schemaHeaders,
49
+ }: {
50
+ control: Control<PlaygroundForm>;
51
+ headers: Header[];
52
+ }) => {
53
+ const { fields, append, remove } = useFieldArray<PlaygroundForm, "headers">({
48
54
  control,
49
55
  name: "headers",
50
56
  });
51
- const { setValue } = useFormContext<PlaygroundForm>();
57
+ const { setValue, watch } = useFormContext<PlaygroundForm>();
52
58
  const valueRefs = useRef<Array<HTMLInputElement | null>>([]);
53
59
  const nameRefs = useRef<Array<HTMLInputElement | null>>([]);
60
+ const watchedHeaders = watch("headers");
54
61
 
55
- const addNewHeader = () => {
56
- append({
57
- name: "",
58
- value: "",
59
- active: false,
60
- } as PlaygroundForm["headers"][number]);
61
- };
62
+ const addNewHeader = useCallback(() => {
63
+ append({ name: "", value: "", active: false });
64
+ }, [append]);
65
+
66
+ useEffect(() => {
67
+ if (watchedHeaders.length === 0) {
68
+ addNewHeader();
69
+ }
70
+ }, [watchedHeaders, addNewHeader]);
62
71
 
63
72
  const handleHeaderEnter = (index: number) => {
64
73
  valueRefs.current[index]?.focus();
@@ -69,86 +78,113 @@ export const Headers = ({ control }: { control: Control<PlaygroundForm> }) => {
69
78
  requestAnimationFrame(() => nameRefs.current[index + 1]?.focus());
70
79
  };
71
80
 
81
+ const missingHeaders = schemaHeaders
82
+ .filter((h) => !watchedHeaders.some((f) => f.name === h.name))
83
+ .map(({ name }) => name);
84
+
72
85
  return (
73
86
  <div className="flex flex-col gap-2">
74
87
  <Card className="overflow-hidden">
75
88
  <ParamsGrid>
76
- {fields.map((header, i) => (
77
- <div
78
- key={header.name}
79
- className="group grid col-span-full grid-cols-subgrid"
80
- >
81
- <div className="flex items-center gap-2 ">
82
- <Controller
83
- control={control}
84
- name={`headers.${i}.active`}
85
- render={({ field }) => (
86
- <Checkbox
87
- variant="outline"
88
- id={`headers.${i}.active`}
89
- checked={field.value}
90
- onCheckedChange={(checked) => {
91
- field.onChange(checked);
92
- }}
93
- />
94
- )}
95
- />
96
- <Controller
97
- control={control}
98
- name={`headers.${i}.name`}
99
- render={({ field }) => (
100
- <Autocomplete
101
- {...field}
102
- placeholder="Name"
103
- className="border-0 shadow-none bg-transparent text-xs font-mono"
104
- options={headerOptions}
105
- onEnterPress={() => handleHeaderEnter(i)}
106
- onChange={(e) => {
107
- field.onChange(e);
108
- setValue(`headers.${i}.active`, true);
109
- }}
110
- ref={(el) => {
111
- nameRefs.current[i] = el;
112
- }}
113
- />
114
- )}
115
- />
116
- </div>
117
- <div className="flex items-center gap-2">
118
- <Controller
119
- control={control}
120
- name={`headers.${i}.value`}
121
- render={({ field }) => (
122
- <Input
123
- placeholder="Value"
124
- className="w-full border-0 shadow-none text-xs font-mono focus-visible:ring-0"
125
- {...field}
126
- ref={(el) => {
127
- valueRefs.current[i] = el;
128
- }}
129
- onKeyDown={(e) => {
130
- if (e.key === "Enter" && e.currentTarget.value.trim()) {
131
- handleValueEnter(i);
132
- }
133
- }}
134
- autoComplete="off"
135
- />
136
- )}
137
- />
138
- <Button
139
- size="icon"
140
- variant="ghost"
141
- className="text-muted-foreground opacity-0 group-hover:opacity-100 rounded-full w-8 h-7"
142
- onClick={() => {
143
- remove(i);
144
- }}
145
- type="button"
146
- >
147
- <XIcon size={16} />
148
- </Button>
149
- </div>
150
- </div>
151
- ))}
89
+ {fields.map((field, i) => {
90
+ const currentHeader = schemaHeaders.find(
91
+ (h) => h.name === watch(`headers.${i}.name`),
92
+ );
93
+ return (
94
+ <ParamsGridItem key={field.id}>
95
+ <div className="flex items-center gap-2 ">
96
+ <Controller
97
+ control={control}
98
+ name={`headers.${i}.active`}
99
+ render={({ field }) => (
100
+ <Checkbox
101
+ variant="outline"
102
+ id={`headers.${i}.active`}
103
+ checked={field.value}
104
+ onCheckedChange={(checked) => {
105
+ field.onChange(checked);
106
+ }}
107
+ />
108
+ )}
109
+ />
110
+ <Controller
111
+ control={control}
112
+ name={`headers.${i}.name`}
113
+ render={({ field }) => (
114
+ <Autocomplete
115
+ {...field}
116
+ placeholder="Name"
117
+ className="border-0 shadow-none bg-transparent text-xs font-mono"
118
+ options={[...missingHeaders, ...headerOptions]}
119
+ onEnterPress={() => handleHeaderEnter(i)}
120
+ onChange={(e) => {
121
+ field.onChange(e);
122
+ setValue(`headers.${i}.active`, true);
123
+ }}
124
+ ref={(el) => {
125
+ nameRefs.current[i] = el;
126
+ }}
127
+ />
128
+ )}
129
+ />
130
+ </div>
131
+ <div className="flex items-center gap-2">
132
+ <Controller
133
+ control={control}
134
+ name={`headers.${i}.value`}
135
+ render={({ field }) => {
136
+ const hasEnum =
137
+ currentHeader?.enum && currentHeader.enum.length > 0;
138
+
139
+ if (!hasEnum) {
140
+ return (
141
+ <Input
142
+ placeholder="Value"
143
+ className="w-full border-0 shadow-none text-xs font-mono focus-visible:ring-0"
144
+ {...field}
145
+ ref={(el) => {
146
+ valueRefs.current[i] = el;
147
+ }}
148
+ onKeyDown={(e) => {
149
+ if (
150
+ e.key === "Enter" &&
151
+ e.currentTarget.value.trim()
152
+ ) {
153
+ handleValueEnter(i);
154
+ }
155
+ }}
156
+ autoComplete="off"
157
+ />
158
+ );
159
+ }
160
+
161
+ return (
162
+ <Autocomplete
163
+ shouldFilter={false}
164
+ value={field.value}
165
+ options={currentHeader.enum ?? []}
166
+ onChange={(e) => {
167
+ field.onChange(e);
168
+ setValue(`headers.${i}.active`, true);
169
+ }}
170
+ className="font-mono text-xs border-0"
171
+ />
172
+ );
173
+ }}
174
+ />
175
+ <Button
176
+ size="icon"
177
+ variant="ghost"
178
+ className="text-muted-foreground opacity-0 group-hover:opacity-100 rounded-full w-8 h-7"
179
+ onClick={() => remove(i)}
180
+ type="button"
181
+ >
182
+ <XIcon size={16} />
183
+ </Button>
184
+ </div>
185
+ </ParamsGridItem>
186
+ );
187
+ })}
152
188
  </ParamsGrid>
153
189
  </Card>
154
190
  <div className="text-end">
@@ -2,7 +2,12 @@ import createVariantComponent from "../../../util/createVariantComponent.js";
2
2
 
3
3
  const ParamsGrid = createVariantComponent(
4
4
  "div",
5
- "hover:bg-accent/40 grid grid-cols-[2fr_3fr] gap-2 items-center px-3",
5
+ "grid grid-cols-[2fr_3fr] gap-2 items-center",
6
+ );
7
+
8
+ export const ParamsGridItem = createVariantComponent(
9
+ "div",
10
+ "group hover:bg-accent px-3 grid col-span-full grid-cols-subgrid",
6
11
  );
7
12
 
8
13
  export default ParamsGrid;
@@ -2,7 +2,7 @@ import { Control, Controller, useFieldArray } from "react-hook-form";
2
2
  import { Card } from "zudoku/ui/Card.js";
3
3
  import { Input } from "../../../ui/Input.js";
4
4
  import { ColorizedParam } from "../ColorizedParam.js";
5
- import ParamsGrid from "./ParamsGrid.js";
5
+ import ParamsGrid, { ParamsGridItem } from "./ParamsGrid.js";
6
6
  import type { PlaygroundForm } from "./Playground.js";
7
7
 
8
8
  export const PathParams = ({
@@ -10,7 +10,7 @@ export const PathParams = ({
10
10
  }: {
11
11
  control: Control<PlaygroundForm>;
12
12
  }) => {
13
- const { fields } = useFieldArray<PlaygroundForm>({
13
+ const { fields } = useFieldArray<PlaygroundForm, "pathParams">({
14
14
  control,
15
15
  name: "pathParams",
16
16
  });
@@ -18,16 +18,16 @@ export const PathParams = ({
18
18
  return (
19
19
  <Card className="rounded-lg">
20
20
  <ParamsGrid>
21
- {fields.map((part, i) => (
22
- <>
21
+ {fields.map((field, i) => (
22
+ <ParamsGridItem key={field.id}>
23
23
  <Controller
24
24
  control={control}
25
25
  name={`pathParams.${i}.name`}
26
26
  render={() => (
27
- <div>
27
+ <div className="flex items-center">
28
28
  <ColorizedParam
29
- slug={part.name}
30
- name={part.name}
29
+ slug={field.name}
30
+ name={field.name}
31
31
  className="font-mono text-xs px-2"
32
32
  />
33
33
  </div>
@@ -43,12 +43,12 @@ export const PathParams = ({
43
43
  {...field}
44
44
  required
45
45
  placeholder="Enter value"
46
- className="w-full border-0 shadow-none text-xs font-mono hover:bg-accent"
46
+ className="w-full border-0 shadow-none text-xs font-mono focus-visible:ring-0"
47
47
  />
48
48
  )}
49
49
  />
50
50
  </div>
51
- </>
51
+ </ParamsGridItem>
52
52
  ))}
53
53
  </ParamsGrid>
54
54
  </Card>
@@ -15,7 +15,7 @@ import {
15
15
  SelectValue,
16
16
  } from "zudoku/ui/Select.js";
17
17
  import { Textarea } from "zudoku/ui/Textarea.js";
18
- import { useSelectedServerStore } from "../../../authentication/state.js";
18
+ import { useSelectedServer } from "../../../authentication/state.js";
19
19
  import { useApiIdentities } from "../../../components/context/ZudokuContext.js";
20
20
  import { Card } from "../../../ui/Card.js";
21
21
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
@@ -68,6 +68,7 @@ export type PlaygroundForm = {
68
68
  name: string;
69
69
  value: string;
70
70
  active: boolean;
71
+ enum?: string[];
71
72
  }>;
72
73
  identity?: string;
73
74
  };
@@ -100,7 +101,7 @@ export type PlaygroundContentProps = {
100
101
 
101
102
  export const Playground = ({
102
103
  server,
103
- servers,
104
+ servers = [],
104
105
  url,
105
106
  method,
106
107
  headers = [],
@@ -109,7 +110,9 @@ export const Playground = ({
109
110
  defaultBody = "",
110
111
  examples,
111
112
  }: PlaygroundContentProps) => {
112
- const { selectedServer, setSelectedServer } = useSelectedServerStore();
113
+ const { selectedServer, setSelectedServer } = useSelectedServer(
114
+ servers.map((server) => ({ url: server })),
115
+ );
113
116
  const [, startTransition] = useTransition();
114
117
  const { register, control, handleSubmit, watch, setValue, ...form } =
115
118
  useForm<PlaygroundForm>({
@@ -340,7 +343,7 @@ export const Playground = ({
340
343
  </TabsList>
341
344
  </div>
342
345
  <TabsContent value="headers">
343
- <Headers control={control} />
346
+ <Headers control={control} headers={headers} />
344
347
  </TabsContent>
345
348
  <TabsContent value="parameters">
346
349
  {pathParams.length > 0 && (