zudoku 0.64.2 → 0.65.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 (279) hide show
  1. package/dist/app/main.d.ts +91 -1
  2. package/dist/app/main.js +5 -1
  3. package/dist/app/main.js.map +1 -1
  4. package/dist/config/validators/InputNavigationSchema.d.ts +16 -16
  5. package/dist/config/validators/NavigationSchema.js +2 -4
  6. package/dist/config/validators/NavigationSchema.js.map +1 -1
  7. package/dist/config/validators/validate.d.ts +53 -1
  8. package/dist/config/validators/validate.js +7 -0
  9. package/dist/config/validators/validate.js.map +1 -1
  10. package/dist/config/validators/validate.test.js +43 -0
  11. package/dist/config/validators/validate.test.js.map +1 -1
  12. package/dist/flat-config.d.ts +6 -0
  13. package/dist/lib/authentication/providers/auth0.js +6 -1
  14. package/dist/lib/authentication/providers/auth0.js.map +1 -1
  15. package/dist/lib/components/Autocomplete.d.ts +3 -1
  16. package/dist/lib/components/Autocomplete.js +6 -2
  17. package/dist/lib/components/Autocomplete.js.map +1 -1
  18. package/dist/lib/components/Layout.js +3 -2
  19. package/dist/lib/components/Layout.js.map +1 -1
  20. package/dist/lib/components/navigation/NavigationItem.js +2 -2
  21. package/dist/lib/components/navigation/NavigationItem.js.map +1 -1
  22. package/dist/lib/errors/ErrorAlert.js +1 -1
  23. package/dist/lib/errors/RouterError.d.ts +3 -1
  24. package/dist/lib/errors/RouterError.js +3 -2
  25. package/dist/lib/errors/RouterError.js.map +1 -1
  26. package/dist/lib/plugins/openapi/GeneratedExampleSidecarBox.js +1 -1
  27. package/dist/lib/plugins/openapi/GeneratedExampleSidecarBox.js.map +1 -1
  28. package/dist/lib/plugins/openapi/OperationList.js +2 -1
  29. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  30. package/dist/lib/plugins/openapi/OperationListItem.js +2 -1
  31. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  32. package/dist/lib/plugins/openapi/ParameterList.js +7 -4
  33. package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
  34. package/dist/lib/plugins/openapi/ParameterListItem.js +17 -6
  35. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  36. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +4 -1
  37. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  38. package/dist/lib/plugins/openapi/ResponsesSidecarBox.d.ts +1 -2
  39. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +15 -6
  40. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  41. package/dist/lib/plugins/openapi/Sidecar.d.ts +1 -2
  42. package/dist/lib/plugins/openapi/Sidecar.js +39 -15
  43. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  44. package/dist/lib/plugins/openapi/SidecarBox.js +4 -4
  45. package/dist/lib/plugins/openapi/SidecarBox.js.map +1 -1
  46. package/dist/lib/plugins/openapi/SidecarExamples.js +15 -16
  47. package/dist/lib/plugins/openapi/SidecarExamples.js.map +1 -1
  48. package/dist/lib/plugins/openapi/components/ConstValue.js +1 -1
  49. package/dist/lib/plugins/openapi/components/ConstValue.js.map +1 -1
  50. package/dist/lib/plugins/openapi/components/EnumValues.js +1 -1
  51. package/dist/lib/plugins/openapi/components/EnumValues.js.map +1 -1
  52. package/dist/lib/plugins/openapi/components/ResponseContent.js +5 -6
  53. package/dist/lib/plugins/openapi/components/ResponseContent.js.map +1 -1
  54. package/dist/lib/plugins/openapi/interfaces.d.ts +13 -0
  55. package/dist/lib/plugins/openapi/playground/BodyPanel.js +67 -15
  56. package/dist/lib/plugins/openapi/playground/BodyPanel.js.map +1 -1
  57. package/dist/lib/plugins/openapi/playground/CollapsibleHeader.js +2 -2
  58. package/dist/lib/plugins/openapi/playground/CollapsibleHeader.js.map +1 -1
  59. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js +1 -1
  60. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js.map +1 -1
  61. package/dist/lib/plugins/openapi/playground/Headers.js +23 -83
  62. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  63. package/dist/lib/plugins/openapi/playground/ParamsGrid.d.ts +8 -0
  64. package/dist/lib/plugins/openapi/playground/ParamsGrid.js +8 -1
  65. package/dist/lib/plugins/openapi/playground/ParamsGrid.js.map +1 -1
  66. package/dist/lib/plugins/openapi/playground/PathParams.js +2 -3
  67. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  68. package/dist/lib/plugins/openapi/playground/Playground.d.ts +7 -0
  69. package/dist/lib/plugins/openapi/playground/Playground.js +56 -28
  70. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  71. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +3 -2
  72. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
  73. package/dist/lib/plugins/openapi/playground/QueryParams.js +16 -40
  74. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  75. package/dist/lib/plugins/openapi/playground/request-panel/MultipartField.d.ts +8 -0
  76. package/dist/lib/plugins/openapi/playground/request-panel/MultipartField.js +19 -0
  77. package/dist/lib/plugins/openapi/playground/request-panel/MultipartField.js.map +1 -0
  78. package/dist/lib/plugins/openapi/playground/request-panel/UrlQueryParams.js +1 -1
  79. package/dist/lib/plugins/openapi/playground/request-panel/UrlQueryParams.js.map +1 -1
  80. package/dist/lib/plugins/openapi/playground/request-panel/fieldManager/useKeyValueFieldManager.test.d.ts +1 -0
  81. package/dist/lib/plugins/openapi/playground/request-panel/fieldManager/useKeyValueFieldManager.test.js +540 -0
  82. package/dist/lib/plugins/openapi/playground/request-panel/fieldManager/useKeyValueFieldManager.test.js.map +1 -0
  83. package/dist/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.d.ts +40 -0
  84. package/dist/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.js +205 -0
  85. package/dist/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.js.map +1 -0
  86. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +2 -2
  87. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -1
  88. package/dist/lib/plugins/openapi/schema/SchemaExampleAndDefault.js +1 -1
  89. package/dist/lib/plugins/openapi/schema/SchemaExampleAndDefault.js.map +1 -1
  90. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js +17 -7
  91. package/dist/lib/plugins/openapi/schema/SchemaPropertyItem.js.map +1 -1
  92. package/dist/lib/plugins/openapi/schema/SchemaView.d.ts +1 -1
  93. package/dist/lib/plugins/openapi/schema/SchemaView.js +20 -9
  94. package/dist/lib/plugins/openapi/schema/SchemaView.js.map +1 -1
  95. package/dist/lib/plugins/openapi/schema/UnionView.js +2 -5
  96. package/dist/lib/plugins/openapi/schema/UnionView.js.map +1 -1
  97. package/dist/lib/ui/Badge.d.ts +3 -3
  98. package/dist/lib/ui/Badge.js +9 -7
  99. package/dist/lib/ui/Badge.js.map +1 -1
  100. package/dist/lib/ui/Button.d.ts +1 -1
  101. package/dist/lib/ui/Button.js +1 -0
  102. package/dist/lib/ui/Button.js.map +1 -1
  103. package/dist/lib/ui/Checkbox.d.ts +2 -2
  104. package/dist/lib/ui/Checkbox.js +4 -4
  105. package/dist/lib/ui/Checkbox.js.map +1 -1
  106. package/dist/lib/ui/CodeBlock.js +1 -1
  107. package/dist/lib/ui/CodeBlock.js.map +1 -1
  108. package/dist/lib/ui/Collapsible.d.ts +4 -4
  109. package/dist/lib/ui/Collapsible.js +11 -4
  110. package/dist/lib/ui/Collapsible.js.map +1 -1
  111. package/dist/lib/ui/EmbeddedCodeBlock.js +3 -2
  112. package/dist/lib/ui/EmbeddedCodeBlock.js.map +1 -1
  113. package/dist/lib/ui/Frame.d.ts +8 -0
  114. package/dist/lib/ui/Frame.js +22 -0
  115. package/dist/lib/ui/Frame.js.map +1 -0
  116. package/dist/lib/ui/Item.d.ts +23 -0
  117. package/dist/lib/ui/Item.js +67 -0
  118. package/dist/lib/ui/Item.js.map +1 -0
  119. package/dist/lib/ui/NativeSelect.d.ts +5 -0
  120. package/dist/lib/ui/NativeSelect.js +14 -0
  121. package/dist/lib/ui/NativeSelect.js.map +1 -0
  122. package/dist/lib/ui/Select.d.ts +13 -11
  123. package/dist/lib/ui/Select.js +34 -23
  124. package/dist/lib/ui/Select.js.map +1 -1
  125. package/dist/lib/util/readFrontmatter.d.ts +6 -0
  126. package/dist/lib/util/readFrontmatter.js +12 -0
  127. package/dist/lib/util/readFrontmatter.js.map +1 -0
  128. package/dist/vite/mdx/remark-last-modified.js +57 -3
  129. package/dist/vite/mdx/remark-last-modified.js.map +1 -1
  130. package/dist/vite/plugin-api.js +2 -2
  131. package/dist/vite/plugin-api.js.map +1 -1
  132. package/dist/vite/plugin-frontmatter.js +3 -5
  133. package/dist/vite/plugin-frontmatter.js.map +1 -1
  134. package/dist/vite/plugin-markdown-export.js +3 -4
  135. package/dist/vite/plugin-markdown-export.js.map +1 -1
  136. package/lib/{Button-DmS4u8Lj.js → Button-B3ucvvQw.js} +7 -6
  137. package/lib/Button-B3ucvvQw.js.map +1 -0
  138. package/lib/{ErrorAlert--3alJ_-b.js → ErrorAlert-D5LKLFOd.js} +1100 -1112
  139. package/lib/ErrorAlert-D5LKLFOd.js.map +1 -0
  140. package/lib/{MdxPage-Bpa9tL63.js → MdxPage-hOCN-u-L.js} +6 -6
  141. package/lib/{MdxPage-Bpa9tL63.js.map → MdxPage-hOCN-u-L.js.map} +1 -1
  142. package/lib/{OAuthErrorPage-B79J86Fo.js → OAuthErrorPage-oXnxcJg4.js} +4 -4
  143. package/lib/{OAuthErrorPage-B79J86Fo.js.map → OAuthErrorPage-oXnxcJg4.js.map} +1 -1
  144. package/lib/{OasProvider-jr0oDSFy.js → OasProvider-BuBeRIHB.js} +2 -2
  145. package/lib/{OasProvider-jr0oDSFy.js.map → OasProvider-BuBeRIHB.js.map} +1 -1
  146. package/lib/{OperationList-DLEAg4qw.js → OperationList-Cx8TGKhB.js} +2053 -1830
  147. package/lib/OperationList-Cx8TGKhB.js.map +1 -0
  148. package/lib/{Pagination-H2HW9-Er.js → Pagination-lLSoHnxa.js} +2 -2
  149. package/lib/{Pagination-H2HW9-Er.js.map → Pagination-lLSoHnxa.js.map} +1 -1
  150. package/lib/{RouteGuard-CjzxosTf.js → RouteGuard-Brz95MSt.js} +2 -2
  151. package/lib/{RouteGuard-CjzxosTf.js.map → RouteGuard-Brz95MSt.js.map} +1 -1
  152. package/lib/RouterError-VGZB_wg4.js +42 -0
  153. package/lib/RouterError-VGZB_wg4.js.map +1 -0
  154. package/lib/{SchemaList-CSDSazqV.js → SchemaList-rBWXYJEb.js} +7 -7
  155. package/lib/{SchemaList-CSDSazqV.js.map → SchemaList-rBWXYJEb.js.map} +1 -1
  156. package/lib/SchemaView-jouS_xvc.js +586 -0
  157. package/lib/SchemaView-jouS_xvc.js.map +1 -0
  158. package/lib/Select-DFRCS31-.js +399 -0
  159. package/lib/Select-DFRCS31-.js.map +1 -0
  160. package/lib/{SignUp-Fycafbyg.js → SignUp-D2mmQOkg.js} +2 -2
  161. package/lib/{SignUp-Fycafbyg.js.map → SignUp-D2mmQOkg.js.map} +1 -1
  162. package/lib/{Toc-ChkOg2UU.js → Toc-CBWfFCVf.js} +2 -2
  163. package/lib/{Toc-ChkOg2UU.js.map → Toc-CBWfFCVf.js.map} +1 -1
  164. package/lib/{circular-DGfd8SGc.js → circular-CGkbVs2O.js} +6360 -5953
  165. package/lib/circular-CGkbVs2O.js.map +1 -0
  166. package/lib/{createServer-DGD8hEzT.js → createServer-CcV_75PW.js} +770 -735
  167. package/lib/createServer-CcV_75PW.js.map +1 -0
  168. package/lib/{errors-BTpjwHS6.js → errors-D7xzOd8X.js} +2 -2
  169. package/lib/{errors-BTpjwHS6.js.map → errors-D7xzOd8X.js.map} +1 -1
  170. package/lib/{index-Bvas0H4x.js → index-CF7_erXq.js} +2 -2
  171. package/lib/{index-Bvas0H4x.js.map → index-CF7_erXq.js.map} +1 -1
  172. package/lib/{index-FNRZUtwo.js → index-CPws05Tb.js} +3 -3
  173. package/lib/{index-FNRZUtwo.js.map → index-CPws05Tb.js.map} +1 -1
  174. package/lib/index-I4zC7Yht.js +3680 -0
  175. package/lib/index-I4zC7Yht.js.map +1 -0
  176. package/lib/ui/ActionButton.js +1 -1
  177. package/lib/ui/Badge.js +27 -13
  178. package/lib/ui/Badge.js.map +1 -1
  179. package/lib/ui/Button.js +6 -5
  180. package/lib/ui/Button.js.map +1 -1
  181. package/lib/ui/Checkbox.js +29 -26
  182. package/lib/ui/Checkbox.js.map +1 -1
  183. package/lib/ui/CodeBlock.js +7 -7
  184. package/lib/ui/CodeBlock.js.map +1 -1
  185. package/lib/ui/Collapsible.js +32 -5
  186. package/lib/ui/Collapsible.js.map +1 -1
  187. package/lib/ui/EmbeddedCodeBlock.js +19 -18
  188. package/lib/ui/EmbeddedCodeBlock.js.map +1 -1
  189. package/lib/ui/Frame.js +81 -0
  190. package/lib/ui/Frame.js.map +1 -0
  191. package/lib/ui/Item.js +188 -0
  192. package/lib/ui/Item.js.map +1 -0
  193. package/lib/ui/NativeSelect.js +57 -0
  194. package/lib/ui/NativeSelect.js.map +1 -0
  195. package/lib/ui/Select.js +166 -116
  196. package/lib/ui/Select.js.map +1 -1
  197. package/lib/ui/Tabs.js +10 -10
  198. package/lib/zudoku.__internal.js +345 -345
  199. package/lib/zudoku.__internal.js.map +1 -1
  200. package/lib/zudoku.auth-auth0.js +7 -7
  201. package/lib/zudoku.auth-auth0.js.map +1 -1
  202. package/lib/zudoku.auth-azureb2c.js +3 -3
  203. package/lib/zudoku.auth-clerk.js +1 -1
  204. package/lib/zudoku.auth-openid.js +3 -3
  205. package/lib/zudoku.auth-supabase.js +3 -3
  206. package/lib/zudoku.components.js +2 -2
  207. package/lib/zudoku.plugin-api-catalog.js +3 -3
  208. package/lib/zudoku.plugin-api-keys.js +4 -4
  209. package/lib/zudoku.plugin-markdown.js +1 -1
  210. package/lib/zudoku.plugin-openapi.js +1 -1
  211. package/lib/zudoku.plugin-search-pagefind.js +2 -2
  212. package/package.json +4 -4
  213. package/src/app/main.tsx +5 -1
  214. package/src/lib/authentication/providers/auth0.tsx +6 -1
  215. package/src/lib/components/Autocomplete.tsx +11 -2
  216. package/src/lib/components/Layout.tsx +3 -2
  217. package/src/lib/components/navigation/NavigationItem.tsx +7 -20
  218. package/src/lib/errors/ErrorAlert.tsx +1 -1
  219. package/src/lib/errors/RouterError.tsx +7 -2
  220. package/src/lib/plugins/openapi/GeneratedExampleSidecarBox.tsx +2 -2
  221. package/src/lib/plugins/openapi/OperationList.tsx +3 -1
  222. package/src/lib/plugins/openapi/OperationListItem.tsx +7 -7
  223. package/src/lib/plugins/openapi/ParameterList.tsx +37 -23
  224. package/src/lib/plugins/openapi/ParameterListItem.tsx +105 -54
  225. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +36 -13
  226. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +67 -44
  227. package/src/lib/plugins/openapi/Sidecar.tsx +84 -41
  228. package/src/lib/plugins/openapi/SidecarBox.tsx +26 -4
  229. package/src/lib/plugins/openapi/SidecarExamples.tsx +59 -37
  230. package/src/lib/plugins/openapi/components/ConstValue.tsx +1 -1
  231. package/src/lib/plugins/openapi/components/EnumValues.tsx +2 -2
  232. package/src/lib/plugins/openapi/components/ResponseContent.tsx +63 -53
  233. package/src/lib/plugins/openapi/interfaces.ts +12 -0
  234. package/src/lib/plugins/openapi/playground/BodyPanel.tsx +246 -30
  235. package/src/lib/plugins/openapi/playground/CollapsibleHeader.tsx +10 -6
  236. package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +3 -2
  237. package/src/lib/plugins/openapi/playground/Headers.tsx +103 -219
  238. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +33 -1
  239. package/src/lib/plugins/openapi/playground/PathParams.tsx +26 -34
  240. package/src/lib/plugins/openapi/playground/Playground.tsx +73 -35
  241. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +9 -30
  242. package/src/lib/plugins/openapi/playground/QueryParams.tsx +82 -136
  243. package/src/lib/plugins/openapi/playground/request-panel/MultipartField.tsx +91 -0
  244. package/src/lib/plugins/openapi/playground/request-panel/UrlQueryParams.tsx +1 -1
  245. package/src/lib/plugins/openapi/playground/request-panel/fieldManager/useKeyValueFieldManager.test.tsx +872 -0
  246. package/src/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.ts +349 -0
  247. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +2 -6
  248. package/src/lib/plugins/openapi/schema/SchemaExampleAndDefault.tsx +1 -1
  249. package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +89 -52
  250. package/src/lib/plugins/openapi/schema/SchemaView.tsx +82 -48
  251. package/src/lib/plugins/openapi/schema/UnionView.tsx +6 -17
  252. package/src/lib/ui/Badge.tsx +21 -12
  253. package/src/lib/ui/Button.tsx +1 -0
  254. package/src/lib/ui/Checkbox.tsx +23 -24
  255. package/src/lib/ui/CodeBlock.tsx +3 -3
  256. package/src/lib/ui/Collapsible.tsx +26 -4
  257. package/src/lib/ui/EmbeddedCodeBlock.tsx +21 -18
  258. package/src/lib/ui/Frame.tsx +81 -0
  259. package/src/lib/ui/Item.tsx +192 -0
  260. package/src/lib/ui/NativeSelect.tsx +47 -0
  261. package/src/lib/ui/Select.tsx +153 -126
  262. package/src/lib/util/readFrontmatter.ts +13 -0
  263. package/dist/lib/plugins/openapi/playground/InlineInput.d.ts +0 -4
  264. package/dist/lib/plugins/openapi/playground/InlineInput.js +0 -3
  265. package/dist/lib/plugins/openapi/playground/InlineInput.js.map +0 -1
  266. package/lib/Button-DmS4u8Lj.js.map +0 -1
  267. package/lib/ErrorAlert--3alJ_-b.js.map +0 -1
  268. package/lib/OperationList-DLEAg4qw.js.map +0 -1
  269. package/lib/RouterError-DZS2d6Sc.js +0 -41
  270. package/lib/RouterError-DZS2d6Sc.js.map +0 -1
  271. package/lib/SchemaView-DJiBd0_5.js +0 -397
  272. package/lib/SchemaView-DJiBd0_5.js.map +0 -1
  273. package/lib/Select-C1DeCqKv.js +0 -372
  274. package/lib/Select-C1DeCqKv.js.map +0 -1
  275. package/lib/circular-DGfd8SGc.js.map +0 -1
  276. package/lib/createServer-DGD8hEzT.js.map +0 -1
  277. package/lib/index-DP1xZgfJ.js +0 -3364
  278. package/lib/index-DP1xZgfJ.js.map +0 -1
  279. package/src/lib/plugins/openapi/playground/InlineInput.tsx +0 -6
