zudoku 0.26.1 → 0.27.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 (214) hide show
  1. package/dist/app/main.d.ts +1 -1
  2. package/dist/app/main.js +19 -7
  3. package/dist/app/main.js.map +1 -1
  4. package/dist/config/loader.js +1 -1
  5. package/dist/config/loader.js.map +1 -1
  6. package/dist/config/validators/InputSidebarSchema.d.ts +2 -2
  7. package/dist/config/validators/common.d.ts +67 -0
  8. package/dist/config/validators/common.js +5 -0
  9. package/dist/config/validators/common.js.map +1 -1
  10. package/dist/config/validators/validate.d.ts +29 -0
  11. package/dist/lib/components/AnchorLink.js +5 -2
  12. package/dist/lib/components/AnchorLink.js.map +1 -1
  13. package/dist/lib/components/Header.js +1 -1
  14. package/dist/lib/components/Header.js.map +1 -1
  15. package/dist/lib/components/Heading.d.ts +1 -1
  16. package/dist/lib/components/Markdown.d.ts +2 -2
  17. package/dist/lib/components/Markdown.js +3 -1
  18. package/dist/lib/components/Markdown.js.map +1 -1
  19. package/dist/lib/components/StatusPage.d.ts +7 -0
  20. package/dist/lib/components/StatusPage.js +71 -0
  21. package/dist/lib/components/StatusPage.js.map +1 -0
  22. package/dist/lib/components/SyntaxHighlight.d.ts +2 -1
  23. package/dist/lib/components/SyntaxHighlight.js +2 -2
  24. package/dist/lib/components/SyntaxHighlight.js.map +1 -1
  25. package/dist/lib/components/ThemeSwitch.js +4 -4
  26. package/dist/lib/components/ThemeSwitch.js.map +1 -1
  27. package/dist/lib/components/cache.d.ts +6 -0
  28. package/dist/lib/components/cache.js +13 -0
  29. package/dist/lib/components/cache.js.map +1 -0
  30. package/dist/lib/components/context/ViewportAnchorContext.js +16 -4
  31. package/dist/lib/components/context/ViewportAnchorContext.js.map +1 -1
  32. package/dist/lib/components/context/ZudokuContext.js +2 -1
  33. package/dist/lib/components/context/ZudokuContext.js.map +1 -1
  34. package/dist/lib/components/index.d.ts +9 -2
  35. package/dist/lib/components/index.js +3 -0
  36. package/dist/lib/components/index.js.map +1 -1
  37. package/dist/lib/core/RouteGuard.d.ts +1 -0
  38. package/dist/lib/core/RouteGuard.js +28 -0
  39. package/dist/lib/core/RouteGuard.js.map +1 -0
  40. package/dist/lib/core/ZudokuContext.d.ts +4 -2
  41. package/dist/lib/core/ZudokuContext.js +9 -7
  42. package/dist/lib/core/ZudokuContext.js.map +1 -1
  43. package/dist/lib/oas/graphql/circular.d.ts +3 -0
  44. package/dist/lib/oas/graphql/circular.js +27 -0
  45. package/dist/lib/oas/graphql/circular.js.map +1 -0
  46. package/dist/lib/oas/graphql/index.js +5 -6
  47. package/dist/lib/oas/graphql/index.js.map +1 -1
  48. package/dist/lib/oas/parser/dereference/index.d.ts +0 -1
  49. package/dist/lib/oas/parser/dereference/index.js +1 -1
  50. package/dist/lib/oas/parser/dereference/index.js.map +1 -1
  51. package/dist/lib/plugins/openapi/OperationListItem.js +1 -1
  52. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  53. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  54. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  55. package/dist/lib/plugins/openapi/Sidecar.js +2 -2
  56. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  57. package/dist/lib/plugins/openapi/index.js +4 -11
  58. package/dist/lib/plugins/openapi/index.js.map +1 -1
  59. package/dist/lib/plugins/openapi/interfaces.d.ts +7 -2
  60. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js +5 -5
  61. package/dist/lib/plugins/openapi/playground/ExamplesDropdown.js.map +1 -1
  62. package/dist/lib/plugins/openapi/playground/Headers.js +17 -16
  63. package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -1
  64. package/dist/lib/plugins/openapi/playground/ParamsGrid.d.ts +5 -0
  65. package/dist/lib/plugins/openapi/playground/ParamsGrid.js +4 -0
  66. package/dist/lib/plugins/openapi/playground/ParamsGrid.js.map +1 -0
  67. package/dist/lib/plugins/openapi/playground/PathParams.js +4 -12
  68. package/dist/lib/plugins/openapi/playground/PathParams.js.map +1 -1
  69. package/dist/lib/plugins/openapi/playground/Playground.d.ts +13 -0
  70. package/dist/lib/plugins/openapi/playground/Playground.js +19 -31
  71. package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
  72. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js +1 -1
  73. package/dist/lib/plugins/openapi/playground/PlaygroundDialog.js.map +1 -1
  74. package/dist/lib/plugins/openapi/playground/QueryParams.js +4 -3
  75. package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -1
  76. package/dist/lib/plugins/openapi/playground/SubmitButton.d.ts +7 -0
  77. package/dist/lib/plugins/openapi/playground/SubmitButton.js +22 -0
  78. package/dist/lib/plugins/openapi/playground/SubmitButton.js.map +1 -0
  79. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.d.ts +7 -0
  80. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.js +11 -0
  81. package/dist/lib/plugins/openapi/playground/result-panel/RequestTab.js.map +1 -0
  82. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.d.ts +8 -0
  83. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js +95 -0
  84. package/dist/lib/plugins/openapi/playground/result-panel/ResponseTab.js.map +1 -0
  85. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.d.ts +7 -0
  86. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js +16 -0
  87. package/dist/lib/plugins/openapi/playground/result-panel/ResultPanel.js.map +1 -0
  88. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.d.ts +10 -0
  89. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.js +32 -0
  90. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.js.map +1 -0
  91. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.test.d.ts +1 -0
  92. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.test.js +56 -0
  93. package/dist/lib/plugins/openapi/playground/result-panel/convertToTypes.test.js.map +1 -0
  94. package/dist/lib/plugins/openapi/schema/SchemaComponents.js +1 -1
  95. package/dist/lib/plugins/openapi/schema/SchemaComponents.js.map +1 -1
  96. package/dist/lib/ui/Command.js +1 -1
  97. package/dist/lib/ui/Command.js.map +1 -1
  98. package/dist/lib/ui/Select.js +2 -2
  99. package/dist/lib/ui/Select.js.map +1 -1
  100. package/dist/lib/util/MdxComponents.js +2 -2
  101. package/dist/lib/util/MdxComponents.js.map +1 -1
  102. package/dist/lib/util/useScrollToAnchor.d.ts +1 -0
  103. package/dist/lib/util/useScrollToAnchor.js +26 -15
  104. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  105. package/dist/vite/plugin-api.js +9 -3
  106. package/dist/vite/plugin-api.js.map +1 -1
  107. package/dist/vite/prerender.js +1 -0
  108. package/dist/vite/prerender.js.map +1 -1
  109. package/dist/zuplo/enrich-with-zuplo.js +1 -1
  110. package/dist/zuplo/enrich-with-zuplo.js.map +1 -1
  111. package/dist/zuplo/with-zuplo.d.ts +2 -1
  112. package/dist/zuplo/with-zuplo.js +3 -1
  113. package/dist/zuplo/with-zuplo.js.map +1 -1
  114. package/lib/{AuthenticationPlugin-C9SwOxkc.js → AuthenticationPlugin-CO_YCd2x.js} +3 -3
  115. package/lib/{AuthenticationPlugin-C9SwOxkc.js.map → AuthenticationPlugin-CO_YCd2x.js.map} +1 -1
  116. package/lib/{Markdown-DFN6p0J-.js → Markdown-B8o9Qz4q.js} +1197 -1186
  117. package/lib/{Markdown-DFN6p0J-.js.map → Markdown-B8o9Qz4q.js.map} +1 -1
  118. package/lib/{MdxPage-D9c4z09Q.js → MdxPage-BxRt3Ly7.js} +5 -5
  119. package/lib/{MdxPage-D9c4z09Q.js.map → MdxPage-BxRt3Ly7.js.map} +1 -1
  120. package/lib/OperationList-DH-zIgtq.js +5160 -0
  121. package/lib/OperationList-DH-zIgtq.js.map +1 -0
  122. package/lib/{Route-VdmEyOD0.js → Route-DJ0ZlVq1.js} +3 -3
  123. package/lib/{Route-VdmEyOD0.js.map → Route-DJ0ZlVq1.js.map} +1 -1
  124. package/lib/{Select-D3O7wISy.js → Select-B7UXR0SB.js} +61 -61
  125. package/lib/Select-B7UXR0SB.js.map +1 -0
  126. package/lib/{SlotletProvider-_3zzX_g_.js → SlotletProvider-CtIp8rP3.js} +4 -4
  127. package/lib/{SlotletProvider-_3zzX_g_.js.map → SlotletProvider-CtIp8rP3.js.map} +1 -1
  128. package/lib/{SyntaxHighlight-CJCSPG1F.js → SyntaxHighlight-C1w1QPdY.js} +300 -295
  129. package/lib/{SyntaxHighlight-CJCSPG1F.js.map → SyntaxHighlight-C1w1QPdY.js.map} +1 -1
  130. package/lib/{ZudokuContext-DeQZEp-x.js → ZudokuContext-8jts0fF3.js} +259 -248
  131. package/lib/ZudokuContext-8jts0fF3.js.map +1 -0
  132. package/lib/{chunk-SYFQ2XB5-BF5IDYrB.js → chunk-SYFQ2XB5-BPvC-soB.js} +5 -5
  133. package/lib/{chunk-SYFQ2XB5-BF5IDYrB.js.map → chunk-SYFQ2XB5-BPvC-soB.js.map} +1 -1
  134. package/lib/circular-Dgpd6AN-.js +15397 -0
  135. package/lib/circular-Dgpd6AN-.js.map +1 -0
  136. package/lib/{createServer-BcaswoFO.js → createServer-BV0tHzLK.js} +3450 -5577
  137. package/lib/createServer-BV0tHzLK.js.map +1 -0
  138. package/lib/{hook-BRQEDRbn.js → hook-BG02esyv.js} +2 -2
  139. package/lib/{hook-BRQEDRbn.js.map → hook-BG02esyv.js.map} +1 -1
  140. package/lib/index-DmqsUPcm.js +1915 -0
  141. package/lib/index-DmqsUPcm.js.map +1 -0
  142. package/lib/ui/Command.js +27 -27
  143. package/lib/ui/Command.js.map +1 -1
  144. package/lib/ui/Select.js +2 -2
  145. package/lib/ui/Select.js.map +1 -1
  146. package/lib/{useExposedProps-CetwhZpP.js → useExposedProps-BLKFBylA.js} +2 -2
  147. package/lib/{useExposedProps-CetwhZpP.js.map → useExposedProps-BLKFBylA.js.map} +1 -1
  148. package/lib/useScrollToAnchor-Bl6mz9_x.js +288 -0
  149. package/lib/useScrollToAnchor-Bl6mz9_x.js.map +1 -0
  150. package/lib/zudoku.auth-clerk.js +1 -1
  151. package/lib/zudoku.auth-openid.js +3 -3
  152. package/lib/zudoku.components.js +753 -991
  153. package/lib/zudoku.components.js.map +1 -1
  154. package/lib/zudoku.plugin-api-catalog.js +3 -3
  155. package/lib/zudoku.plugin-api-keys.js +5 -5
  156. package/lib/zudoku.plugin-custom-pages.js +2 -2
  157. package/lib/zudoku.plugin-markdown.js +1 -1
  158. package/lib/zudoku.plugin-openapi.js +4 -4
  159. package/lib/zudoku.plugin-redirect.js +1 -1
  160. package/package.json +2 -2
  161. package/src/app/main.tsx +26 -7
  162. package/src/lib/components/AnchorLink.tsx +5 -2
  163. package/src/lib/components/Header.tsx +1 -1
  164. package/src/lib/components/Markdown.tsx +14 -15
  165. package/src/lib/components/StatusPage.tsx +91 -0
  166. package/src/lib/components/SyntaxHighlight.tsx +14 -0
  167. package/src/lib/components/ThemeSwitch.tsx +14 -15
  168. package/src/lib/components/cache.ts +15 -0
  169. package/src/lib/components/context/ViewportAnchorContext.tsx +20 -6
  170. package/src/lib/components/context/ZudokuContext.ts +3 -1
  171. package/src/lib/components/index.ts +7 -0
  172. package/src/lib/core/RouteGuard.tsx +35 -0
  173. package/src/lib/core/ZudokuContext.ts +9 -8
  174. package/src/lib/oas/graphql/circular.ts +29 -0
  175. package/src/lib/oas/graphql/index.ts +9 -9
  176. package/src/lib/oas/parser/dereference/index.ts +1 -2
  177. package/src/lib/plugins/openapi/OperationListItem.tsx +0 -2
  178. package/src/lib/plugins/openapi/ParameterListItem.tsx +1 -0
  179. package/src/lib/plugins/openapi/Sidecar.tsx +3 -2
  180. package/src/lib/plugins/openapi/index.tsx +9 -15
  181. package/src/lib/plugins/openapi/interfaces.ts +10 -2
  182. package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +30 -27
  183. package/src/lib/plugins/openapi/playground/Headers.tsx +65 -65
  184. package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +8 -0
  185. package/src/lib/plugins/openapi/playground/PathParams.tsx +34 -74
  186. package/src/lib/plugins/openapi/playground/Playground.tsx +64 -116
  187. package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +1 -1
  188. package/src/lib/plugins/openapi/playground/QueryParams.tsx +46 -45
  189. package/src/lib/plugins/openapi/playground/SubmitButton.tsx +75 -0
  190. package/src/lib/plugins/openapi/playground/result-panel/RequestTab.tsx +73 -0
  191. package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +210 -0
  192. package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +101 -0
  193. package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.test.ts +64 -0
  194. package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.ts +36 -0
  195. package/src/lib/plugins/openapi/schema/SchemaComponents.tsx +1 -1
  196. package/src/lib/ui/Command.tsx +1 -1
  197. package/src/lib/ui/Select.tsx +1 -1
  198. package/src/lib/util/MdxComponents.tsx +2 -1
  199. package/src/lib/util/useScrollToAnchor.ts +32 -15
  200. package/dist/lib/plugins/openapi/playground/ResponseTab.d.ts +0 -4
  201. package/dist/lib/plugins/openapi/playground/ResponseTab.js +0 -42
  202. package/dist/lib/plugins/openapi/playground/ResponseTab.js.map +0 -1
  203. package/lib/AnchorLink-bObQitZv.js +0 -34
  204. package/lib/AnchorLink-bObQitZv.js.map +0 -1
  205. package/lib/OperationList-DGJWDx1G.js +0 -5148
  206. package/lib/OperationList-DGJWDx1G.js.map +0 -1
  207. package/lib/Select-D3O7wISy.js.map +0 -1
  208. package/lib/ZudokuContext-DeQZEp-x.js.map +0 -1
  209. package/lib/createServer-BcaswoFO.js.map +0 -1
  210. package/lib/index-CXRrqOIl.js +0 -1750
  211. package/lib/index-CXRrqOIl.js.map +0 -1
  212. package/lib/index-TaRXY2w1.js +0 -43
  213. package/lib/index-TaRXY2w1.js.map +0 -1
  214. package/src/lib/plugins/openapi/playground/ResponseTab.tsx +0 -76
@@ -0,0 +1,210 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { ChevronRightIcon } from "lucide-react";
3
+ import { Fragment, useState } from "react";
4
+ import {
5
+ Collapsible,
6
+ CollapsibleContent,
7
+ CollapsibleTrigger,
8
+ } from "zudoku/ui/Collapsible.js";
9
+ import {
10
+ Select,
11
+ SelectContent,
12
+ SelectItem,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ } from "zudoku/ui/Select.js";
16
+ import { SyntaxHighlight } from "../../../../components/SyntaxHighlight.js";
17
+ import { Card } from "../../../../ui/Card.js";
18
+ import { convertToTypes } from "./convertToTypes.js";
19
+
20
+ const statusCodeMap: Record<number, string> = {
21
+ 200: "OK",
22
+ 201: "Created",
23
+ 202: "Accepted",
24
+ 204: "No Content",
25
+ 400: "Bad Request",
26
+ 401: "Unauthorized",
27
+ 403: "Forbidden",
28
+ 404: "Not Found",
29
+ 405: "Method Not Allowed",
30
+ 500: "Internal Server Error",
31
+ };
32
+
33
+ const mimeTypeToLanguage = (mimeType: string) => {
34
+ const mimeTypeMapping = {
35
+ "application/json": "json",
36
+ "text/json": "json",
37
+ "text/html": "html",
38
+ "text/css": "css",
39
+ "text/javascript": "javascript",
40
+ "application/xml": "xml",
41
+ "application/xhtml+xml": "xhtml",
42
+ };
43
+
44
+ return Object.entries(mimeTypeMapping).find(([mime]) =>
45
+ mimeType.includes(mime),
46
+ )?.[1];
47
+ };
48
+
49
+ const detectLanguage = (headers: Array<[string, string]>) => {
50
+ const contentType =
51
+ headers.find(([key, value]) => key === "Content-Type")?.[1] || "";
52
+ return mimeTypeToLanguage(contentType);
53
+ };
54
+
55
+ const tryParseJson = (body: string) => {
56
+ try {
57
+ return JSON.stringify(JSON.parse(body), null, 2);
58
+ } catch {
59
+ return null;
60
+ }
61
+ };
62
+
63
+ const sortHeadersByRelevance = (
64
+ headers: Array<[string, string]>,
65
+ ): Array<[string, string]> => {
66
+ const priorityOrder = [
67
+ "Content-Type",
68
+ "Content-Length",
69
+ "Authorization",
70
+ "X-RateLimit-Remaining",
71
+ "X-RateLimit-Limit",
72
+ "Cache-Control",
73
+ "ETag",
74
+ ].map((key) => key.toLowerCase());
75
+
76
+ return [...headers].sort(([keyA], [keyB]) => {
77
+ const indexA = priorityOrder.indexOf(keyA.toLowerCase());
78
+ const indexB = priorityOrder.indexOf(keyB.toLowerCase());
79
+ if (indexA === indexB) return 0;
80
+ if (indexA === -1) return 1;
81
+ if (indexB === -1) return -1;
82
+ return indexA - indexB;
83
+ });
84
+ };
85
+
86
+ export const ResponseTab = ({
87
+ body = "",
88
+ headers,
89
+ status,
90
+ time,
91
+ size,
92
+ url,
93
+ }: {
94
+ body?: string;
95
+ headers: Array<[string, string]>;
96
+ status: number;
97
+ time: number;
98
+ size: number;
99
+ url: string;
100
+ }) => {
101
+ const detectedLanguage = detectLanguage(headers);
102
+ const jsonContent = tryParseJson(body);
103
+ const beautifiedBody = jsonContent || body;
104
+ const [view, setView] = useState<"formatted" | "raw" | "types">(
105
+ jsonContent ? "formatted" : "raw",
106
+ );
107
+
108
+ const types = useQuery({
109
+ queryKey: ["types", beautifiedBody],
110
+ queryFn: async () => {
111
+ return convertToTypes(JSON.parse(beautifiedBody));
112
+ },
113
+ enabled: view === "types",
114
+ });
115
+
116
+ const sortedHeaders = sortHeadersByRelevance([...headers]);
117
+
118
+ return (
119
+ <div className="flex flex-col gap-2 h-full overflow-y-scroll max-h-[calc(100vh-220px)] py-4">
120
+ <Collapsible defaultOpen>
121
+ <CollapsibleTrigger className="flex items-center gap-2 hover:text-primary group">
122
+ <ChevronRightIcon className="h-4 w-4 transition-transform duration-200 group-data-[state=open]:rotate-[90deg]" />
123
+ <span className="font-semibold">Headers</span>
124
+ </CollapsibleTrigger>
125
+ <CollapsibleContent>
126
+ <div className="grid grid-cols-[auto,1fr] gap-x-8 gap-y-1 pl-1.5 pt-2 font-mono text-xs">
127
+ {sortedHeaders.slice(0, 5).map(([key, value]) => (
128
+ <Fragment key={key}>
129
+ <div className="text-primary whitespace-pre">{key}</div>
130
+ <div className="break-all">{value}</div>
131
+ </Fragment>
132
+ ))}
133
+ {sortedHeaders.length > 5 && (
134
+ <Collapsible className="col-span-full grid-cols-subgrid grid">
135
+ <CollapsibleTrigger className="col-span-2 text-xs text-muted-foreground hover:text-primary flex items-center gap-1 py-1">
136
+ <ChevronRightIcon className="h-3 w-3 transition-transform duration-200 group-data-[state=open]:rotate-[90deg]" />
137
+ Show {sortedHeaders.length - 5} more headers
138
+ </CollapsibleTrigger>
139
+ <CollapsibleContent className="col-span-full grid grid-cols-subgrid gap-x-8 gap-y-1 ">
140
+ {sortedHeaders.slice(5).map(([key, value]) => (
141
+ <Fragment key={key}>
142
+ <div className="text-primary whitespace-pre">{key}</div>
143
+ <div className="break-all">{value}</div>
144
+ </Fragment>
145
+ ))}
146
+ </CollapsibleContent>
147
+ </Collapsible>
148
+ )}
149
+ </div>
150
+ </CollapsibleContent>
151
+ </Collapsible>
152
+
153
+ <Card className="shadow-none">
154
+ <SyntaxHighlight
155
+ language={
156
+ view === "types"
157
+ ? "typescript"
158
+ : view === "raw"
159
+ ? jsonContent
160
+ ? "plain"
161
+ : detectedLanguage
162
+ : "json"
163
+ }
164
+ noBackground
165
+ // playground dialog has h-5/6 ≈ 83.333vh
166
+ className="overflow-x-auto p-4 text-xs max-h-[calc(83.333vh-180px)]"
167
+ code={
168
+ (view === "raw"
169
+ ? body
170
+ : view === "types"
171
+ ? types.data?.lines.join("\n")
172
+ : beautifiedBody) ?? ""
173
+ }
174
+ />
175
+ </Card>
176
+ <div className="flex gap-2 justify-between">
177
+ <div className="flex text-xs gap-2 border bg-muted rounded-md p-2 items-center h-8 font-mono divide-x">
178
+ <div>
179
+ <span className="text-muted-foreground">Status</span> {status}{" "}
180
+ {statusCodeMap[status] ?? ""}
181
+ </div>
182
+ <div>
183
+ <span className="text-muted-foreground">Time</span>{" "}
184
+ {time.toFixed(0)}ms
185
+ </div>
186
+ <div>
187
+ <span className="text-muted-foreground">Size</span> {size}B
188
+ </div>
189
+ </div>
190
+ {jsonContent && (
191
+ <div>
192
+ <Select
193
+ value={view}
194
+ onValueChange={(value) => setView(value as "formatted" | "raw")}
195
+ >
196
+ <SelectTrigger className="min-w-32">
197
+ <SelectValue placeholder="View" />
198
+ </SelectTrigger>
199
+ <SelectContent>
200
+ <SelectItem value="formatted">Formatted</SelectItem>
201
+ <SelectItem value="raw">Raw</SelectItem>
202
+ <SelectItem value="types">Types</SelectItem>
203
+ </SelectContent>
204
+ </Select>
205
+ </div>
206
+ )}
207
+ </div>
208
+ </div>
209
+ );
210
+ };
@@ -0,0 +1,101 @@
1
+ import { UseMutationResult } from "@tanstack/react-query";
2
+ import { Spinner } from "../../../../components/Spinner.js";
3
+ import { Callout } from "../../../../ui/Callout.js";
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from "../../../../ui/Card.js";
10
+ import {
11
+ Tabs,
12
+ TabsContent,
13
+ TabsList,
14
+ TabsTrigger,
15
+ } from "../../../../ui/Tabs.js";
16
+ import { cn } from "../../../../util/cn.js";
17
+ import { PlaygroundResult } from "../Playground.js";
18
+ import { RequestTab } from "./RequestTab.js";
19
+ import { ResponseTab } from "./ResponseTab.js";
20
+
21
+ export const ResultPanel = ({
22
+ queryMutation,
23
+ showPathParamsWarning,
24
+ }: {
25
+ queryMutation: UseMutationResult<PlaygroundResult, Error, any, unknown>;
26
+ showPathParamsWarning: boolean;
27
+ }) => {
28
+ const status = ((queryMutation.data?.status ?? 0) / 100).toFixed(0);
29
+ return (
30
+ <div className="min-w-0 p-8 bg-muted/70 overflow-y-auto">
31
+ {queryMutation.error ? (
32
+ <div className="flex flex-col gap-2">
33
+ {showPathParamsWarning && (
34
+ <Callout type="caution">
35
+ Some path parameters are missing values. Please fill them in to
36
+ ensure the request is sent correctly.
37
+ </Callout>
38
+ )}
39
+ <Card>
40
+ <CardHeader>
41
+ <CardTitle>Request failed</CardTitle>
42
+ </CardHeader>
43
+ <CardContent>
44
+ Error:{" "}
45
+ {queryMutation.error.message ||
46
+ String(queryMutation.error) ||
47
+ "Unexpected error"}
48
+ </CardContent>
49
+ </Card>
50
+ </div>
51
+ ) : queryMutation.data ? (
52
+ <div className="flex flex-col gap-2">
53
+ <Tabs defaultValue="response">
54
+ <TabsList>
55
+ <TabsTrigger value="request">Request</TabsTrigger>
56
+ <TabsTrigger value="response">
57
+ Response
58
+ <span
59
+ className={cn(
60
+ "text-xs font-mono ml-1",
61
+ status === "2" && "text-green-500",
62
+ status === "3" && "text-blue-500",
63
+ status === "4" && "text-yellow-500",
64
+ status === "5" && "text-red-500",
65
+ )}
66
+ >
67
+ ({queryMutation.data.status})
68
+ </span>
69
+ </TabsTrigger>
70
+ </TabsList>
71
+ <TabsContent value="request">
72
+ <RequestTab {...queryMutation.data.request} />
73
+ </TabsContent>
74
+ <TabsContent value="response">
75
+ <ResponseTab
76
+ status={queryMutation.data.status}
77
+ time={queryMutation.data.time}
78
+ size={queryMutation.data.size}
79
+ headers={queryMutation.data.headers}
80
+ body={queryMutation.data.body}
81
+ url={queryMutation.data.request.url}
82
+ />
83
+ </TabsContent>
84
+ </Tabs>
85
+ </div>
86
+ ) : (
87
+ <div className="grid place-items-center h-full">
88
+ <span className="text-[16px] font-semibold text-muted-foreground">
89
+ {queryMutation.isPending ? (
90
+ <Spinner />
91
+ ) : (
92
+ "Send a request first to see the response here"
93
+ )}
94
+ </span>
95
+ </div>
96
+ )}
97
+ </div>
98
+ );
99
+ };
100
+
101
+ export default ResultPanel;
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { generateInterface } from "./convertToTypes.js";
3
+
4
+ describe("generateInterface", () => {
5
+ it("should handle primitive types", () => {
6
+ const input = {
7
+ string: "hello",
8
+ number: 42,
9
+ boolean: true,
10
+ null: null,
11
+ };
12
+
13
+ const expected = [
14
+ "{",
15
+ " string: string;",
16
+ " number: number;",
17
+ " boolean: boolean;",
18
+ " null: null;",
19
+ "}",
20
+ ].join("\n");
21
+
22
+ expect(generateInterface(input)).toBe(expected);
23
+ });
24
+
25
+ it("should handle nested objects", () => {
26
+ const input = {
27
+ user: {
28
+ name: "John",
29
+ age: 30,
30
+ },
31
+ };
32
+
33
+ const expected = [
34
+ "{",
35
+ " user: {",
36
+ " name: string;",
37
+ " age: number;",
38
+ "};",
39
+ "}",
40
+ ].join("\n");
41
+
42
+ expect(generateInterface(input)).toBe(expected);
43
+ });
44
+
45
+ it("should handle arrays", () => {
46
+ const input = {
47
+ numbers: [1, 2, 3],
48
+ empty: [],
49
+ objects: [{ id: 1 }],
50
+ };
51
+
52
+ const expected = [
53
+ "{",
54
+ " numbers: number[];",
55
+ " empty: any[];",
56
+ " objects: {",
57
+ " id: number;",
58
+ "}[];",
59
+ "}",
60
+ ].join("\n");
61
+
62
+ expect(generateInterface(input)).toBe(expected);
63
+ });
64
+ });
@@ -0,0 +1,36 @@
1
+ type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
2
+ type JsonObject = { [key: string]: JsonValue };
3
+ type JsonArray = JsonValue[];
4
+
5
+ function inferType(value: JsonValue): string {
6
+ if (value === null) return "null";
7
+ if (Array.isArray(value)) {
8
+ if (value.length === 0) return "any[]";
9
+ const firstValue = value[0];
10
+ if (firstValue === undefined) return "any[]";
11
+ const elementType = inferType(firstValue);
12
+ return `${elementType}[]`;
13
+ }
14
+ if (typeof value === "object") {
15
+ return generateInterface(value);
16
+ }
17
+ return typeof value;
18
+ }
19
+
20
+ export function generateInterface(obj: JsonObject, indentation = ""): string {
21
+ const lines: string[] = ["{"];
22
+
23
+ for (const [key, value] of Object.entries(obj)) {
24
+ const propertyType = inferType(value);
25
+ lines.push(` ${key}: ${propertyType};`);
26
+ }
27
+
28
+ lines.push("}");
29
+ return lines.join("\n");
30
+ }
31
+
32
+ export function convertToTypes(json: JsonValue): { lines: string[] } {
33
+ const typeDefinition = inferType(json);
34
+ const lines = [`type GeneratedType = ${typeDefinition};`];
35
+ return { lines };
36
+ }
@@ -3,7 +3,7 @@ import { ListPlusIcon, RefreshCcwDotIcon } from "lucide-react";
3
3
  import { useCallback, useState } from "react";