@@ -72,16 +72,20 @@ export type PathParam = {
72
72
 
73
73
  export type PlaygroundForm = {
74
74
  body: string;
75
- queryParams: Array<{
75
+ bodyMode?: "text" | "file" | "multipart";
76
+ file?: File | null;
77
+ multipartFormFields: Array<{
76
78
  name: string;
77
- value: string;
79
+ value: File | string;
78
80
  active: boolean;
79
- enum?: string[];
80
81
  }>;
81
- pathParams: Array<{
82
+ queryParams: Array<{
82
83
  name: string;
83
84
  value: string;
85
+ active: boolean;
86
+ enum?: string[];
84
87
  }>;
88
+ pathParams: Array<{ name: string; value: string }>;
85
89
  headers: Array<{
86
90
  name: string;
87
91
  value: string;
@@ -165,6 +169,9 @@ export const Playground = ({
165
169
  useForm<PlaygroundForm>({
166
170
  defaultValues: {
167
171
  body: defaultBody,
172
+ bodyMode: "text",
173
+ file: null,
174
+ multipartFormFields: [],
168
175
  queryParams:
169
176
  queryParams.length > 0
170
177
  ? queryParams.map((param) => ({
@@ -173,14 +180,7 @@ export const Playground = ({
173
180
  active: param.defaultActive ?? false,
174
181
  enum: param.enum ?? [],
175
182
  }))
176
- : [
177
- {
178
- name: "",
179
- value: "",
180
- active: false,
181
- enum: [],
182
- },
183
- ],
183
+ : [{ name: "", value: "", active: false, enum: [] }],
184
184
  pathParams: sortedPathParams.map((param) => ({
185
185
  name: param.name,
186
186
  value: param.defaultValue ?? "",
@@ -192,13 +192,7 @@ export const Playground = ({
192
192
  value: header.defaultValue ?? "",
193
193
  active: header.defaultActive ?? false,
194
194
  }))
195
- : [
196
- {
197
- name: "",
198
- value: "",
199
- active: false,
200
- },
201
- ],
195
+ : [{ name: "", value: "", active: false }],
202
196
  identity: getRememberedIdentity([
203
197
  NO_IDENTITY,
204
198
  ...(identities.data?.map((i) => i.id) ?? []),
@@ -223,19 +217,37 @@ export const Playground = ({
223
217
  mutationFn: async (data: PlaygroundForm) => {
224
218
  const start = performance.now();
225
219
 
226
- const headers = Object.fromEntries([
227
- ...data.headers
220
+ const headers = new window.Headers(
221
+ data.headers
228
222
  .filter((h) => h.name && h.active)
229
- .map((header) => [header.name, header.value]),
230
- ]);
223
+ .map<[string, string]>((h) => [h.name, h.value]),
224
+ );
225
+
226
+ let body: string | FormData | File | undefined;
227
+
228
+ switch (data.bodyMode) {
229
+ case "file":
230
+ body = data.file ?? undefined;
231
+ headers.delete("Content-Type");
232
+ break;
233
+ case "multipart": {
234
+ const formData = new FormData();
235
+ data.multipartFormFields
236
+ ?.filter((field) => field.name && field.active)
237
+ .forEach((field) => formData.append(field.name, field.value));
238
+
239
+ body = formData;
240
+ headers.delete("Content-Type");
241
+ break;
242
+ }
243
+ default:
244
+ body = data.body ?? undefined;
245
+ break;
246
+ }
231
247
 
232
248
  const request = new Request(
233
249
  createUrl(server ?? selectedServer, url, data),
234
- {
235
- method: method.toUpperCase(),
236
- headers,
237
- body: data.body ? data.body : undefined,
238
- },
250
+ { method, headers, body },
239
251
  );
240
252
 
241
253
  if (data.identity !== NO_IDENTITY) {
@@ -283,6 +295,31 @@ export const Playground = ({
283
295
 
284
296
  const responseSize = response.headers.get("content-length");
285
297
 
298
+ let requestBody = "";
299
+
300
+ switch (data.bodyMode) {
301
+ case "text":
302
+ requestBody = data.body;
303
+ break;
304
+ case "file":
305
+ requestBody = `[File: ${data.file?.name ?? "Unknown"}]`;
306
+ break;
307
+ case "multipart":
308
+ requestBody = "[Multipart Form Data]\n";
309
+ requestBody += data.multipartFormFields
310
+ ?.filter((f) => f.name && f.active)
311
+ .map((f) =>
312
+ f.value instanceof File
313
+ ? `${f.name}: [File: ${f.value.name}]`
314
+ : `${f.name}: ${f.value}`,
315
+ )
316
+ .join("\n");
317
+ break;
318
+ default:
319
+ requestBody = data.body;
320
+ break;
321
+ }
322
+
286
323
  return {
287
324
  status: response.status,
288
325
  headers: responseHeaders,
@@ -300,7 +337,7 @@ export const Playground = ({
300
337
  ["User-Agent", "Zudoku Playground"],
301
338
  ...Array.from(request.headers.entries()),
302
339
  ],
303
- body: data.body ? data.body : undefined,
340
+ body: requestBody,
304
341
  },
305
342
  } satisfies PlaygroundResult;
306
343
  } catch (error) {
@@ -375,6 +412,11 @@ export const Playground = ({
375
412
  <TooltipProvider delayDuration={150}>
376
413
  <form
377
414
  ref={formRef}
415
+ onKeyDown={(e) => {
416
+ if (e.key === "Enter" && e.target instanceof HTMLInputElement) {
417
+ e.preventDefault();
418
+ }
419
+ }}
378
420
  onSubmit={handleSubmit((data) => {
379
421
  if (identities.data?.length === 0 || data.identity) {
380
422
  queryMutation.mutate(data);
@@ -475,9 +517,7 @@ export const Playground = ({
475
517
  <Collapsible defaultOpen>
476
518
  <CollapsibleHeaderTrigger>
477
519
  <IdCardLanyardIcon size={16} />
478
- <CollapsibleHeader className="col-span-2">
479
- Authentication
480
- </CollapsibleHeader>
520
+ <CollapsibleHeader>Authentication</CollapsibleHeader>
481
521
  </CollapsibleHeaderTrigger>
482
522
  <CollapsibleContent className="CollapsibleContent">
483
523
  <IdentitySelector
@@ -493,9 +533,7 @@ export const Playground = ({
493
533
  <Collapsible defaultOpen>
494
534
  <CollapsibleHeaderTrigger>
495
535
  <ShapesIcon size={16} />
496
- <CollapsibleHeader className="col-span-2">
497
- Path Parameters
498
- </CollapsibleHeader>
536
+ <CollapsibleHeader>Path Parameters</CollapsibleHeader>
499
537
  </CollapsibleHeaderTrigger>
500
538
  <CollapsibleContent className="CollapsibleContent">
501
539
  <PathParams url={url} control={control} />
@@ -1,6 +1,8 @@
1
1
  import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
2
+ import { PlayIcon } from "lucide-react";
2
3
  import { type PropsWithChildren, useState } from "react";
3
4
  import { useAuth } from "zudoku/hooks";
5
+ import { Button } from "zudoku/ui/Button.js";
4
6
  import {
5
7
  Dialog,
6
8
  DialogContent,
@@ -11,29 +13,6 @@ import { Playground, type PlaygroundContentProps } from "./Playground.js";
11
13
 
12
14
  export type PlaygroundDialogProps = PropsWithChildren<PlaygroundContentProps>;
13
15
 
14
- const HeroPlayIcon = ({
15
- className,
16
- size = 16,
17
- }: {
18
- className?: string;
19
- size?: number;
20
- }) => (
21
- <svg
22
- xmlns="http://www.w3.org/2000/svg"
23
- viewBox="0 0 24 24"
24
- fill="currentColor"
25
- className={className}
26
- width={size}
27
- height={size}
28
- >
29
- <path
30
- fillRule="evenodd"
31
- d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm14.024-.983a1.125 1.125 0 0 1 0 1.966l-5.603 3.113A1.125 1.125 0 0 1 9 15.113V8.887c0-.857.921-1.4 1.671-.983l5.603 3.113Z"
32
- clipRule="evenodd"
33
- />
34
- </svg>
35
- );
36
-
37
16
  const PlaygroundDialog = (props: PlaygroundDialogProps) => {
38
17
  const [open, setOpen] = useState(false);
39
18
  const { isAuthEnabled, login, signup, isPending, isAuthenticated } =
@@ -43,13 +22,13 @@ const PlaygroundDialog = (props: PlaygroundDialogProps) => {
43
22
  <Dialog onOpenChange={(open) => setOpen(open)}>
44
23
  <DialogTrigger asChild>
45
24
  {props.children ?? (
46
- <button
47
- type="button"
48
- className="flex gap-1 items-center px-2 py-1 rounded-md transition text-xs bg-primary text-primary-foreground shadow-xs hover:bg-primary/80"
49
- >
50
- Test
51
- <HeroPlayIcon size={14} />
52
- </button>
25
+ <Button variant="ghost" size="icon-xs" className="group">
26
+ <PlayIcon
27
+ className="fill-muted-foreground group-hover:fill-foreground transition"
28
+ size={16}
29
+ strokeWidth={1.5}
30
+ />
31
+ </Button>
53
32
  )}
54
33
  </DialogTrigger>
55
34
 
@@ -1,23 +1,19 @@
1
- import { PlusCircleIcon, Unlink2Icon, XIcon } from "lucide-react";
2
- import { useEffect } from "react";
3
- import {
4
- type Control,
5
- Controller,
6
- useFieldArray,
7
- useFormContext,
8
- } from "react-hook-form";
9
- import { Button } from "zudoku/ui/Button.js";
1
+ import { Unlink2Icon } from "lucide-react";
2
+ import { type Control, useFormContext } from "react-hook-form";
10
3
  import { Checkbox } from "zudoku/ui/Checkbox.js";
11
4
  import { Collapsible, CollapsibleContent } from "zudoku/ui/Collapsible.js";
12
5
  import { Autocomplete } from "../../../components/Autocomplete.js";
13
- import { Input } from "../../../ui/Input.js";
14
6
  import {
15
7
  CollapsibleHeader,
16
8
  CollapsibleHeaderTrigger,
17
9
  } from "./CollapsibleHeader.js";
18
- import { InlineInput } from "./InlineInput.js";
19
- import ParamsGrid, { ParamsGridItem } from "./ParamsGrid.js";
10
+ import ParamsGrid, {
11
+ ParamsGridInput,
12
+ ParamsGridItem,
13
+ ParamsGridRemoveButton,
14
+ } from "./ParamsGrid.js";
20
15
  import type { PlaygroundForm, QueryParam } from "./Playground.js";
16
+ import { useKeyValueFieldManager } from "./request-panel/useKeyValueFieldManager.js";
21
17
 
22
18
  export const QueryParams = ({
23
19
  control,
@@ -26,148 +22,98 @@ export const QueryParams = ({
26
22
  control: Control<PlaygroundForm>;
27
23
  schemaQueryParams: QueryParam[];
28
24
  }) => {
29
- const { fields, remove, append } = useFieldArray<
30
- PlaygroundForm,
31
- "queryParams"
32
- >({
25
+ const { watch } = useFormContext<PlaygroundForm>();
26
+ const watchedQueryParams = watch("queryParams");
27
+
28
+ const manager = useKeyValueFieldManager<PlaygroundForm, "queryParams">({
33
29
  control,
34
30
  name: "queryParams",
31
+ defaultValue: { name: "", value: "", active: false },
35
32
  });
36
- const { setValue, getValues, watch } = useFormContext<PlaygroundForm>();
37
- const watchedQueryParams = watch("queryParams");
38
- useEffect(() => {
39
- if (watchedQueryParams.length === 0) {
40
- append({ name: "", value: "", active: false }, { shouldFocus: true });
41
- }
42
- }, [watchedQueryParams, append]);
43
33
 
44
34
  const requiredFields = schemaQueryParams.map((param) =>
45
35
  Boolean(param.isRequired),
46
36
  );
47
37
 
38
+ const hasSchemaParams = schemaQueryParams.length > 0;
39
+
48
40
  return (
49
41
  <Collapsible defaultOpen>
50
42
  <CollapsibleHeaderTrigger>
51
43
  <Unlink2Icon size={16} />
52
44
  <CollapsibleHeader>Query Parameters</CollapsibleHeader>
53
- <Button
54
- onClick={() => {
55
- setValue("queryParams", [
56
- ...getValues("queryParams"),
57
- { name: "", value: "", active: false },
58
- ]);
59
- }}
60
- type="button"
61
- size="sm"
62
- variant="ghost"
63
- className="hover:bg-accent hover:brightness-95 flex gap-2"
64
- >
65
- Add parameter <PlusCircleIcon size={16} />
66
- </Button>
67
45
  </CollapsibleHeaderTrigger>
68
46
  <CollapsibleContent className="CollapsibleContent">
69
- <div className="overflow-hidden w-full">
70
- <ParamsGrid>
71
- {fields.map((field, i) => {
72
- const currentParam = schemaQueryParams.find(
73
- (param) => param.name === watchedQueryParams.at(i)?.name,
74
- );
75
- return (
76
- <ParamsGridItem key={field.id}>
77
- <Controller
78
- control={control}
79
- name={`queryParams.${i}.active`}
80
- render={({ field }) => (
81
- <Checkbox
82
- id={`queryParams.${i}.active`}
83
- checked={field.value}
84
- onCheckedChange={field.onChange}
85
- />
86
- )}
87
- />
88
- <Controller
89
- control={control}
90
- render={({ field }) =>
91
- !requiredFields[i] ? (
92
- <Autocomplete
93
- placeholder="Name"
94
- value={field.value}
95
- options={schemaQueryParams.map((param) => param.name)}
96
- onChange={(e) => {
97
- field.onChange(e);
98
- }}
99
- className="border-0 p-0 m-0 shadow-none focus-visible:ring-0 bg-transparent hover:bg-transparent text-xs font-mono"
100
- />
101
- ) : (
102
- <InlineInput asChild>
103
- <label
104
- className="flex items-center cursor-pointer gap-1"
105
- htmlFor={`queryParams.${i}.active`}
106
- title={
107
- requiredFields[i] ? "Required field" : undefined
108
- }
109
- >
110
- {field.value}
111
- {requiredFields[i] && <sup>&nbsp;*</sup>}
112
- </label>
113
- </InlineInput>
114
- )
115
- }
116
- name={`queryParams.${i}.name`}
117
- />
118
- <div className="flex justify-between items-center">
119
- <Controller
120
- control={control}
121
- render={({ field }) => {
122
- const hasEnum =
123
- currentParam?.enum && currentParam.enum.length > 0;
47
+ <ParamsGrid>
48
+ {manager.fields.map((field, i) => {
49
+ const currentParam = schemaQueryParams.find(
50
+ (param) => param.name === watchedQueryParams.at(i)?.name,
51
+ );
52
+ const hasEnum = currentParam?.enum && currentParam.enum.length > 0;
53
+ const nameInputProps = manager.getNameInputProps(i);
54
+ const valueInputProps = manager.getValueInputProps(i);
124
55
 
125
- if (!hasEnum) {
126
- return (
127
- <Input
128
- {...field}
129
- onChange={(e) => {
130
- field.onChange(e.target.value);
131
- if (e.target.value.length > 0) {
132
- setValue(`queryParams.${i}.active`, true);
133
- }
134
- }}
135
- placeholder="Value"
136
- aria-label="Query parameter value"
137
- className="w-full border-0 p-0 m-0 shadow-none focus-visible:ring-0 text-xs font-mono"
138
- />
139
- );
56
+ return (
57
+ <ParamsGridItem key={field.id}>
58
+ <Checkbox {...manager.getCheckboxProps(i)} />
59
+ {!requiredFields[i] ? (
60
+ hasSchemaParams ? (
61
+ <ParamsGridInput asChild>
62
+ <Autocomplete
63
+ {...nameInputProps}
64
+ value={String(manager.getValue(i, "name"))}
65
+ placeholder="Name"
66
+ options={schemaQueryParams.map((param) => param.name)}
67
+ onChange={(v) => manager.setValue(i, "name", v)}
68
+ onSelect={(v) =>
69
+ manager.setValue(i, "name", v, { focus: "next" })
140
70
  }
141
-
142
- return (
143
- <Autocomplete
144
- value={field.value}
145
- options={currentParam.enum ?? []}
146
- onChange={(e) => {
147
- field.onChange(e);
148
- setValue(`queryParams.${i}.active`, true);
149
- }}
150
- className="border-0 shadow-none focus-visible:ring-0 bg-transparent hover:bg-transparent text-xs font-mono"
151
- />
152
- );
153
- }}
154
- name={`queryParams.${i}.value`}
155
- />
156
- <Button
157
- size="icon-xs"
158
- variant="ghost"
159
- className="text-muted-foreground opacity-0 group-hover:brightness-95 group-hover:opacity-100"
160
- onClick={() => remove(i)}
161
- type="button"
71
+ />
72
+ </ParamsGridInput>
73
+ ) : (
74
+ <ParamsGridInput {...nameInputProps} placeholder="Name" />
75
+ )
76
+ ) : (
77
+ <ParamsGridInput asChild>
78
+ <label
79
+ className="flex items-center cursor-pointer gap-1"
80
+ htmlFor={`queryParams.${i}.active`}
81
+ title={requiredFields[i] ? "Required field" : undefined}
162
82
  >
163
- <XIcon size={16} />
164
- </Button>
165
- </div>
166
- </ParamsGridItem>
167
- );
168
- })}
169
- </ParamsGrid>
170
- </div>
83
+ {watchedQueryParams[i]?.name}
84
+ {requiredFields[i] && <sup>&nbsp;*</sup>}
85
+ </label>
86
+ </ParamsGridInput>
87
+ )}
88
+ <div className="flex justify-between items-center">
89
+ {!hasEnum ? (
90
+ <ParamsGridInput
91
+ placeholder="Value"
92
+ aria-label="Query parameter value"
93
+ {...valueInputProps}
94
+ />
95
+ ) : (
96
+ <ParamsGridInput asChild>
97
+ <Autocomplete
98
+ {...valueInputProps}
99
+ value={String(manager.getValue(i, "value"))}
100
+ shouldFilter={false}
101
+ options={currentParam.enum ?? []}
102
+ onChange={(v) => manager.setValue(i, "value", v)}
103
+ onSelect={(v) =>
104
+ manager.setValue(i, "value", v, { focus: "next" })
105
+ }
106
+ />
107
+ </ParamsGridInput>
108
+ )}
109
+ <ParamsGridRemoveButton
110
+ {...manager.getRemoveButtonProps(i)}
111
+ />
112
+ </div>
113
+ </ParamsGridItem>
114
+ );
115
+ })}
116
+ </ParamsGrid>
171
117
  </CollapsibleContent>
172
118
  </Collapsible>
173
119
  );
@@ -0,0 +1,91 @@
1
+ import { PaperclipIcon, TrashIcon } from "lucide-react";
2
+ import { useRef } from "react";
3
+ import { Button } from "zudoku/components";
4
+ import { Checkbox } from "zudoku/ui/Checkbox.js";
5
+ import { humanFileSize } from "../../../../util/humanFileSize.js";
6
+ import {
7
+ ParamsGridInput,
8
+ ParamsGridItem,
9
+ ParamsGridRemoveButton,
10
+ } from "../ParamsGrid.js";
11
+ import type { PlaygroundForm } from "../Playground.js";
12
+ import type { useKeyValueFieldManager } from "./useKeyValueFieldManager.js";
13
+
14
+ type MultipartFieldProps = {
15
+ index: number;
16
+ manager: ReturnType<
17
+ typeof useKeyValueFieldManager<PlaygroundForm, "multipartFormFields">
18
+ >;
19
+ };
20
+
21
+ export const MultipartField = ({ index, manager }: MultipartFieldProps) => {
22
+ const fieldFileInputRef = useRef<HTMLInputElement>(null);
23
+ const fieldValue = manager.getValue(index, "value");
24
+
25
+ return (
26
+ <ParamsGridItem>
27
+ <Checkbox
28
+ {...manager.getCheckboxProps(index)}
29
+ disabled={!manager.getValue(index, "name")}
30
+ />
31
+ <ParamsGridInput
32
+ {...manager.getNameInputProps(index)}
33
+ placeholder="Key"
34
+ />
35
+ <div className="flex items-center gap-1 flex-1">
36
+ {fieldValue instanceof File ? (
37
+ <div className="flex items-center gap-1 flex-1 min-w-0">
38
+ <div className="flex items-center gap-1 border-b cursor-default">
39
+ <PaperclipIcon size={12} className="text-muted-foreground" />
40
+ <span
41
+ className="text-xs truncate"
42
+ title={`${fieldValue.name} (${humanFileSize(fieldValue.size)})`}
43
+ >
44
+ {fieldValue.name}
45
+ </span>
46
+ </div>
47
+ <Button
48
+ type="button"
49
+ variant="ghost"
50
+ size="icon-xs"
51
+ className="opacity-50 hover:opacity-100 hover:brightness-95 transition-opacity"
52
+ onClick={() => manager.setValue(index, "value", "")}
53
+ >
54
+ <TrashIcon size={13} />
55
+ </Button>
56
+ </div>
57
+ ) : (
58
+ <>
59
+ <ParamsGridInput
60
+ {...manager.getValueInputProps(index)}
61
+ placeholder="Value"
62
+ />
63
+ <input
64
+ ref={fieldFileInputRef}
65
+ type="file"
66
+ className="hidden"
67
+ onChange={(e) => {
68
+ const file = e.target.files?.[0];
69
+ if (!file) return;
70
+
71
+ manager.setValue(index, "value", file);
72
+ manager.setValue(index, "active", true);
73
+ }}
74
+ />
75
+ <Button
76
+ type="button"
77
+ variant="ghost"
78
+ size="icon-xs"
79
+ onClick={() => fieldFileInputRef.current?.click()}
80
+ title="Attach file"
81
+ className="opacity-0 focus-visible:opacity-100 group-hover:opacity-100 group-hover:brightness-95 transition-opacity"
82
+ >
83
+ <PaperclipIcon size={14} />
84
+ </Button>
85
+ </>
86
+ )}
87
+ <ParamsGridRemoveButton {...manager.getRemoveButtonProps(index)} />
88
+ </div>
89
+ </ParamsGridItem>
90
+ );
91
+ };
@@ -9,7 +9,7 @@ export const UrlQueryParams = () => {
9
9
  const urlQueryParams = queryParams
10
10
  .filter((p) => p.active && p.name)
11
11
  .map((p, i, arr) => (
12
- <Fragment key={p.name}>
12
+ <Fragment key={`${i}-${p.name}`}>
13
13
  {p.name}={encodeURIComponent(p.value).replaceAll("%20", "+")}
14
14
  {i < arr.length - 1 && "&"}
15
15
  <wbr />