4
4
  import { Badge } from "zudoku/ui/Badge.js";
5
5
  import { Markdown, ProseClasses } from "../../../components/Markdown.js";
6
- import { CIRCULAR_REF } from "../../../oas/parser/dereference/index.js";
6
+ import { CIRCULAR_REF } from "../../../oas/graphql/circular.js";
7
7
  import type { SchemaObject } from "../../../oas/parser/index.js";
8
8
  import { Button } from "../../../ui/Button.js";
9
9
  import { cn } from "../../../util/cn.js";
@@ -61,7 +61,7 @@ const CommandInlineInput = React.forwardRef<
61
61
  <CommandPrimitive.Input
62
62
  ref={ref}
63
63
  className={cn(
64
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
64
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
65
65
  "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
66
66
  className,
67
67
  )}
@@ -83,9 +83,9 @@ const SelectContent = React.forwardRef<
83
83
  <SelectScrollUpButton />
84
84
  <SelectPrimitive.Viewport
85
85
  className={cn(
86
- "p-1",
87
86
  position === "popper" &&
88
87
  "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
88
+ "divide-y",
89
89
  )}
90
90
  >
91
91
  {children}
@@ -61,7 +61,7 @@ export const MdxComponents = {
61
61
  code: ({ className, children, ...props }) => {
62
62
  // `inline` provided by the rehype plugin, as react-markdown removed support for that
63
63
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
- const inline = (props as any).inline;
64
+ const { inline, title } = props as Record<string, unknown>;
65
65
 
66
66
  if (inline === true || inline === "true") {
67
67
  return <InlineCode className={className}>{children}</InlineCode>;
@@ -75,6 +75,7 @@ export const MdxComponents = {
75
75
  className="rounded-xl p-4 border dark:!bg-foreground/10 dark:border-transparent"
76
76
  showLanguageIndicator
77
77
  code={String(children).trim()}
78
+ title={typeof title === "string" ? title : undefined}
78
79
  />
79
80
  );
80
81
  },
@@ -1,4 +1,4 @@
1
- import { useEffect } from "react";
1
+ import { useCallback, useEffect } from "react";
2
2
  import { useLocation } from "react-router";
3
3
  import { useViewportAnchor } from "../components/context/ViewportAnchorContext.js";
4
4
  import { DATA_ANCHOR_ATTR } from "../components/navigation/SidebarItem.js";
@@ -22,32 +22,49 @@ const scrollIntoViewIfNeeded = (
22
22
  element.scrollIntoView(options);
23
23
  };
24
24
 
25
- export const useScrollToAnchor = () => {
26
- const location = useLocation();
25
+ export const useScrollToHash = () => {
27
26
  const { setActiveAnchor } = useViewportAnchor();
28
27
 
29
- useEffect(() => {
30
- if (!location.hash) return;
31
-
32
- const hash = decodeURIComponent(location.hash.split("/")[0]!.slice(1));
33
-
34
- const scrollToElement = () => {
35
- const element = document.getElementById(hash);
36
- const link = document.querySelector(`[${DATA_ANCHOR_ATTR}="${hash}"]`);
28
+ const scrollToHash = useCallback(
29
+ (hash: string) => {
30
+ const cleanHash = hash
31
+ .replace(/^#/, "")
32
+ // Operation list items might have subdivisions that the sidebar doesn't show.
33
+ // The subdivisions are separated by a slash so we need to remove everything before the slash to get the sidebar correct item.
34
+ .split("/")
35
+ .at(0)!;
36
+ const element = document.getElementById(decodeURIComponent(cleanHash));
37
+ const link = document.querySelector(
38
+ `[${DATA_ANCHOR_ATTR}="${cleanHash}"]`,
39
+ );
37
40
 
38
41
  if (element) {
39
42
  element.scrollIntoView();
40
43
  scrollIntoViewIfNeeded(link);
41
- requestIdleCallback(() => setActiveAnchor(hash));
44
+ requestIdleCallback(() => setActiveAnchor(cleanHash));
42
45
  return true;
43
46
  }
44
47
 
48
+ // Scroll didn't happen
45
49
  return false;
46
- };
50
+ },
51
+ [setActiveAnchor],
52
+ );
53
+
54
+ return scrollToHash;
55
+ };
56
+
57
+ export const useScrollToAnchor = () => {
58
+ const location = useLocation();
59
+ const { setActiveAnchor } = useViewportAnchor();
60
+ const scrollToHash = useScrollToHash();
61
+
62
+ useEffect(() => {
63
+ if (!location.hash) return;
47
64
 
48
- if (!scrollToElement()) {
65
+ if (!scrollToHash(location.hash)) {
49
66
  const observer = new MutationObserver((_, obs) => {
50
- if (!scrollToElement()) return;
67
+ if (!scrollToHash(location.hash)) return;
51
68
  obs.disconnect();
52
69
  });
53
70
 
@@ -1,4 +0,0 @@
1
- export declare const ResponseTab: ({ body, headers, }: {
2
- body?: string;
3
- headers: Headers;
4
- }) => import("react/jsx-runtime").JSX.Element;
@@ -1,42 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from "react";
3
- import { SyntaxHighlight } from "../../../components/SyntaxHighlight.js";
4
- import { Card } from "../../../ui/Card.js";
5
- import { SimpleSelect } from "../SimpleSelect.js";
6
- const mimeTypeToLanguage = (mimeType) => {
7
- const mimeTypeMapping = {
8
- "application/json": "json",
9
- "text/json": "json",
10
- "text/html": "html",
11
- "text/css": "css",
12
- "text/javascript": "javascript",
13
- "application/xml": "xml",
14
- "application/xhtml+xml": "xhtml",
15
- };
16
- return Object.entries(mimeTypeMapping).find(([mime]) => mimeType.includes(mime))?.[1];
17
- };
18
- const detectLanguage = (headers) => {
19
- const contentType = headers.get("Content-Type") || "";
20
- return mimeTypeToLanguage(contentType);
21
- };
22
- const tryParseJson = (body) => {
23
- try {
24
- return JSON.stringify(JSON.parse(body), null, 2);
25
- }
26
- catch {
27
- return null;
28
- }
29
- };
30
- export const ResponseTab = ({ body = "", headers, }) => {
31
- const detectedLanguage = detectLanguage(headers);
32
- const jsonContent = tryParseJson(body);
33
- const beautifiedBody = jsonContent || body;
34
- const [view, setView] = useState(jsonContent ? "formatted" : "raw");
35
- return (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Card, { className: "shadow-none", children: _jsx(SyntaxHighlight, { language: view === "raw" ? (jsonContent ? "plain" : detectedLanguage) : "json", noBackground: true,
36
- // playground dialog has h-5/6 ≈ 83.333vh
37
- className: "overflow-x-auto p-4 text-xs max-h-[calc(83.333vh-180px)]", code: view === "raw" ? body : beautifiedBody }) }), jsonContent && (_jsx("div", { className: "flex justify-end", children: _jsx(SimpleSelect, { value: view, onChange: (e) => setView(e.target.value), options: [
38
- { value: "formatted", label: "Formatted" },
39
- { value: "raw", label: "Raw" },
40
- ] }) }))] }));
41
- };
42
- //# sourceMappingURL=ResponseTab.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ResponseTab.js","sourceRoot":"","sources":["../../../../../src/lib/plugins/openapi/playground/ResponseTab.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,kBAAkB,GAAG,CAAC,QAAgB,EAAE,EAAE;IAC9C,MAAM,eAAe,GAAG;QACtB,kBAAkB,EAAE,MAAM;QAC1B,WAAW,EAAE,MAAM;QACnB,WAAW,EAAE,MAAM;QACnB,UAAU,EAAE,KAAK;QACjB,iBAAiB,EAAE,YAAY;QAC/B,iBAAiB,EAAE,KAAK;QACxB,uBAAuB,EAAE,OAAO;KACjC,CAAC;IAEF,OAAO,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CACrD,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CACxB,EAAE,CAAC,CAAC,CAAC,CAAC;AACT,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAE,EAAE;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACtD,OAAO,kBAAkB,CAAC,WAAW,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAC1B,IAAI,GAAG,EAAE,EACT,OAAO,GAIR,EAAE,EAAE;IACH,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,cAAc,GAAG,WAAW,IAAI,IAAI,CAAC;IAC3C,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAC9B,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAClC,CAAC;IAEF,OAAO,CACL,eAAK,SAAS,EAAC,qBAAqB,aAClC,KAAC,IAAI,IAAC,SAAS,EAAC,aAAa,YAC3B,KAAC,eAAe,IACd,QAAQ,EACN,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,MAAM,EAEtE,YAAY;oBACZ,yCAAyC;oBACzC,SAAS,EAAC,0DAA0D,EACpE,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,GAC5C,GACG,EACN,WAAW,IAAI,CACd,cAAK,SAAS,EAAC,kBAAkB,YAC/B,KAAC,YAAY,IACX,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAA4B,CAAC,EAC/D,OAAO,EAAE;wBACP,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE;wBAC1C,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;qBAC/B,GACD,GACE,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC"}
@@ -1,34 +0,0 @@
1
- import e from "react";
2
- import { j as r } from "./jsx-runtime-Bdg6XQ1m.js";
3
- import { u as l, L as m } from "./chunk-SYFQ2XB5-BF5IDYrB.js";
4
- const c = {}, u = e.createContext(c);
5
- function f(t) {
6
- const n = e.useContext(u);
7
- return e.useMemo(
8
- function() {
9
- return typeof t == "function" ? t(n) : { ...n, ...t };
10
- },
11
- [n, t]
12
- );
13
- }
14
- function h(t) {
15
- let n;
16
- return t.disableParentContext ? n = typeof t.components == "function" ? t.components(c) : t.components || c : n = f(t.components), e.createElement(
17
- u.Provider,
18
- { value: n },
19
- t.children
20
- );
21
- }
22
- const y = (t) => {
23
- const n = l(), o = typeof t.to == "string" ? t.to : t.to.hash, i = (a) => {
24
- var s;
25
- !(o != null && o.startsWith("#")) || o !== n.hash || (a.preventDefault(), (s = document.getElementById(o.slice(1))) == null || s.scrollIntoView());
26
- };
27
- return /* @__PURE__ */ r.jsx(m, { onClick: i, ...t });
28
- };
29
- export {
30
- y as A,
31
- h as M,
32
- f as u
33
- };
34
- //# sourceMappingURL=AnchorLink-bObQitZv.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AnchorLink-bObQitZv.js","sources":["../../../node_modules/.pnpm/@mdx-js+react@3.1.0_@types+react@19.0.7_react@19.0.0/node_modules/@mdx-js/react/lib/index.js","../src/lib/components/AnchorLink.tsx"],"sourcesContent":["/**\n * @import {MDXComponents} from 'mdx/types.js'\n * @import {Component, ReactElement, ReactNode} from 'react'\n */\n\n/**\n * @callback MergeComponents\n * Custom merge function.\n * @param {Readonly<MDXComponents>} currentComponents\n * Current components from the context.\n * @returns {MDXComponents}\n * Additional components.\n *\n * @typedef Props\n * Configuration for `MDXProvider`.\n * @property {ReactNode | null | undefined} [children]\n * Children (optional).\n * @property {Readonly<MDXComponents> | MergeComponents | null | undefined} [components]\n * Additional components to use or a function that creates them (optional).\n * @property {boolean | null | undefined} [disableParentContext=false]\n * Turn off outer component context (default: `false`).\n */\n\nimport React from 'react'\n\n/** @type {Readonly<MDXComponents>} */\nconst emptyComponents = {}\n\nconst MDXContext = React.createContext(emptyComponents)\n\n/**\n * Get current components from the MDX Context.\n *\n * @param {Readonly<MDXComponents> | MergeComponents | null | undefined} [components]\n * Additional components to use or a function that creates them (optional).\n * @returns {MDXComponents}\n * Current components.\n */\nexport function useMDXComponents(components) {\n const contextComponents = React.useContext(MDXContext)\n\n // Memoize to avoid unnecessary top-level context changes\n return React.useMemo(\n function () {\n // Custom merge via a function prop\n if (typeof components === 'function') {\n return components(contextComponents)\n }\n\n return {...contextComponents, ...components}\n },\n [contextComponents, components]\n )\n}\n\n/**\n * Provider for MDX context.\n *\n * @param {Readonly<Props>} properties\n * Properties.\n * @returns {ReactElement}\n * Element.\n * @satisfies {Component}\n */\nexport function MDXProvider(properties) {\n /** @type {Readonly<MDXComponents>} */\n let allComponents\n\n if (properties.disableParentContext) {\n allComponents =\n typeof properties.components === 'function'\n ? properties.components(emptyComponents)\n : properties.components || emptyComponents\n } else {\n allComponents = useMDXComponents(properties.components)\n }\n\n return React.createElement(\n MDXContext.Provider,\n {value: allComponents},\n properties.children\n )\n}\n","import React from \"react\";\nimport { Link, type LinkProps, useLocation } from \"react-router\";\n\n/**\n * Link that scrolls to anchor even if the hash is already set in the URL.\n */\nexport const AnchorLink = (props: LinkProps) => {\n const location = useLocation();\n const hash = typeof props.to === \"string\" ? props.to : props.to.hash;\n\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n if (!hash?.startsWith(\"#\") || hash !== location.hash) return;\n\n event.preventDefault();\n document.getElementById(hash.slice(1))?.scrollIntoView();\n };\n\n return <Link onClick={handleClick} {...props} />;\n};\n"],"names":["emptyComponents","MDXContext","React","useMDXComponents","components","contextComponents","MDXProvider","properties","allComponents","AnchorLink","props","location","useLocation","hash","handleClick","event","_a","jsx","Link"],"mappings":";;;AA0BA,MAAMA,IAAkB,CAAA,GAElBC,IAAaC,EAAM,cAAcF,CAAe;AAU/C,SAASG,EAAiBC,GAAY;AAC3C,QAAMC,IAAoBH,EAAM,WAAWD,CAAU;AAGrD,SAAOC,EAAM;AAAA,IACX,WAAY;AAEV,aAAI,OAAOE,KAAe,aACjBA,EAAWC,CAAiB,IAG9B,EAAC,GAAGA,GAAmB,GAAGD,EAAU;AAAA,IAC5C;AAAA,IACD,CAACC,GAAmBD,CAAU;AAAA,EAClC;AACA;AAWO,SAASE,EAAYC,GAAY;AAEtC,MAAIC;AAEJ,SAAID,EAAW,uBACbC,IACE,OAAOD,EAAW,cAAe,aAC7BA,EAAW,WAAWP,CAAe,IACrCO,EAAW,cAAcP,IAE/BQ,IAAgBL,EAAiBI,EAAW,UAAU,GAGjDL,EAAM;AAAA,IACXD,EAAW;AAAA,IACX,EAAC,OAAOO,EAAa;AAAA,IACrBD,EAAW;AAAA,EACf;AACA;AC5Ea,MAAAE,IAAa,CAACC,MAAqB;AAC9C,QAAMC,IAAWC,EAAY,GACvBC,IAAO,OAAOH,EAAM,MAAO,WAAWA,EAAM,KAAKA,EAAM,GAAG,MAE1DI,IAAc,CAACC,MAA+C;;AAClE,IAAI,EAACF,KAAA,QAAAA,EAAM,WAAW,SAAQA,MAASF,EAAS,SAEhDI,EAAM,eAAe,IACrBC,IAAA,SAAS,eAAeH,EAAK,MAAM,CAAC,CAAC,MAArC,QAAAG,EAAwC;AAAA,EAC1C;AAEA,SAAQC,gBAAAA,EAAAA,IAAAC,GAAA,EAAK,SAASJ,GAAc,GAAGJ,GAAO;AAChD;","x_google_ignoreList":[0]}